From 5a0168d904e1a6315cfec445bf5486b57b6bb391 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 25 Apr 2017 18:44:05 +1000 Subject: [PATCH 001/839] Add missing changelog entries, Update build directory layout --- .gitignore | 8 +++++++- CHANGELOG.txt | 6 ++++++ ProcessHacker/ProcessHacker.vcxproj | 16 ++++++++-------- build/build_binaries.cmd | 2 +- build/build_debug.cmd | 2 +- build/build_release.cmd | 2 +- build/build_sdk.cmd | 2 +- build/build_sdk_rebuild.cmd | 2 +- .../CustomBuildTool.csproj | 2 +- tools/CustomBuildTool/CustomBuildTool.sln | 2 +- .../CustomBuildTool/CustomBuildTool/app.config | 3 --- .../Properties/AssemblyInfo.cs | 0 .../Resource Files/ProcessHacker.ico | Bin .../Resource Files/app.manifest | 0 .../Source Files/Build.cs | 11 ----------- .../Source Files/HeaderGen.cs | 0 .../{CustomBuildTool => }/Source Files/Json.cs | 0 .../Source Files/NativeMethods.cs | 0 .../Source Files/Nightly.cs | 0 .../Source Files/Program.cs | 0 .../Source Files/Verify.cs | 0 .../Source Files/VisualStudio.cs | 0 .../{CustomBuildTool => }/Source Files/Zip.cs | 0 tools/CustomBuildTool/app.config | 8 ++++++++ .../bin/Release/CustomBuildTool.exe | Bin 0 -> 143360 bytes .../bin/Release/CustomBuildTool.exe.config | 8 ++++++++ .../bin/Release/CustomBuildTool.pdb | Bin 0 -> 62976 bytes .../resources/ProcessHacker.ico | Bin 28 files changed, 44 insertions(+), 30 deletions(-) rename tools/CustomBuildTool/{CustomBuildTool => }/CustomBuildTool.csproj (98%) delete mode 100644 tools/CustomBuildTool/CustomBuildTool/app.config rename tools/CustomBuildTool/{CustomBuildTool => }/Properties/AssemblyInfo.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Resource Files/ProcessHacker.ico (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Resource Files/app.manifest (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Build.cs (99%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/HeaderGen.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Json.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/NativeMethods.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Nightly.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Program.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Verify.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/VisualStudio.cs (100%) rename tools/CustomBuildTool/{CustomBuildTool => }/Source Files/Zip.cs (100%) create mode 100644 tools/CustomBuildTool/app.config create mode 100644 tools/CustomBuildTool/bin/Release/CustomBuildTool.exe create mode 100644 tools/CustomBuildTool/bin/Release/CustomBuildTool.exe.config create mode 100644 tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb rename tools/CustomBuildTool/{CustomBuildTool => }/resources/ProcessHacker.ico (100%) diff --git a/.gitignore b/.gitignore index f29e5c0572b9..1a44e8b91e28 100644 --- a/.gitignore +++ b/.gitignore @@ -101,7 +101,6 @@ Desktop.ini ########################## build/installer/*.exe -ProcessHacker/include/phapprev.h ProcessHacker/sdk/phapppub.h plugins-extra/ /sdk/ @@ -121,6 +120,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*/* diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6860af3f2ae8..561d9aacd6a2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,7 +2,13 @@ Process Hacker 3.0 * HIGHLIGHTS: + * New Process Hacker setup. + * 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. diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 7b8342faa6c4..ae99a1ad051e 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -105,11 +105,11 @@ aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ..\build\CustomBuildTool.exe -updaterev + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev Generating revision number... - ..\build\CustomBuildTool.exe -cleanup + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup Cleaning up build... @@ -137,11 +137,11 @@ aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ..\build\CustomBuildTool.exe -updaterev + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev Generating revision number... - ..\build\CustomBuildTool.exe -cleanup + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup Cleaning up build... @@ -176,11 +176,11 @@ aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ..\build\CustomBuildTool.exe -updaterev + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev Generating revision number... - ..\build\CustomBuildTool.exe -cleanup + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup Cleaning up build... @@ -214,11 +214,11 @@ aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ..\build\CustomBuildTool.exe -updaterev + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev Generating revision number... - ..\build\CustomBuildTool.exe -cleanup + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup Cleaning up build... diff --git a/build/build_binaries.cmd b/build/build_binaries.cmd index 3fad7f7d524c..8c42e4bbe1b1 100644 --- a/build/build_binaries.cmd +++ b/build/build_binaries.cmd @@ -1,5 +1,5 @@ @echo off -CustomBuildTool.exe -bin +..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -bin pause diff --git a/build/build_debug.cmd b/build/build_debug.cmd index d11d2ffedd85..7f3fbb6d6035 100644 --- a/build/build_debug.cmd +++ b/build/build_debug.cmd @@ -1,5 +1,5 @@ @echo off -CustomBuildTool.exe -debug +..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -debug pause diff --git a/build/build_release.cmd b/build/build_release.cmd index c92eb4847d49..15e528690fb6 100644 --- a/build/build_release.cmd +++ b/build/build_release.cmd @@ -12,6 +12,6 @@ set KPH_BUILD_KEY=%KPH_BUILD_KEY% set NIGHTLY_BUILD_KEY=%NIGHTLY_BUILD_KEY% -CustomBuildTool.exe -release +..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -release pause diff --git a/build/build_sdk.cmd b/build/build_sdk.cmd index 31bed7a1b211..11e16deb7d05 100644 --- a/build/build_sdk.cmd +++ b/build/build_sdk.cmd @@ -1,3 +1,3 @@ @echo off -CustomBuildTool.exe -sdk \ No newline at end of file +..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -sdk \ No newline at end of file diff --git a/build/build_sdk_rebuild.cmd b/build/build_sdk_rebuild.cmd index 8658cf1b1a04..bc9493c504d5 100644 --- a/build/build_sdk_rebuild.cmd +++ b/build/build_sdk_rebuild.cmd @@ -1,5 +1,5 @@ @echo off -CustomBuildTool.exe -cleansdk +..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleansdk pause diff --git a/tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj similarity index 98% rename from tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj rename to tools/CustomBuildTool/CustomBuildTool.csproj index 7e2f0b447a35..08d1323194e2 100644 --- a/tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -9,7 +9,7 @@ Properties CustomBuildTool CustomBuildTool - v4.6.2 + v4.5 512 true diff --git a/tools/CustomBuildTool/CustomBuildTool.sln b/tools/CustomBuildTool/CustomBuildTool.sln index 43914052a1c2..811b822b2e06 100644 --- a/tools/CustomBuildTool/CustomBuildTool.sln +++ b/tools/CustomBuildTool/CustomBuildTool.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26403.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomBuildTool", "CustomBuildTool\CustomBuildTool.csproj", "{CD644DF2-A658-4CBC-9497-CA5DD13CFEC3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomBuildTool", "CustomBuildTool.csproj", "{CD644DF2-A658-4CBC-9497-CA5DD13CFEC3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/tools/CustomBuildTool/CustomBuildTool/app.config b/tools/CustomBuildTool/CustomBuildTool/app.config deleted file mode 100644 index 2a0024f75eca..000000000000 --- a/tools/CustomBuildTool/CustomBuildTool/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tools/CustomBuildTool/CustomBuildTool/Properties/AssemblyInfo.cs b/tools/CustomBuildTool/Properties/AssemblyInfo.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Properties/AssemblyInfo.cs rename to tools/CustomBuildTool/Properties/AssemblyInfo.cs 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/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs similarity index 99% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Build.cs rename to tools/CustomBuildTool/Source Files/Build.cs index 8f229f0064e7..e2dd8230d978 100644 --- a/tools/CustomBuildTool/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -183,18 +183,7 @@ public static void CleanupBuildEnvironment() { try { - // BUG - if (File.Exists("ProcessHacker\\include\\phapprev.h")) - File.Delete("ProcessHacker\\include\\phapprev.h"); - File.WriteAllText("ProcessHacker\\include\\phapprev.h", -@"#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION 0 - -#endif // PHAPPREV_H -"); } catch (Exception ex) { diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/HeaderGen.cs b/tools/CustomBuildTool/Source Files/HeaderGen.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/HeaderGen.cs rename to tools/CustomBuildTool/Source Files/HeaderGen.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/Json.cs b/tools/CustomBuildTool/Source Files/Json.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Json.cs rename to tools/CustomBuildTool/Source Files/Json.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/NativeMethods.cs rename to tools/CustomBuildTool/Source Files/NativeMethods.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/Nightly.cs b/tools/CustomBuildTool/Source Files/Nightly.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Nightly.cs rename to tools/CustomBuildTool/Source Files/Nightly.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Program.cs rename to tools/CustomBuildTool/Source Files/Program.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/Verify.cs b/tools/CustomBuildTool/Source Files/Verify.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Verify.cs rename to tools/CustomBuildTool/Source Files/Verify.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/VisualStudio.cs b/tools/CustomBuildTool/Source Files/VisualStudio.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/VisualStudio.cs rename to tools/CustomBuildTool/Source Files/VisualStudio.cs diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/Zip.cs rename to tools/CustomBuildTool/Source Files/Zip.cs 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/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe new file mode 100644 index 0000000000000000000000000000000000000000..5e2154b8d6b793832261812d5971bfb277e2b5eb GIT binary patch literal 143360 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 z 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®xezAOXBe(@oib`1ii(7nW3+DccU}2!|xgSd! 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 CjrFns literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bc21e1d516acfa1eaa34099d5de2f91520936a9d GIT binary patch literal 62976 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 RX@Qd#IB9{C7C1&0`2Wz59HamM literal 0 HcmV?d00001 diff --git a/tools/CustomBuildTool/CustomBuildTool/resources/ProcessHacker.ico b/tools/CustomBuildTool/resources/ProcessHacker.ico similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/resources/ProcessHacker.ico rename to tools/CustomBuildTool/resources/ProcessHacker.ico From 1ef703b3ee69c0c63bcf252132ce65ab71884da4 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 25 Apr 2017 18:51:01 +1000 Subject: [PATCH 002/839] Update build tools --- build/CustomBuildTool.exe | Bin 143872 -> 0 bytes tools/CustomBuildTool/Source Files/Build.cs | 22 +++++++++++++----- tools/CustomBuildTool/Source Files/Program.cs | 3 --- .../bin/Release/CustomBuildTool.exe | Bin 143360 -> 143360 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 62976 bytes 5 files changed, 16 insertions(+), 9 deletions(-) delete mode 100644 build/CustomBuildTool.exe diff --git a/build/CustomBuildTool.exe b/build/CustomBuildTool.exe deleted file mode 100644 index 1acc5834cacd17643cdf36a6766a3c2c0213bb23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143872 zcmd?S34B}Cu|GcdN|t1KmAu7E632;S#T(vYI~&AnVsJLwNeJZCv1P?EaV&9V$4&x4 zAwUd;21=l1YuO5=rIZ4FEtIxlX$x&Bbf;SgU0zF9O1Hc|9{GJ|&b^ZCI3ewO@BiNC z^H-v|Gjrz5nKS2{Im^AeUUSinWFsOwz9&x-eHK^#tQ2@=5J7V4+^16MiNu%YeOBvy zXScy2o4&3y9)h%BO}4l!u~*^F*Z^-G*Z~QuDftoa3D~blHy)qs$Snl)T!C% zyyxG(&?@aX6;YB_MRY$n*K5LdKZi>pzQ^%p>k4tvmEFL|!GJ$^gNgtAvC+1xiPGhN z>}rxqgg*N$(K^PD6TNz^D$7h#mxxZuz#`8O6--HZ7ow2J6(`~Xy*iGr91Tp2f=<5= z0BKTJ({51mDJ0rfX@m_wD6#E0kn3RM1GvV0RuZkOGy=mx2(qmNo5crJQx!t4nUmu` zzkOCwY@1FAG~7v)IYz{u*c0d0_&jIA^Q&ysh|Ku!@#PS?Bf#tiK*%?X$rO;j2sA4) zl~d2NQU%NR3y;l8G5!R>#A$XM({oB)rJ8iKHd=Rt@-xMi zXyZc?^%3D?{- z)i?IDJxVsu$by(9Aqke0u_!JjQFJPGmKHXv!sD^Wl6;F$Tq$K#*$I7+m#-k$7I7CT)&G0T~AVUpAFzM-0*?MH{Rr(|c?%SsbH z_S-WOj9XB+Cqa6&@u?WD?DyDnF3hld?1{+x@_sM!W;n7loMmMmhsSw)hRgUG=Wxa4 zR=AlP73JB&9_ zU=X9(;Rqt~I*hrgaNrgI)8U7helkYC#&kwnG<^wBV`r2;HY-VNnm%c$V2!dZo3?C; z`FsudnEySZ$5)M!10I*e{n1L=Gd1Z)ak=t6Q*vDnQgUJL5nSe;C*!z+ z3->Q^T)~CA*c;OzcuckqZV4_1FeD*LN)3%M&WK6HQ7lu8d!b?%X3WU`+B9}Wma#vR z1C+#jYY9-QbbJ&k@v0$wan`BAITAB}hclR2@TRar?AQRFlcFhr*A)xhR-nl zeE^4||AXoCawy~y`dDs?aS+t>0ce*2Xh#S)`}WYCUmxt@IYp`a zDioh;PmC+}*mJGYV+l9HxHAXbs6U9>+G6E@ZA$scP7Op2(G9|EbW9xH_FA~_7stFmzp$t&c$)YOV|pZug%&jk7i z>dA3V7Fbpkc>r2t9hQ_K$GOnhYTfJ;F)sVyG!316tVJ#0^75=|9bwGI@op*d$&f{W zMNn{K$f^a%)bY&=>u6gQQ2A=B4mR1B#rn!C(0r<$F|M-nOsgErPvzxS8Fso1F*-Tg zAKm#gl@a4|8Pf*>2k-JZ)41VfT>Iq!5wwDRdXvZJiFSmzJeW1iJkv&O?zD1zSD^Dm z5bS0y!W54dp~Mjn?BNx#4Ri8()Hrx9@P!WJdf@w6ms$;D_vf~fJ~Um7bxapiwne?= z2(E;NvzR`c;x_Qx&>sB7o|M$y$4(L8{5jJ+%7N4B=HG*{RyWTno$fLAJvNfF3SvUp z<}A|>o)Z5cFdsWWdT<0i#Lja|0=)v;Qnn_JdVaH zz5*KT#*evxCS(r|FjICd!R?aH9mdzpNjsP}jzctfiJ+Ti!$1rPyOA=-G%z?kEoFW* zWrS12&|pYVHZy1l%0>o91!WV1yMg*zP^dXtQLAa&|JYQRWGv<~&_q}V0E?w0o0Fhc zYEFC5(#sNTvkcz3N#@?(*NW`nHh`4GU^}b21A?yLDxir@hi^5rJ^@y^16c5U0A9@o zF93o|Co{2_b63nUUWSy#S_zga$Xr1jb0?NN5`$~O|M9U|STu`tg0Bz^>mUKUZr
O?EePA;o-x^7qVdm&>I7qe50^B_ER@oITt3o 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 diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index e2dd8230d978..6819e256a7bf 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -206,13 +206,23 @@ public static void ShowBuildEnvironment(bool ShowBuildInfo) if (ShowBuildInfo) { + string buildMessage = string.Empty; + + if (BuildNightly) + { + buildMessage = Win32.ExecCommand(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); + } + else + { + 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.ExecCommand(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(yellow)%h%Creset)\" --abbrev-commit"); + } + string currentBranch = Win32.ExecCommand(GitExePath, "rev-parse --abbrev-ref HEAD"); string currentCommitTag = Win32.ExecCommand(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD //string latestGitCount = Win32.GitExecCommand("rev-list --count " + BuildBranch); - string buildMessageColor = !BuildNightly ? - Win32.ExecCommand(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(yellow)%h%Creset)\" --abbrev-commit") : - Win32.ExecCommand(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %<(65,trunc)%s (%h)\" --abbrev-commit"); - + if (!string.IsNullOrEmpty(currentBranch)) { Program.PrintColorMessage(Environment.NewLine + "Branch: ", ConsoleColor.Cyan, false); @@ -228,9 +238,9 @@ public static void ShowBuildEnvironment(bool ShowBuildInfo) Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(currentCommitTag + Environment.NewLine, ConsoleColor.White); - if (!BuildNightly) + if (!BuildNightly && !string.IsNullOrEmpty(buildMessage)) { - Console.WriteLine(buildMessageColor + Environment.NewLine); + Console.WriteLine(buildMessage + Environment.NewLine); } } } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 89e4f603c114..4cc566b5c6d3 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -22,9 +22,6 @@ public static void PrintColorMessage(string Message, ConsoleColor Color, bool Ne public static void Main(string[] args) { - 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); - ProgramArgs = ParseArgs(args); BuildDebug = ProgramArgs.ContainsKey("-debug"); diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 5e2154b8d6b793832261812d5971bfb277e2b5eb..0416602b5cdd7fcbf6f32c9600c257ccb91d5a4a 100644 GIT binary patch delta 9869 zcmbVSd3;pW*?!NRJ9oCsWG0z4Guam=LlV{iVKXcm!X7~ciWZW~q$zFcuvI2&wr}nL2~@V0+Xi7R_6I2fTC{M+Ph=2G>@@1b)R8l^fKBTO8~5CjS_%dmeFX(hR-smiRQSa(TIb}P4|(Tf0*KN$>ARAW!W3`o3*M=vly+F3W@<+ zBLJDbWx&E<0C0tUXlumjkj3b&)-d|5!BsAtqGB)qJC@#B?8|^~Pc*L_jn=Kje(qF+ zIe_M}q&bU6ggIP}rf^hr#jDXkozi4_FEi8y>I@U=2!a;h<+t*&sFZLXX6L5)!s-WU4z*J;t9gp2XqJdRn26?* z8TCAblx(##Er<#3^5O9@{YpfAnuy|Ii^xLw=dA4LGiVRzW9Q{IPP{-IB!4}{Ok6XA z6-!kk=!d&P=wnIkw4t7bp!;_TeJP>;<>98OmmFp_W}1*SISuY{8R?Ak`ALqo)Q?O> zeU=N8ImNAUtt*$wH$BtjGP#t;@9g_VS1{AU$Erik)l;`&Rs*g|MQOmpRQP+Sx!ax? z;z*=?aZ+y-`pu-iAasc%9^d7+Vc|}5RX<@1_r}?>D&an20z(SF8Et$qLPKBSpgbkq z4|zPVNUgWzN82zY*P&eoz6kFUrE)}#bbhhBOU!uO)jz#kRvXH=>rdTXgPgjn%*u)P z$Krf(+!{qQ2{P}Bo6$;~tY4*Ai6>(#u~@?ZQIQ-8*_bZ2oZc?%TWmSSR=Sc)H1qo@ z=3KFF==S9q-NVa0yh?rA8gS3e{3nN3ea%`Bt#+fsFS-|uHa&g1=OsLXB|SF2BHCtw}a>*A_Xs~7J{I?hoKR4cpDQ^BMZoVE5%^T5nDGV?1q=o~ZiJDHRDcmU58y9*F zf}-i)3Vjj5Ou8#z=fl+lwrVy5{}fWVm!xq(zT*Ph)TE}dja3^RL#4+8BJZ(4 z_dMK|aZ&=fH5Eul0;<#QiFAv&`V5GRw=74us<)(PS0;}!)t9g?8iE<&p$IVxp3HdW z?sQL9^1McIc4F3q`>1**-IH>L$ZbRSqG4Fas&HEt?mWYhmnggjd85eHZll+rHSU^m zb(pAeb(l7yBk`kF8mh4ZZe^zUQtLDa)F+&kw_LTCiKQOjm0Gc*_=)mG{?(k}hWE#m zAa6w5EV6^~r7}egyXx6GwZ#?by$P)icj25Ab5{rh#KQzXhAd1+H~%V6mwL`MeUNx2 zqV2)PzY?LeaR`>O3FsF&i(l0&!n4#x8DaOdY`jhzYOsXGq=fNV%*yAfJ2Q&e&(xC{ z<%ovNanZ#2!UqoR7!zk1pE<6Y$F0`DDm+8@jG{UAV-G0@G8nr^lu7!!#K<+DUNGrn zr_?tyi!5Th=pWA2%Q9uOW{ zp;k_uwjIIvO9+jMnBytA@pQVCWs9pL+cPTns(Wi!Hr^ZYX~m?im29=ikg2Zm6bx&@ zd?`FhOy5)SQt?MZ-j0@BtM-a9e!uNX#Kp2 z+5mrw+AO}8+93ZUwITimwb?wk0OMm$ER~z6wLsXg)+3^gVzE{-1o#$ev-oeQ4f5mE zhWMYU&E{WHo5NY5D3EL69*tOeu|{mXN+W4}l1A)&u0|aEL5-yIRT^>fEl8qR$i??+ zrVM^mBbodijkx(|8u9Qe8u4;_5q0n5`5N)_N{s|~twyr=jT#B^g&GO*pKBzW?-V44 zPwR4S(W>FNlZmJHL*(`Glhpe87t{tgD;9la@l0xiyqMY$ucS7ckE1q+-$pG?$-Nq} z@<%md+#&>JP&JSzE!QUx{sF=xg{)uLC@*gzf;>Hp>$_(z)NG9*45jVe9BOZRE zM!bB1Mtrvg) zp0p#8l$}gEl9D8DQilwsg=1C6aV)rfa|GDg+bVmEtPWkq?_ zKVkqKW!bIC{{EvvLA_F`OO z*3$H-&r*$e3#Z}wdZt9;J%^0{$q+MdkM`k3-2UKH7?rF1hpt^TTXBg;{*jhth-YH80$%k#v* zwYMJ|{{#yz`aO>LAw%5cDvz5aUZZ}{vwYe(v;-So6^DPT;*vM=8$k3$Lm(5Gd7lWZ)1owgyW$W_`&|qjN#=aV+BH+XCTFPukQ8rHD&#J4+ z^VwQ;XZcLFQ?*vy#YU_5R!m|A9Y-qel%uVR3kpJO5ubN7feX&K1%B)jxQ_5Mli(NA z*JI2_gdb33Ct(KR4e280I>LIyKJcS=lhOxn4hWo#sK6yl6DVNx|2OilyxSE9*M^#+ z_#LkULm3?P3G`tQgY7s71`|9@FfGQV$Bolsvk507_JLK>^NOPEGwfET#k__+h&hCn zgfj`}6V?z8lSE7jA&}(=@fh;xwAgwwZ6(utglUNJ4r;6~VseF3;7nQIZG?9R1fOVo z5eL0f7QjeA&`f+VVF6(~;Z(xADdrevd82Tjf4|~|cE?&JAI{1GCm97^L3cjH zFf^Y^=Yxm%ax9S#FA#4d{2xM<;`%T-6Iqoe2D@<43AHn;4J6p3tD{*pOoHi! zny({J!whgrSNnW}O)^Z!uNxvy@d&j?SJ#ryi7OU;Q5Zn-MNNVf5TWaF`w)ps+(MP% z=TD*Lky@{--f=nRg5N(yP_Vayu!sV25osu%uy5G0(7;)wAAEIsCA~5xQbTbn%A5eD=3bLc>Yl8-6Z#UBN#agc#jhWkxxP9t%2d7u6xm6-=NY%ia?M7Oz zi8xxHq%e-r`Xn{T_J)a8B&pNhjud6gdRyIB73}#=ifw{9#**>lZK=xcJEdN!dL)`^ zd((F|CHo2c-%MdxOLM#2e4ktgrSO!lekwarZP!(&s}3UYysl2UoT&Ed>Ueq`^n{mn z^=`To)iGUhXI&J^pi`GorxVqmb+y%52jy^9S3A*H4xj33CHgAhYhA5FUj_W2s}gq| z^a4XQ&Jip(_jfx{rRnM+9B6NF>uNa;G};@obve>ehxd|VU5&*{pbu2&>W`*6=nDgM zbsE=d-)CXO6 z#XJa#t`(tekQ=m{t6;ybe5i)P)ax|gTjt?#4Rq@2TgOtU24{`%wSr`Fo2%hdQp?S? zxB#l*Yh8`O1yBtab=AjGhj~#6zpsdi`W%nG8mzh!XS5nJbhU>#|v==i86aW~3A*}GOdig$cP<`M8BsW#Z}?t;wq0ijIfHy03!WWF8M+=;~v($TR_rQmM5b&+C?qRm9t12UPhtB9?eJ zV&pr4r_ugta2r^pexY56ZNcGa|BE83y{Ej3maV}q#1Xc7#0=ZV&`0{rsvv*G-i6P1 zBW#zW*a#uY$*f8`?QuetbRgZ$c7wQ0?1mhxp9yacYk}psQd$u!*$7GSG}_B+rFMG_ z+eh*HU_f9zn;{L6uV=HRzq9G=PONz|dOtDT!5);tSqiw}_N<55ak#@2W6>rWQH!+4 zH5^)~ghl$y*N$Bd%i4jsBD5Rvfh;jM!?O0W5XBS`hTtvFIaUTAVYxDB3dUFwyx_l# zI7GgTkwauz>Q9kF2}fgme`!3$pJ#sODCvBZ9Y`0GdE7om8cJzH;kf-W8%l+yV%jum zgVZZD3k%tU8=*fH-6Dz3X6fE<14pD=bv`Kcg)|>#`~8N+*jc$@33fKpu#~(j^_Xn; zLcDYA%}A@7sA0=KCBMimAY z$hGXz%=_d+((eOKsAWCzkZReK;1acAlrQ>IIUwDL>n0$*gqziV_&Ru*ot2JSH_Q8R zoZDqFf4h+nk@q1o*FO2Ybg$zN@;>aPQ{K%oZ12k7W4~Q8GyEku1MvmJ4A~;}l|Ke% zxa9dpK8}&!%YCGonTnAaK1a)2XbBnrjHpB*V0gt*WXv}#ca#}l(Whpmb((PovpNb|mDx9E?aDvXWGybQIEmEDk9dRP!dA7l^-B@Z6tK22+ z^6fJo!p;sG&mz8t-gG>q^XvuxC@ETNc+Y$iO_%K-8DH1CuXE0lTBY)=?~Esavyp}q z;9?Ks>O5}WZk!LT_B3U-A;acT&a*k5I+%~AxI{6-J#Y=;Vi=A10MsfLSPB!7FGIAz z3Ydbth4@OCu4KR}!K1JmZbC;ZIi7$y$k!5I4|ibD2I5b`d@QjU79wtg`w*XjhY_E{ zq88YNsa7LtYE>@cn9JulzWU~UZ;sP~{h&pu#C9P5)#Bec{F?wPl}YfpQip9X zScsR~uV616hqvHE_!7Q@G*--(vNdcSJHWnSR%wysm2>4Wa`bk&RX!wtDBF#FjRTF# zjO&cg8qXOO#RCiictzlYliXki7@Hwbb_#4J>>%7kILIyRKXVD3=n&{N3!GqHggD+N z@RCJX_M{8csvWg76rnI@3e#Y6WD)+F&_f+7w)@rm+Qa<^v&HNrJHCIJH270 z+RBwRR^je$TZ$jRueKG@n_Ks|ng1frD&o|)#s&v9NFP9#bQUaz4`7<%3s{Bi?+xF8 zu>Y^&A7GQe!M_FUzl;m} z9ZxKXgq_=>w|!W6@5$O-uN}QNcjM=i)XudFGw|OO9pdK-8G$*n9g?C>Y`eGP)wWhc z$E=Mno67LNicJ@Xg*`xs0aPEvzpVh*PZ>Ss$(Mfd(1{@}V>VW9$UpYbgaaL8wwl=* q3I8WeyDUF_2w>grF6L*e)d$;KnjPErI<~QkEz+(&9f#ijJNrMcxf}QZ delta 9824 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index bc21e1d516acfa1eaa34099d5de2f91520936a9d..586c922cd41f3527db93e1e43b3502a94fcdcaed 100644 GIT binary patch delta 11261 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 AQvd(} delta 11786 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 From 8475e8ca4dad8f64c31fa51188152cc6496aa617 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 25 Apr 2017 18:53:40 +1000 Subject: [PATCH 003/839] Fix appveyor build config --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 15728ca977a5..4ad9645b488a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,7 @@ test: off # Run custom build script. build_script: -- cmd: .\build\CustomBuildTool.exe +- cmd: tools\CustomBuildTool\bin\Release\CustomBuildTool.exe # Setup build notifications. notifications: From 55b59fc9863d6f380e304c15931e535d8bb9d259 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 25 Apr 2017 20:11:06 +1000 Subject: [PATCH 004/839] Move Searchbox control into main program, Update plugins with new Searchbox control --- ProcessHacker/ProcessHacker.rc | 14 +- ProcessHacker/ProcessHacker.vcxproj | 37 ++- ProcessHacker/ProcessHacker.vcxproj.filters | 15 + ProcessHacker/include/phapp.h | 64 ++++ ProcessHacker/resource.h | 6 +- ProcessHacker/resources/active_search.bmp | Bin 0 -> 1494 bytes ProcessHacker/resources/active_search.png | Bin 0 -> 569 bytes ProcessHacker/resources/inactive_search.bmp | Bin 0 -> 1494 bytes ProcessHacker/resources/inactive_search.png | Bin 0 -> 755 bytes .../CommonUtil => ProcessHacker}/searchbox.c | 297 +++++++++++++++--- plugins/CommonUtil/CommonUtil.vcxproj | 8 - plugins/CommonUtil/CommonUtil.vcxproj.filters | 20 -- plugins/CommonUtil/main.c | 4 - plugins/CommonUtil/main.h | 40 --- plugins/CommonUtil/png.c | 190 ----------- plugins/ExtraPlugins/dialog.c | 6 +- plugins/ExtraPlugins/setup/updater.c | 2 +- plugins/ExtraPlugins/wndtree.c | 6 +- plugins/NetworkTools/main.c | 2 +- plugins/NetworkTools/nettools.h | 1 + plugins/NetworkTools/ping.c | 2 +- plugins/NetworkTools/tracert.c | 2 +- plugins/NetworkTools/tracetree.c | 2 +- plugins/NetworkTools/whois.c | 2 +- plugins/ToolStatus/searchbox.c | 2 +- plugins/ToolStatus/toolbar.c | 18 +- plugins/ToolStatus/toolstatus.h | 1 + plugins/Updater/updater.c | 2 +- plugins/WindowExplorer/wnddlg.c | 4 +- plugins/WindowExplorer/wndexp.h | 1 + plugins/include/commonutil.h | 117 ------- 31 files changed, 403 insertions(+), 462 deletions(-) create mode 100644 ProcessHacker/resources/active_search.bmp create mode 100644 ProcessHacker/resources/active_search.png create mode 100644 ProcessHacker/resources/inactive_search.bmp create mode 100644 ProcessHacker/resources/inactive_search.png rename {plugins/CommonUtil => ProcessHacker}/searchbox.c (59%) delete mode 100644 plugins/CommonUtil/png.c diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 1fdef831b78a..6b3a616f4b86 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -2498,7 +2498,7 @@ BEGIN VK_F6, IDC_PAUSE, VIRTKEY, NOINVERT VK_PAUSE, IDC_PAUSE, VIRTKEY, NOINVERT VK_F5, IDC_REFRESH, VIRTKEY, NOINVERT - VK_F11, IDC_MAXSCREEN, VIRTKEY, NOINVERT + VK_F11, IDC_MAXSCREEN, VIRTKEY, NOINVERT END @@ -2508,8 +2508,9 @@ END // 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" ///////////////////////////////////////////////////////////////////////////// @@ -2590,6 +2591,15 @@ BEGIN 0 END + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" +IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index ae99a1ad051e..10b281657fd7 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -95,7 +95,7 @@ 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) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX86 @@ -109,8 +109,10 @@ Generating revision number... - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup - Cleaning up build... + + + + @@ -127,7 +129,7 @@ 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) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX64 @@ -141,8 +143,10 @@ Generating revision number... - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup - Cleaning up build... + + + + @@ -163,7 +167,7 @@ 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) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -180,8 +184,10 @@ Generating revision number... - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup - Cleaning up build... + + + + @@ -201,7 +207,7 @@ 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) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -218,8 +224,10 @@ Generating revision number... - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleanup - Cleaning up build... + + + + @@ -322,6 +330,7 @@ + @@ -428,7 +437,11 @@ + + + + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 85a5e1b50318..6b6d551e6e04 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -408,6 +408,9 @@ Process Hacker + + Process Hacker + @@ -634,5 +637,17 @@ Resources + + Resources + + + Resources + + + Resources + + + Resources + \ No newline at end of file diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 077f2e7b828a..b9fafef05156 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -605,6 +605,70 @@ NTSTATUS PhInvokeRunAsService( _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters ); +// searchbox + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhCreateSearchControl( + _In_ HWND Parent, + _In_ HWND WindowHandle, + _In_ PWSTR BannerText + ); + +PHAPPAPI +HBITMAP +NTAPI +PhLoadPngImageFromResource( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ); + +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) + SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE); + + return fontHandle; +} +// end_phapppub + // sessmsg VOID PhShowSessionSendMessageDialog( diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index cd3680fce5b1..9d8e082d45af 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -121,6 +121,10 @@ #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 IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 @@ -738,7 +742,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 223 +#define _APS_NEXT_RESOURCE_VALUE 227 #define _APS_NEXT_COMMAND_VALUE 40295 #define _APS_NEXT_CONTROL_VALUE 1385 #define _APS_NEXT_SYMED_VALUE 169 diff --git a/ProcessHacker/resources/active_search.bmp b/ProcessHacker/resources/active_search.bmp new file mode 100644 index 0000000000000000000000000000000000000000..61b13de9a1152694de72dd81c01c47282773298e GIT binary patch literal 1494 zcmZ?ry~fG_24+A~1Bk_eSOka}8650^YlUPk4#vGuUW6N6R zWNHFQJcaSx|X%2hHw&(QFzR`wykGxlOd1cH4UFR%aZTy0vQ4UK=!p| e$uJ%|jmI2xrFi+H=HLw;0_g|8USwG+hBE*$K{!VM literal 0 HcmV?d00001 diff --git a/ProcessHacker/resources/active_search.png b/ProcessHacker/resources/active_search.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf29576b0b50ed5ee88c1ea8565fe2e9adb9b69 GIT binary patch literal 569 zcmV-90>=G`P)sM}kty8!R#X_B;6=p#3k05!yiVY9d zlP4*H^pRy55Oc`+@ds70n9+mE9;(NrCYe{xn9wV|9VBsU+d(|PXf{(L44hUrH>@-V-11oc);or!AQP^ zGA=1rIynoT>*g-{55qdy^B+mtM4?zjp=AgQmBSj+v@_=Xhhe3lstAxc%pz)8#;6wA zW~r4l^}SZ|wEr*+mw^5uN{4!`dE+8h9B2g+;~9Laf!o+nq2=@QC~4{5 zzNMIgKr``}1C-rcX?bp5HnP>n`l5h>$RZFnesj((%HG>#cy3wt`|HP^U*3CeUNVq- zZ5@_$h2I>YUDuXnAL}xNqidV67y?vA&>V=f-`}3|{{B)38!JhqIau`~3l2ha0LuUq A!~g&Q literal 0 HcmV?d00001 diff --git a/ProcessHacker/resources/inactive_search.png b/ProcessHacker/resources/inactive_search.png new file mode 100644 index 0000000000000000000000000000000000000000..29eaa91305b3de1ce3854f19d86780a552bff734 GIT binary patch literal 755 zcmV;^k2Oj%`vu0L~h9Ndt{Z?B0N*TK5C5_$jUJj2-f3Mf; z=Q4%>h(lx3%*O9D(}M#pG|-AZmB~Y^5Kn3OGXm_qV;y8!p2Ycr{A~&Vh&eaG$uG|C z6Av0_h0HQDGp8TB=MgF`eoUj*zR2Kp69(V&kV6d|aG`-#)NJOFTW(>}f=4uY!z&#( zy!b0FYT$qi4YZzi z!U4p{@mX8WMy*7RX54w@=Gkk?VfutyFI>F1**tDmn1AarCNdTRhz$lqT^w#vqiIjf z++4ek*{XCNwS|Dwx6J)v#I?d`27J0X0K~|N-h&;@?7SR0m@dn1EiEQs_o;`$Fyb>) z{!a}>%}S)Dt6pr_b2g-Y=aI^?%}2xWn%Jm@kdeTRQJW3s_yq*>%`QLV|Bc0r1Qr_O lJMbXX`SgwQ{nsmReE`jH|7l`4W6l5o002ovPDHLkV1naFZkYf8 literal 0 HcmV?d00001 diff --git a/plugins/CommonUtil/searchbox.c b/ProcessHacker/searchbox.c similarity index 59% rename from plugins/CommonUtil/searchbox.c rename to ProcessHacker/searchbox.c index b79ea2096480..c07b6cb2fd7f 100644 --- a/plugins/CommonUtil/searchbox.c +++ b/ProcessHacker/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,12 +21,56 @@ * */ -#include "main.h" +#include +#include +#define CINTERFACE +#define COBJMACROS +#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; +} EDIT_CONTEXT, *PEDIT_CONTEXT; + +HBITMAP PhpSearchLoadPngImageFromResources( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ); + +HICON PhpSearchBitmapToIcon( + _In_ HBITMAP BitmapHandle, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhpSearchFreeTheme( _Inout_ PEDIT_CONTEXT Context ) { @@ -40,17 +84,19 @@ VOID NcAreaFreeTheme( DeleteObject(Context->BrushPushed); } -VOID NcAreaInitializeFont( +VOID PhpSearchInitializeFont( _Inout_ PEDIT_CONTEXT Context ) { - if (Context->WindowFont) DeleteObject(Context->WindowFont); - Context->WindowFont = CommonCreateFont(10, FW_MEDIUM, Context->WindowHandle); + if (Context->WindowFont) + DeleteObject(Context->WindowFont); + + Context->WindowFont = PhCreateCommonFont(10, FW_MEDIUM, Context->WindowHandle); } -VOID NcAreaInitializeTheme( +VOID PhpSearchInitializeTheme( _Inout_ PEDIT_CONTEXT Context -) + ) { Context->CXWidth = PhMultiplyDivide(20, PhGlobalDpi, 96); Context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); @@ -89,42 +135,42 @@ VOID NcAreaInitializeTheme( } } -VOID NcAreaInitializeImages( +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); + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(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); + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(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); + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(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); + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(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 +178,7 @@ VOID NcAreaGetButtonRect( ButtonRect->top += Context->CXBorder; } -VOID NcAreaDrawButton( +VOID PhpSearchDrawButton( _Inout_ PEDIT_CONTEXT Context, _In_ RECT ButtonRect ) @@ -207,14 +253,14 @@ VOID NcAreaDrawButton( 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 -) + ) { PEDIT_CONTEXT context; @@ -224,12 +270,12 @@ LRESULT CALLBACK NcAreaWndSubclassProc( { case WM_NCDESTROY: { - NcAreaFreeTheme(context); + PhpSearchFreeTheme(context); if (context->WindowFont) DeleteObject(context->WindowFont); - RemoveWindowSubclass(hWnd, NcAreaWndSubclassProc, uIdSubclass); + RemoveWindowSubclass(hWnd, PhpSearchWndSubclassProc, uIdSubclass); RemoveProp(hWnd, L"SearchBoxContext"); PhFree(context); } @@ -261,10 +307,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 +326,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 +348,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 +374,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 +408,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 +435,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 +479,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); @@ -447,7 +493,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( return DefSubclassProc(hWnd, uMsg, wParam, lParam); } -HICON BitmapToIcon( +HICON PhpSearchBitmapToIcon( _In_ HBITMAP BitmapHandle, _In_ INT Width, _In_ INT Height @@ -473,7 +519,7 @@ HICON BitmapToIcon( return icon; } -VOID UtilCreateSearchControl( +VOID PhCreateSearchControl( _In_ HWND Parent, _In_ HWND WindowHandle, _In_ PWSTR BannerText @@ -486,8 +532,8 @@ VOID UtilCreateSearchControl( context->WindowHandle = WindowHandle; - //NcAreaInitializeTheme(context); - NcAreaInitializeImages(context); + //PhpSearchInitializeTheme(context); + PhpSearchInitializeImages(context); // Set initial text Edit_SetCueBannerText(context->WindowHandle, BannerText); @@ -496,8 +542,173 @@ VOID UtilCreateSearchControl( SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); // Subclass the Edit control window procedure. - SetWindowSubclass(context->WindowHandle, NcAreaWndSubclassProc, 0, (ULONG_PTR)context); + SetWindowSubclass(context->WindowHandle, PhpSearchWndSubclassProc, 0, (ULONG_PTR)context); // Initialize the theme parameters. SendMessage(context->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; + 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/CommonUtil.vcxproj b/plugins/CommonUtil/CommonUtil.vcxproj index 2f1bb83743f6..c2b017b83820 100644 --- a/plugins/CommonUtil/CommonUtil.vcxproj +++ b/plugins/CommonUtil/CommonUtil.vcxproj @@ -92,8 +92,6 @@ - - @@ -119,11 +117,5 @@ - - - - - - \ No newline at end of file diff --git a/plugins/CommonUtil/CommonUtil.vcxproj.filters b/plugins/CommonUtil/CommonUtil.vcxproj.filters index 4cff34c3c810..83a49f7f2074 100644 --- a/plugins/CommonUtil/CommonUtil.vcxproj.filters +++ b/plugins/CommonUtil/CommonUtil.vcxproj.filters @@ -25,12 +25,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -130,20 +124,6 @@ Header Files\json-c - - - Resource Files\Images - - - Resource Files\Images - - - Resource Files\Images - - - Resource Files\Images - - Resource Files diff --git a/plugins/CommonUtil/main.c b/plugins/CommonUtil/main.c index 26bc8af83ebe..ea019c1f52aa 100644 --- a/plugins/CommonUtil/main.c +++ b/plugins/CommonUtil/main.c @@ -27,10 +27,6 @@ PPH_PLUGIN PluginInstance; COMMONUTIL_INTERFACE PluginInterface = { COMMONUTIL_INTERFACE_VERSION, - - UtilCreateSearchControl, - UtilLoadImageFromResources, - UtilCreateJsonParser, UtilCleanupJsonParser, UtilGetJsonValueAsString, diff --git a/plugins/CommonUtil/main.h b/plugins/CommonUtil/main.h index 014638f6d94a..22604b66963c 100644 --- a/plugins/CommonUtil/main.h +++ b/plugins/CommonUtil/main.h @@ -44,46 +44,6 @@ 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, 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/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index c272e21a2e9f..b924fe09d2d9 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -239,9 +239,9 @@ INT_PTR CALLBACK CloudPluginsDlgProc( 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); + PhCreateSearchControl(hwndDlg, context->SearchHandle, L"Search Plugins (Ctrl+K)"); + context->NormalFontHandle = PhCreateCommonFont(-14, FW_NORMAL, NULL); + context->BoldFontHandle = PhCreateCommonFont(-16, FW_BOLD, NULL); PhCenterWindow(hwndDlg, PhMainWndHandle); InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index d3b20ea48b02..a17321e1cc1c 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -735,7 +735,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( // NULL // ); // - // CommonCreateFont(-11, hwndEdit); + // PhCreateCommonFont(-11, hwndEdit); // // // Add text to the window. // SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST"); diff --git a/plugins/ExtraPlugins/wndtree.c b/plugins/ExtraPlugins/wndtree.c index 21604c7d70dd..54be25aaa2a8 100644 --- a/plugins/ExtraPlugins/wndtree.c +++ b/plugins/ExtraPlugins/wndtree.c @@ -314,7 +314,7 @@ BOOLEAN NTAPI PluginsTreeNewCallback( { HBITMAP bitmapActive; - if (bitmapActive = LoadImageFromResources(PluginInstance->DllBase, 17, 17, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) + if (bitmapActive = PhLoadPngImageFromResource(PluginInstance->DllBase, 17, 17, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) { node->Icon = CommonBitmapToIcon(bitmapActive, 17, 17); DeleteObject(bitmapActive); @@ -439,8 +439,8 @@ VOID InitializePluginsTree( Context->TreeNewHandle = TreeNewHandle; PhSetControlTheme(TreeNewHandle, L"explorer"); - Context->NormalFontHandle = CommonCreateFont(-10, FW_NORMAL, NULL); - Context->TitleFontHandle = CommonCreateFont(-14, FW_BOLD, NULL); + Context->NormalFontHandle = PhCreateCommonFont(-10, FW_NORMAL, NULL); + Context->TitleFontHandle = PhCreateCommonFont(-14, FW_BOLD, NULL); TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, Context); TreeNew_SetRowHeight(TreeNewHandle, 48); diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 10922e4581cd..8134d8ed4f8c 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -580,7 +580,7 @@ VOID NTAPI TreeNewMessageCallback( if ((resourceCode = LookupResourceCode(extension->RemoteCountryCode)) != 0) { - if (countryBitmap = LoadImageFromResources(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) + if (countryBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) { extension->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); } diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index a0a36d9222e0..f79de0beb6d7 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -28,6 +28,7 @@ #define COBJMACROS #include #include +#include #include #include #include diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index c485c8131f33..a971a9c83a86 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -295,7 +295,7 @@ INT_PTR CALLBACK NetworkPingWndProc( 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->FontHandle = PhCreateCommonFont(-15, FW_MEDIUM, context->StatusHandle); context->PingGraphHandle = CreateWindow( PH_GRAPH_CLASSNAME, NULL, diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 967ede48a701..b1c63a06a26f 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -579,7 +579,7 @@ INT_PTR CALLBACK TracertDlgProc( context->WindowHandle = hwndDlg; context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST_TRACERT); - context->FontHandle = CommonCreateFont(-15, FW_MEDIUM, GetDlgItem(hwndDlg, IDC_STATUS)); + context->FontHandle = PhCreateCommonFont(-15, FW_MEDIUM, GetDlgItem(hwndDlg, IDC_STATUS)); InitializeTracertTree(context); diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 5b7ec58b8463..a8deb46521b9 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -411,7 +411,7 @@ BOOLEAN NTAPI TracertTreeNewCallback( { HBITMAP countryBitmap; - if (countryBitmap = LoadImageFromResources(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) + if (countryBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) { node->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); } diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index c14a847273f1..4755af27061f 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -409,7 +409,7 @@ INT_PTR CALLBACK NetworkOutputDlgProc( 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); + context->FontHandle = PhCreateCommonFont(-11, FW_MEDIUM, context->RichEditHandle); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); PhAddLayoutItem(&context->LayoutManager, context->RichEditHandle, NULL, PH_ANCHOR_ALL); diff --git a/plugins/ToolStatus/searchbox.c b/plugins/ToolStatus/searchbox.c index c23f5f5ba8bc..4cd68dadd33b 100644 --- a/plugins/ToolStatus/searchbox.c +++ b/plugins/ToolStatus/searchbox.c @@ -40,7 +40,7 @@ BOOLEAN CreateSearchboxControl( NULL )) { - CreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); + PhCreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); return TRUE; } diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index aeadee129653..d5609cda8848 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -442,7 +442,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_ARROW_REFRESH_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_ARROW_REFRESH_MODERN), FALSE); } else { @@ -458,7 +458,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_COG_EDIT_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_COG_EDIT_MODERN), FALSE); } else { @@ -474,7 +474,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_FIND_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_FIND_MODERN), FALSE); } else { @@ -490,7 +490,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CHART_LINE_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CHART_LINE_MODERN), FALSE); } else { @@ -506,7 +506,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_MODERN), FALSE); } else { @@ -522,7 +522,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GO_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GO_MODERN), FALSE); } else { @@ -538,7 +538,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CROSS_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CROSS_MODERN), FALSE); } else { @@ -554,7 +554,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GET_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GET_MODERN), FALSE); } else { @@ -570,7 +570,7 @@ HBITMAP ToolbarGetImage( if (ToolStatusConfig.ModernIcons) { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_POWER_MODERN), FALSE); + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_POWER_MODERN), FALSE); } else { diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 1bf451bf28f0..45b08fd9f807 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -28,6 +28,7 @@ #define COBJMACROS #define INITGUID #include +#include #include #include #include diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 4742288974d8..d910e02f6d94 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -1165,7 +1165,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( // NULL // ); // - // CommonCreateFont(-11, hwndEdit); + // PhCreateCommonFont(-11, hwndEdit); // // // Add text to the window. // SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST"); diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index 030850942bb2..189d52ea3ea9 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -353,7 +353,7 @@ INT_PTR CALLBACK WepWindowsDlgProc( SetWindowText(hwndDlg, PH_AUTO_T(PH_STRING, WepGetWindowTitleForSelector(&context->Selector))->Buffer); - CreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); + PhCreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); @@ -788,7 +788,7 @@ INT_PTR CALLBACK WepWindowsPageProc( context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); - CreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); + PhCreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index 804350ae9030..05c330d571b8 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -2,6 +2,7 @@ #define WNDEXP_H #include +#include #include "wndtree.h" extern BOOLEAN IsHookClient; diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index 737d9328b42f..a6e265992413 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -27,20 +27,6 @@ #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 ); @@ -123,10 +109,6 @@ typedef PPH_LIST (NTAPI *PUTIL_GET_JSON_OBJECT_ARRAY_LIST)( 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; @@ -145,65 +127,6 @@ typedef struct _COMMONUTIL_INTERFACE 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( @@ -646,46 +569,6 @@ 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( From e4f3ee8cc03e282d093f4e327f5ef75bf390f181 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 00:49:38 +1000 Subject: [PATCH 005/839] Add process properties handle searching, Add splitter control for resizing process properties > token groups/privileges controls --- .gitignore | 1 + CHANGELOG.txt | 1 + ProcessHacker/ProcessHacker.rc | 21 +- ProcessHacker/ProcessHacker.vcxproj | 1 + ProcessHacker/ProcessHacker.vcxproj.filters | 3 + ProcessHacker/findobj.c | 2 + ProcessHacker/hndllist.c | 59 ++--- ProcessHacker/include/appsup.h | 67 +++++ ProcessHacker/include/hndllist.h | 9 +- ProcessHacker/include/procprpp.h | 4 + ProcessHacker/prpghndl.c | 127 ++++++++- ProcessHacker/prpgtok.c | 22 -- ProcessHacker/resource.h | 5 +- ProcessHacker/searchbox.c | 15 +- ProcessHacker/splitter.c | 275 ++++++++++++++++++++ ProcessHacker/tokprp.c | 43 ++- 16 files changed, 571 insertions(+), 84 deletions(-) create mode 100644 ProcessHacker/splitter.c diff --git a/.gitignore b/.gitignore index 1a44e8b91e28..d8d3deaf0cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,7 @@ Desktop.ini build/installer/*.exe ProcessHacker/sdk/phapppub.h +ProcessHacker/include/phapprev.h plugins-extra/ /sdk/ diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 561d9aacd6a2..493211cc7e7d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ 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: diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 6b3a616f4b86..db1968f60b85 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -603,6 +603,7 @@ 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 + EDITTEXT IDC_HANDLESEARCH,110,4,143,14,ES_AUTOHSCROLL END IDD_PROCENVIRONMENT DIALOGEX 0, 0, 260, 260 @@ -723,7 +724,7 @@ 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 + EDITTEXT IDC_FILTER,32,8,224,14,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 @@ -746,8 +747,8 @@ BEGIN 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 + CONTROL "",IDC_GROUPS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,53,246,112 + CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,168,246,57 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 @@ -2508,8 +2509,11 @@ END // 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" @@ -2591,6 +2595,16 @@ BEGIN 0 END +IDD_PROCHANDLES AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_OBJTOKEN AFX_DIALOG_LAYOUT +BEGIN + 0 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -2598,6 +2612,7 @@ END // IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" + IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" #endif // English (Australia) resources diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 10b281657fd7..c2e557e676dc 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -334,6 +334,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 6b6d551e6e04..fbed89f82328 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -411,6 +411,9 @@ Process Hacker + + Process Hacker + diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 98c68b117ee3..182f4055e882 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -218,6 +218,8 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); PhFindObjectsListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); + PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), NULL); + PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index 0f6de031b6a6..c34947dd9502 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -109,6 +109,8 @@ VOID PhInitializeHandleList( TreeNew_SetSort(hwnd, 0, AscendingSortOrder); PhCmInitializeManager(&Context->Cm, hwnd, PHHNTLC_MAXIMUM, PhpHandleTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList); } VOID PhDeleteHandleList( @@ -177,39 +179,9 @@ VOID PhSetOptionsHandleList( _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); - } } } @@ -248,8 +220,8 @@ PPH_HANDLE_NODE PhAddHandleNode( PhAddEntryHashtable(Context->NodeHashtable, &handleNode); PhAddItemList(Context->NodeList, handleNode); - if (Context->HideUnnamedHandles && PhIsNullOrEmptyString(HandleItem->BestObjectName)) - handleNode->Node.Visible = FALSE; + if (Context->TreeFilterSupport.FilterList) + handleNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &handleNode->Node); PhEmCallObjectOperation(EmHandleNodeType, handleNode, EmObjectCreate); @@ -346,6 +318,29 @@ VOID PhUpdateHandleNode( 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 ) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index e631485c30b9..27912c57b459 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -499,4 +499,71 @@ FORCEINLINE PVOID PhpGenericPropertyPageHeader( #define SWP_SHOWWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_SHOWWINDOW) #define SWP_HIDEWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_HIDEWINDOW) +// splitter + +typedef struct _PH_HSPLITTER_CONTEXT +{ + union + { + ULONG Flags; + struct + { + ULONG Hot : 1; + ULONG Pushed : 1; + ULONG Moved : 1; + ULONG DragMode : 1; + ULONG Spare : 28; + }; + }; + + ULONG Height; + PH_LAYOUT_MANAGER LayoutManager; + PPH_LAYOUT_ITEM Topitem; + PPH_LAYOUT_ITEM Bottomitem; +} PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; + +PPH_HSPLITTER_CONTEXT PhInitializeHSplitterSupport( + _In_ HWND Parent, + _In_ HWND TopChild, + _In_ HWND BottomChild + ); + +VOID PhDeleteHSplitterSupportSupport( + _Inout_ PPH_HSPLITTER_CONTEXT Context + ); + +VOID PhHSplitterHandleWmSize( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhHSplitterHandleLButtonDown( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleLButtonUp( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleMouseMove( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleMouseLeave( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + #endif diff --git a/ProcessHacker/include/hndllist.h b/ProcessHacker/include/hndllist.h index 579b06ced3a2..4d7a1c8a555f 100644 --- a/ProcessHacker/include/hndllist.h +++ b/ProcessHacker/include/hndllist.h @@ -43,14 +43,16 @@ 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; PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; BOOLEAN EnableStateHighlighting; + BOOLEAN HideUnnamedHandles; + PPH_POINTER_LIST NodeStateList; } PH_HANDLE_LIST_CONTEXT, *PPH_HANDLE_LIST_CONTEXT; @@ -98,6 +100,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/procprpp.h b/ProcessHacker/include/procprpp.h index ffa14bee1e82..64149e5078b4 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -255,6 +255,7 @@ typedef struct _PH_HANDLES_CONTEXT PH_CALLBACK_REGISTRATION UpdatedEventRegistration; HWND WindowHandle; + HWND SearchboxHandle; // end_phapppub union @@ -271,6 +272,9 @@ typedef struct _PH_HANDLES_CONTEXT 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 diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index fe7dc80849fb..ada104e8e0b3 100644 --- a/ProcessHacker/prpghndl.c +++ b/ProcessHacker/prpghndl.c @@ -208,6 +208,78 @@ 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 processNode = (PPH_HANDLE_NODE)Node; + PPH_HANDLE_ITEM handleItem = processNode->HandleItem; + + if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->BestObjectName)) + return FALSE; + + if (PhIsNullOrEmptyString(handlesContext->SearchboxText)) + return TRUE; + + 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; + } + + return FALSE; +} + INT_PTR CALLBACK PhpProcessHandlesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -276,6 +348,9 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( ); 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); @@ -284,6 +359,8 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( 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); @@ -353,11 +430,10 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( 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); + + dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_HANDLESEARCH), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); @@ -367,9 +443,35 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( 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 = PhGetWindowText(handlesContext->SearchboxHandle); + + if (!PhEqualString(handlesContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhMoveReference(&handlesContext->SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(handlesContext->SearchboxText)) + { + // Expand any hidden nodes to make search results visible. + PhExpandAllHandleNodes(&handlesContext->ListContext, TRUE); + } - switch (id) + PhApplyTreeNewFilters(&handlesContext->ListContext.TreeFilterSupport); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SHOWCONTEXTMENU: { @@ -409,9 +511,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 +536,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); @@ -467,9 +569,10 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( BOOLEAN hide; hide = Button_GetCheck(GetDlgItem(hwndDlg, IDC_HIDEUNNAMEDHANDLES)) == BST_CHECKED; - - PhSetIntegerSetting(L"HideUnnamedHandles", hide); + + PhSetIntegerSetting(L"HideUnnamedHandles", hide); PhSetOptionsHandleList(&handlesContext->ListContext, hide); + PhApplyTreeNewFilters(&handlesContext->ListContext.TreeFilterSupport); } break; } diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index 2f8ec1cfe583..d6de9b5eaf9f 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -65,8 +65,6 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( if (!GetProp(hwndDlg, PhMakeContextAtom())) // LayoutInitialized { PPH_LAYOUT_ITEM dialogItem; - HWND groupsLv; - HWND privilegesLv; // This is a big violation of abstraction... @@ -80,10 +78,6 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( 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), @@ -93,22 +87,6 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( 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); } } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 9d8e082d45af..ce6d174476b4 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -532,6 +532,7 @@ #define IDC_DELETE 1382 #define IDC_EDIT 1383 #define IDC_NEW 1384 +#define IDC_HANDLESEARCH 1385 #define ID_MAINWND_PROCESSTL 2001 #define ID_MAINWND_SERVICETL 2002 #define ID_MAINWND_NETWORKTL 2003 @@ -742,9 +743,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 227 +#define _APS_NEXT_RESOURCE_VALUE 228 #define _APS_NEXT_COMMAND_VALUE 40295 -#define _APS_NEXT_CONTROL_VALUE 1385 +#define _APS_NEXT_CONTROL_VALUE 1387 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index c07b6cb2fd7f..4cc1c8e132af 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -56,14 +56,6 @@ typedef struct _EDIT_CONTEXT HBRUSH BrushHot; } EDIT_CONTEXT, *PEDIT_CONTEXT; -HBITMAP PhpSearchLoadPngImageFromResources( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ); - HICON PhpSearchBitmapToIcon( _In_ HBITMAP BitmapHandle, _In_ INT Width, @@ -91,7 +83,7 @@ VOID PhpSearchInitializeFont( if (Context->WindowFont) DeleteObject(Context->WindowFont); - Context->WindowFont = PhCreateCommonFont(10, FW_MEDIUM, Context->WindowHandle); + Context->WindowFont = PhCreateCommonFont(PH_SCALE_DPI(10), FW_MEDIUM, Context->WindowHandle); } VOID PhpSearchInitializeTheme( @@ -522,7 +514,7 @@ HICON PhpSearchBitmapToIcon( VOID PhCreateSearchControl( _In_ HWND Parent, _In_ HWND WindowHandle, - _In_ PWSTR BannerText + _In_opt_ PWSTR BannerText ) { PEDIT_CONTEXT context; @@ -536,7 +528,8 @@ VOID PhCreateSearchControl( PhpSearchInitializeImages(context); // Set initial text - Edit_SetCueBannerText(context->WindowHandle, BannerText); + if (BannerText) + Edit_SetCueBannerText(context->WindowHandle, BannerText); // Set our window context data. SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c new file mode 100644 index 000000000000..23682b89c28d --- /dev/null +++ b/ProcessHacker/splitter.c @@ -0,0 +1,275 @@ +/* + * Process Hacker - + * Splitter control + * + * 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 + +static INT nSplitterPos = 250; +static INT nSplitterBorder = 6; +static INT SplitterOffset = -4; + +VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height); + +PPH_HSPLITTER_CONTEXT PhInitializeHSplitterSupport( + _In_ HWND Parent, + _In_ HWND TopChild, + _In_ HWND BottomChild + ) +{ + PPH_HSPLITTER_CONTEXT context; + + context = PhAllocate(sizeof(PH_HSPLITTER_CONTEXT)); + memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); + + PhInitializeLayoutManager(&context->LayoutManager, Parent); + + context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); + context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); + + return context; +} + +VOID PhDeleteHSplitterSupportSupport( + _Inout_ PPH_HSPLITTER_CONTEXT Context + ) +{ + PhDeleteLayoutManager(&Context->LayoutManager); +} + +VOID PhHSplitterHandleWmSize( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ INT Width, + _In_ INT Height + ) +{ + // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. + + // Set the bottom margin of the top control. + Context->Topitem->Margin.bottom = Height - nSplitterPos - nSplitterBorder; + + // Set the top margin of the bottom control. + Context->Bottomitem->Margin.top = nSplitterPos + nSplitterBorder * 2; + + PhLayoutManagerLayout(&Context->LayoutManager); +} + +VOID PhHSplitterHandleLButtonDown( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + POINT pt; + HDC hdc; + RECT rect; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + GetWindowRect(hwnd, &rect); + ClientToScreen(hwnd, &pt); + + pt.x -= rect.left; + pt.y -= rect.top; + + // Adjust the coordinates (start from 0,0). + OffsetRect(&rect, -rect.left, -rect.top); + + if (pt.y < 0) + pt.y = 0; + if (pt.y > rect.bottom - 4) + pt.y = rect.bottom - 4; + + Context->DragMode = TRUE; + + SetCapture(hwnd); + + hdc = GetWindowDC(hwnd); + DrawXorBar(hdc, 1, pt.y - 2, rect.right - 2, 4); + ReleaseDC(hwnd, hdc); + + SplitterOffset = pt.y; +} + +VOID PhHSplitterHandleLButtonUp( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + HDC hdc; + RECT rect; + POINT pt; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + if (Context->DragMode == FALSE) + return; + + GetWindowRect(hwnd, &rect); + ClientToScreen(hwnd, &pt); + + pt.x -= rect.left; + pt.y -= rect.top; + + // Adjust the coordinates (start from 0,0). + OffsetRect(&rect, -rect.left, -rect.top); + + if (pt.y < 0) + pt.y = 0; + if (pt.y > rect.bottom - 4) + pt.y = rect.bottom - 4; + + hdc = GetWindowDC(hwnd); + DrawXorBar(hdc, 1, SplitterOffset - 2, rect.right - 2, 4); + ReleaseDC(hwnd, hdc); + + SplitterOffset = pt.y; + + Context->DragMode = FALSE; + + GetWindowRect(hwnd, &rect); + + pt.x += rect.left; + pt.y += rect.top; + + ScreenToClient(hwnd, &pt); + GetClientRect(hwnd, &rect); + + nSplitterPos = pt.y; + + // position the child controls + PhHSplitterHandleWmSize(Context, rect.right, rect.bottom); + + ReleaseCapture(); +} + +VOID PhHSplitterHandleMouseMove( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + HDC hdc; + RECT windowRect; + POINT windowPoint; + + //if (Context->DragMode == FALSE) + // return; + + windowPoint.x = GET_X_LPARAM(lParam); + windowPoint.y = GET_Y_LPARAM(lParam); + + GetWindowRect(hwnd, &windowRect); + ClientToScreen(hwnd, &windowPoint); + + windowPoint.x -= windowRect.left; + windowPoint.y -= windowRect.top; + + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + + if (windowPoint.y < 0) + windowPoint.y = 0; + if (windowPoint.y > windowRect.bottom - 4) + windowPoint.y = windowRect.bottom - 4; + + if (windowPoint.y != SplitterOffset) + { + hdc = GetWindowDC(hwnd); + + if (wParam & MK_LBUTTON) + { + DrawXorBar(hdc, 1, SplitterOffset - 2, windowRect.right - 2, 4); + DrawXorBar(hdc, 1, windowPoint.y - 2, windowRect.right - 2, 4); + } + else + { + //if (!Context->Hot) + { + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; + + Context->Hot = TRUE; + + SetCursor(LoadCursor(NULL, IDC_SIZENS)); + + TrackMouseEvent(&trackMouseEvent); + } + } + + ReleaseDC(hwnd, hdc); + SplitterOffset = windowPoint.y; + } +} + +VOID PhHSplitterHandleMouseLeave( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + if (Context->Hot) + { + Context->Hot = FALSE; + + // Reset the original cursor. + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } +} + +// http://www.catch22.net/tuts/splitter-windows +VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height) +{ + static WORD _dotPatternBmp[8] = + { + 0x00aa, 0x0055, 0x00aa, 0x0055, + 0x00aa, 0x0055, 0x00aa, 0x0055 + }; + + HBITMAP hbm; + HBRUSH hbr, hbrushOld; + + hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp); + hbr = CreatePatternBrush(hbm); + + SetBrushOrgEx(hdc, x1, y1, 0); + hbrushOld = (HBRUSH)SelectObject(hdc, hbr); + + PatBlt(hdc, x1, y1, width, height, PATINVERT); + + SelectObject(hdc, hbrushOld); + + DeleteObject(hbr); + DeleteObject(hbm); +} + diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index c973169123ce..034d6c65d1bb 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -50,6 +50,7 @@ typedef struct _TOKEN_PAGE_CONTEXT HWND GroupsListViewHandle; HWND PrivilegesListViewHandle; + PPH_HSPLITTER_CONTEXT HSplitterContext; PTOKEN_GROUPS Groups; PTOKEN_PRIVILEGES Privileges; @@ -423,6 +424,12 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSetControlTheme(groupsLv, L"explorer"); PhSetControlTheme(privilegesLv, L"explorer"); + tokenPageContext->HSplitterContext = PhInitializeHSplitterSupport( + hwndDlg, + tokenPageContext->GroupsListViewHandle, + tokenPageContext->PrivilegesListViewHandle + ); + PhAddListViewColumn(groupsLv, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); PhAddListViewColumn(groupsLv, 1, 1, 1, LVCFMT_LEFT, 200, L"Flags"); @@ -432,7 +439,6 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSetExtendedListView(groupsLv); ExtendedListView_SetItemColorFunction(groupsLv, PhpTokenGroupColorFunction); - PhSetExtendedListView(privilegesLv); ExtendedListView_SetItemColorFunction(privilegesLv, PhpTokenPrivilegeColorFunction); @@ -576,6 +582,19 @@ INT_PTR CALLBACK PhpTokenPageProc( ExtendedListView_SortItems(privilegesLv); } + 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); + } + NtClose(tokenHandle); } } @@ -584,6 +603,7 @@ INT_PTR CALLBACK PhpTokenPageProc( { if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); + if (tokenPageContext->HSplitterContext) PhDeleteHSplitterSupportSupport(tokenPageContext->HSplitterContext); } break; case WM_COMMAND: @@ -917,6 +937,27 @@ INT_PTR CALLBACK PhpTokenPageProc( } } break; + case WM_SIZE: + PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); + return 0; + case WM_LBUTTONDOWN: + PhHSplitterHandleLButtonDown(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + return 0; + case WM_LBUTTONUP: + PhHSplitterHandleLButtonUp(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + return 0; + case WM_MOUSEMOVE: + PhHSplitterHandleMouseMove(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + return 0; + case WM_NCMOUSELEAVE: + { + //if (context->Hot) + { + //context->Hot = FALSE; + //RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + } + break; } REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->GroupsListViewHandle, uMsg, wParam, lParam); From fe9e9ea84c28aa2e37b69d406b7c034c5ae1c3ed Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 01:04:54 +1000 Subject: [PATCH 006/839] Update gitattributes --- .gitattributes | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitattributes b/.gitattributes index 31335b861e1d..1f89c58b64ff 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,6 +9,19 @@ *.fsproj merge=union *.dbproj merge=union +# Project files +*.bat eol=crlf +*.c eol=crlf +*.config eol=crlf +*.cmd eol=crlf +*.csproj eol=crlf +*.h eol=crlf +*.manifest eol=crlf +*.rc eol=crlf +*.sln eol=crlf +*.txt eol=crlf +*.vcxproj eol=crlf + # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain From 2a9ed627eb3121542e685cd310822441e7cb8155 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 01:22:38 +1000 Subject: [PATCH 007/839] Updater: Make nightly builds the default branch --- plugins/Updater/Updater.rc | 293 +++-- plugins/Updater/main.c | 279 ++-- plugins/Updater/options.c | 148 +-- plugins/Updater/page3.c | 48 +- plugins/Updater/page4.c | 9 +- plugins/Updater/page5.c | 91 +- plugins/Updater/resource.h | 35 +- plugins/Updater/updater.c | 2508 +++++++++++++++++------------------- plugins/Updater/updater.h | 443 +++---- plugins/Updater/verify.c | 38 +- 10 files changed, 1861 insertions(+), 2031 deletions(-) diff --git a/plugins/Updater/Updater.rc b/plugins/Updater/Updater.rc index 163e82bf6de4..3819a0a7ca80 100644 --- a/plugins/Updater/Updater.rc +++ b/plugins/Updater/Updater.rc @@ -1,147 +1,146 @@ -// 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, 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 +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 + diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index 31095e0052cb..a6a73649f820 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -1,143 +1,138 @@ -/* - * 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; +/* + * 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" }, + }; + + 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 diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 1d3d06de2cf5..cbef57deb360 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -1,78 +1,72 @@ -/* - * 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 - ); +/* + * 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); + } + 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); + + 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 diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index 6b79f7aaede3..a34e8c6c07cf 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -52,15 +52,8 @@ HRESULT CALLBACK ShowAvailableCallbackProc( 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); - } + { + PhShellExecute(hwndDlg, L"https://wj32.org/processhacker/nightly.php", NULL); } } } @@ -68,6 +61,7 @@ HRESULT CALLBACK ShowAvailableCallbackProc( case TDN_HYPERLINK_CLICKED: { TaskDialogLinkClicked(context); + return S_FALSE; } break; } @@ -91,34 +85,18 @@ VOID ShowAvailableDialog( config.cButtons = ARRAYSIZE(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; + config.pszWindowTitle = L"Process Hacker - Updater"; + config.pszMainInstruction = L"A newer build of Process Hacker is available."; + config.pszContent = PhaFormatString(L"Version: %lu.%lu.%lu\r\nDownload size: %s\r\n\r\nView Changelog", + 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"; - } + //if (!PhIsNullOrEmptyString(Context->BuildMessage)) + // config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file diff --git a/plugins/Updater/page4.c b/plugins/Updater/page4.c index 206634cb4383..2f636ac13d0b 100644 --- a/plugins/Updater/page4.c +++ b/plugins/Updater/page4.c @@ -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; @@ -75,11 +76,7 @@ VOID ShowProgressDialog( Context->RevisionVersion )->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); + config.pszExpandedInformation = L"View Changelog"; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 58e22e502855..ca014212da1d 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -92,6 +92,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( case TDN_HYPERLINK_CLICKED: { TaskDialogLinkClicked(context); + return S_FALSE; } break; } @@ -128,6 +129,10 @@ 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); @@ -137,52 +142,22 @@ VOID ShowLatestVersionDialog( config.cxWidth = 200; config.pfCallback = FinalTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; + + // 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.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); - } + config.pszMainInstruction = L"You're running the latest version."; + config.pszContent = PhaFormatString( + L"Version: v%lu.%lu.%lu\r\nCompiled: %s\r\n\r\nView Changelog", + Context->CurrentMajorVersion, + Context->CurrentMinorVersion, + Context->CurrentRevisionVersion, + PhaFormatDateTime(&systemTime)->Buffer + )->Buffer; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } @@ -200,27 +175,13 @@ VOID ShowNewerVersionDialog( 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.pszMainInstruction = L"You're running a pre-release build."; + 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; diff --git a/plugins/Updater/resource.h b/plugins/Updater/resource.h index 77750932f2ba..11f1b12fef3e 100644 --- a/plugins/Updater/resource.h +++ b/plugins/Updater/resource.h @@ -1,18 +1,17 @@ -//{{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 103 +#define IDC_AUTOCHECKBOX 1002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1010 +#define _APS_NEXT_SYMED_VALUE 103 +#endif +#endif diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index d910e02f6d94..156148a2b59b 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -1,1295 +1,1215 @@ -/* - * 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 - // ); - // - // 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 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); +/* + * 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; + + 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; + 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_AUTOMATIC_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 (!(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; + } + + 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 (!(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; + + 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 + // ); + // + // 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 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 diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 2a5cafa71846..b2032e6f432d 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -1,220 +1,225 @@ -/* - * 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 - ); - +/* + * 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 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 + ); + +// info.c + +VOID ShowLinkDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + #endif \ No newline at end of file diff --git a/plugins/Updater/verify.c b/plugins/Updater/verify.c index 80dd999d870c..c2fb55e1780c 100644 --- a/plugins/Updater/verify.c +++ b/plugins/Updater/verify.c @@ -70,35 +70,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. From 863dc81a2941f6d567cd40b43aaa91dd8608ef28 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 02:28:58 +1000 Subject: [PATCH 008/839] Fix github attributes --- .gitattributes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1f89c58b64ff..7d0255299ac0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ -# Auto detect text files and perform LF normalization -* text=crlf +# Auto detect text files +* text=auto # Custom for Visual Studio *.cs diff=csharp From fd84dbc1b2f29bb4e24ffbe54b0955d00cf3ae1f Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 03:16:01 +1000 Subject: [PATCH 009/839] CustomSetupTool: Fix uninstalling setup --- tools/CustomBuildTool/Source Files/Program.cs | 126 ++++--- .../bin/Release/CustomBuildTool.exe | Bin 143360 -> 143360 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 62976 bytes .../CustomSetupTool/CustomSetupTool.vcxproj | 1 + .../CustomSetupTool.vcxproj.filters | 3 + .../CustomSetupTool/CustomSetupTool/extract.c | 9 +- .../CustomSetupTool/include/setup.h | 21 +- tools/CustomSetupTool/CustomSetupTool/main.c | 1 + tools/CustomSetupTool/CustomSetupTool/page4.c | 15 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 115 +++++- .../CustomSetupTool/uninstall.c | 326 ++++++++++++++++++ .../CustomSetupTool/CustomSetupTool/update.c | 97 ++---- 12 files changed, 572 insertions(+), 142 deletions(-) create mode 100644 tools/CustomSetupTool/CustomSetupTool/uninstall.c diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 4cc566b5c6d3..8774e49e97aa 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -34,8 +34,7 @@ public static void Main(string[] args) Build.CleanupBuildEnvironment(); return; } - - if (ProgramArgs.ContainsKey("-updaterev")) + else if (ProgramArgs.ContainsKey("-updaterev")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -45,8 +44,7 @@ public static void Main(string[] args) Build.UpdateHeaderFileVersion(); return; } - - if (ProgramArgs.ContainsKey("-phapppub_gen")) + else if (ProgramArgs.ContainsKey("-phapppub_gen")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -55,14 +53,12 @@ public static void Main(string[] args) Build.BuildPublicHeaderFiles(); return; } - - if (ProgramArgs.ContainsKey("-sign")) + else if (ProgramArgs.ContainsKey("-sign")) { Build.BuildKphSignatureFile(); return; } - - if (ProgramArgs.ContainsKey("-cleansdk")) + else if (ProgramArgs.ContainsKey("-cleansdk")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -89,8 +85,7 @@ public static void Main(string[] args) Build.ShowBuildStats(false); return; } - - if (ProgramArgs.ContainsKey("-sdk")) + else if (ProgramArgs.ContainsKey("-sdk")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -114,8 +109,7 @@ public static void Main(string[] args) Build.ShowBuildStats(false); return; } - - if (ProgramArgs.ContainsKey("-bin")) + else if (ProgramArgs.ContainsKey("-bin")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -151,8 +145,7 @@ public static void Main(string[] args) Build.ShowBuildStats(false); return; } - - if (ProgramArgs.ContainsKey("-exe")) + else if (ProgramArgs.ContainsKey("-exe")) { if (!Build.InitializeBuildEnvironment(false)) return; @@ -167,75 +160,78 @@ public static void Main(string[] args) Build.BuildSdkZip(); return; } + else + { - if (!Build.InitializeBuildEnvironment(true)) - return; + if (!Build.InitializeBuildEnvironment(true)) + return; - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); - Build.ShowBuildEnvironment(true); - Build.BuildSecureFiles(); + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); + Build.ShowBuildEnvironment(true); + Build.BuildSecureFiles(); - if (!Build.BuildSolution("ProcessHacker.sln", true, true)) - return; + if (!Build.BuildSolution("ProcessHacker.sln", true, true)) + return; - PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); - if (!Build.BuildKphSignatureFile()) - return; + PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + if (!Build.BuildKphSignatureFile()) + return; - PrintColorMessage("Copying text files...", ConsoleColor.Cyan); - if (!Build.CopyTextFiles()) - return; + PrintColorMessage("Copying text files...", ConsoleColor.Cyan); + if (!Build.CopyTextFiles()) + return; - PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); - if (!Build.CopyKProcessHacker()) - return; + PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); + if (!Build.CopyKProcessHacker()) + return; - PrintColorMessage("Copying SDK headers...", ConsoleColor.Cyan); - if (!Build.CopyPluginSdkHeaders()) - return; + PrintColorMessage("Copying SDK headers...", ConsoleColor.Cyan); + if (!Build.CopyPluginSdkHeaders()) + return; - PrintColorMessage("Copying version headers...", ConsoleColor.Cyan); - if (!Build.CopyVersionHeader()) - return; + PrintColorMessage("Copying version headers...", ConsoleColor.Cyan); + if (!Build.CopyVersionHeader()) + return; - PrintColorMessage("Building sdk resource header...", ConsoleColor.Cyan); - if (!Build.FixupResourceHeader()) - return; + PrintColorMessage("Building sdk resource header...", ConsoleColor.Cyan); + if (!Build.FixupResourceHeader()) + return; - PrintColorMessage("Copying plugin linker files...", ConsoleColor.Cyan); - if (!Build.CopyLibFiles()) - return; + PrintColorMessage("Copying plugin linker files...", ConsoleColor.Cyan); + if (!Build.CopyLibFiles()) + return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, true)) - return; + if (!Build.BuildSolution("plugins\\Plugins.sln", true, true)) + return; - PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); - if (!Build.CopyWow64Files()) - return; + PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); + if (!Build.CopyWow64Files()) + return; - PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); - if (!Build.BuildBinZip()) - return; + PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); + if (!Build.BuildBinZip()) + return; - //Build.BuildSrcZip(); - //Build.BuildSdkZip(); - //Build.BuildPdbZip(); + //Build.BuildSrcZip(); + //Build.BuildSdkZip(); + //Build.BuildPdbZip(); - PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); - if (!Build.BuildSetupExe()) - return; + PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); + if (!Build.BuildSetupExe()) + return; - PrintColorMessage("Building build-checksums.txt...", ConsoleColor.Cyan); - if (!Build.BuildChecksumsFile()) - return; + PrintColorMessage("Building build-checksums.txt...", ConsoleColor.Cyan); + if (!Build.BuildChecksumsFile()) + return; - PrintColorMessage("Building release signature...", ConsoleColor.Cyan); - if (!Build.BuildUpdateSignature()) - return; + PrintColorMessage("Building release signature...", ConsoleColor.Cyan); + if (!Build.BuildUpdateSignature()) + return; - Build.AppveyorUploadBuildFiles(); - Build.WebServiceUpdateConfig(); - Build.ShowBuildStats(true); + Build.AppveyorUploadBuildFiles(); + Build.WebServiceUpdateConfig(); + Build.ShowBuildStats(true); + } } private static Dictionary ParseArgs(string[] args) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 0416602b5cdd7fcbf6f32c9600c257ccb91d5a4a..29a6995bd64d0eb702065d554efad9fe67977c92 100644 GIT binary patch delta 70 zcmZp8z|ru4V?qa$SL4Pmx2XctHdQMpXpWDF;2rH zr4z){k%3A02qt4DX5&iC!)(k)R!drsk7F4=f#tXww_q;rz&xx)R#kc(SygEtK85?S z5L+z-PZNBIMc9t(uoIubWB4qd!{_i4@>--GEWz(^1KP0+uOqKZ8o>%2#Y!B*jd+KH z5N4c)RcN`3fJKn*Mu|TUtVU0K9(`~V&c+(_!_8>KEf|6?U?{$bi*PGO<4d>%w_yVA zz;vv|mH0B3ma+-hNYZNDg}L|&7GoV&;%?lCY&2;TveBd(d;_=Po46g{!dv1nO z;5)b%8*v}LiwCfYN0-_Nn(6om*|AbLw%}=O#fx|ld+`wV;RiT`ZTKr5#$jwnjiV49 z=z>RZI)02Ecoe;{6Me8tUiv_S;~0h~FdRR@7(9tf@f0TFr_Fxl!i_O@F?bwgU@G^Gc0G`1scpeAw0{(#C zpdI`0M;yeT(4A`HE}}T*3$2J2vxLE#ZFYCmi70E4*~_S-2&nzIg5v=Wz0iq;7^=pR z)@E}OV-{FP%(04*RV9kHD&I&g1%-L}Y!*itM*v5P+Lul|PmPxo2Th1g)*I%9sms)! zOtQffMg^#G#)MJw6-;XXqr~BAEMHZ=^C0VP%Ulb^OS+T7^|P`9Li~bS&aYk@m1dxP zuE$|C;&n9P2u9-{7>j>mJl@0v97Bql5E>z+$sn~%P!y#cbVf>vPPx!}I^+wLqN^=U z>R#;7QD8H49WhvprCVnz^_-MEnZNuQrD=q*w4K~^&8_p>bH`^1A>WzYCXaLQP)h8S zxfSH((kwSB@a>aUcv1)WDk;fHIk$U+@E%Y7=Yn&|MNOHb1e1$XbL!N}nS2k4jS~h= zO6ms4ZBPd~3Olc2yfILhb*~U^6LK~~Z-hf~N#PCpZ{4J`NGSYwOi7{MAjJ248{{@Q zH#%D>-T&mQd-^Lvv<2_oC)ilyZJN*(s3Fg?|C0FLP)6I@hnwzN1Q$wbz%=7i**Ts}M=iw-F@{X08yKo-1*#`_x|MQ!~e#QN7 D??8_E delta 2247 zcmaLZdr%cs9Ki9jJQ9g(11MbcqWK3Gh=-x6B}hC<1S23IOS14)P0+)f2vE3dibX0# zn-8QK1QI7o$(xBdfYfMVqfCZbW`d#(IRwq9*pxxvv+Qk@VLIdT`JLZych9+J?>(#9 z>#X)V*Solz+e|}`D?*6(8$T-Oh$xxAV`ll(Zwiu{?H2bCkMY-*6j#J$eO{ILN3OE_ zF5heI3!~aUi>XoencckHd=KUiR5qPdwA8cSWhs94=(__I7xNACkUb|jFUe*VLS4+A z191fg;WHSFt1txfF%)eWhAM_*8P33Rycc)iEUd-Z_$l6pdoTi9a4sIfC_E~JO~eqK zCJ~37_#k%SJnX}G?8gMWfr;qMS}Z^pOhQk51kIR?6EPL7_$Y>A8irv8M&M%@tMHK) z62y^s0uyi%CL*h%X5!Ph46|@KF2$9&4D)a~<|C`7Zop@8Gd_o<_&io%HdbK{R^y9U zZzFh#-~hgi4fqN+Bg?P0U@m@-dH6H(IMlPa8ZY1)?7<=&Kt7Xt9aa7f;#wTS*YP$d zA=cp-T#vSK1jPh*;0E-;H_#W~#K~BKQ}8Xc;zkU>O&EroaT=E5Tzng&@Ev>z`JSlr zP{TBQ4;SGU=Bj2AY$LG*x8qW*z*YDGuEq~>4OZeh+==V48aHAMZo*yoF4p39+>PZ} zhdc2TtinCG7x%JO>VATH5{GafwxJ!5;eI@gU*I`BfSq^{d$AF(;32$@2M9+A%~nBXW*$FFe)wqZ0L!+87_=i_nAz!Uf+w&P;_4zuwj=3ocr z<0(|}2V9FkVk!QFThMlfU@JivR^xg67%yNQUc?5xge~|h9>yL#j=y0CUdB_{i)Zk6 z?8H92g#Bn@S22l5j+w%f9bkg6Xqkm3XOoz%Wfx8iFo`*iJf?Hp!(rz9={U`ZXK8hX z30xDdT`!E$jVO9m*IwjrGl~0+^D?+DjKj(iZ{(7R9rs*79BAZa9BRZ56FbIAAr86a ze#7%gCm&$s6Afi9DB9@AfviTHO6(X&PRcP*8nI&_InfY#p0>>vWV^y*-7bWrRjKM*SLq*{Si0qqcGO}-~spyJn=!RK17GDs; zOCM+@2|3VfIL@A`wmslf5o@;`ZnW6SVoSyg=04mMQNs@6h|6LdsBtH~jjXj!5OT*K zwdUlb@7AN%(y7VaTsAiCTL&!g6+-^(_2P({$DKm>4VT2o$plf09JLl&yn z_Y@IY>{=@?)S7b&Hu%vn$S&O75zlyS%IP5*$7MK6||4Gk9)4#s?kG%i@ diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 6dd368455d93..3cce033fdf19 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -118,6 +118,7 @@ + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index 1731321acc26..f3ed066b91de 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -63,6 +63,9 @@ Source Files + + Source Files + diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 7a19147bac2d..1c600f73a771 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -50,7 +50,7 @@ PVOID GetZipResourceData( } BOOLEAN SetupExtractBuild( - _In_ PVOID Arguments + _In_ PSETUP_PROGRESS_THREAD Context ) { mz_bool status = MZ_FALSE; @@ -73,9 +73,6 @@ BOOLEAN SetupExtractBuild( //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; @@ -100,7 +97,7 @@ BOOLEAN SetupExtractBuild( InterlockedExchange64(&ExtractTotalLength, totalLength); } - SendMessage(Arguments, WM_START_SETUP, 0, 0); + SendMessage(Context->DialogHandle, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) { @@ -218,7 +215,7 @@ BOOLEAN SetupExtractBuild( currentLength += bufferLength; InterlockedExchange64(&ExtractCurrentLength, currentLength); - SendMessage(Arguments, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); + SendMessage(Context->DialogHandle, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); NtClose(fileHandle); mz_free(buffer); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 2b4a6ecec879..bfbb5e84a9cf 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -128,6 +128,7 @@ typedef struct _SETUP_PROGRESS_THREAD { HWND DialogHandle; HWND PropSheetHandle; + HICON PropSheetIcon; } SETUP_PROGRESS_THREAD, *PSETUP_PROGRESS_THREAD; extern BOOLEAN SetupRunning; @@ -148,10 +149,18 @@ NTSTATUS SetupCreateUninstallKey( VOID ); +NTSTATUS SetupDeleteUninstallKey( + VOID + ); + VOID SetupSetWindowsOptions( VOID ); +VOID SetupDeleteWindowsOptions( + VOID + ); + VOID SetupCreateUninstallFile( VOID ); @@ -160,10 +169,14 @@ BOOLEAN SetupExecuteProcessHacker( _In_ HWND Parent ); +VOID SetupUpgradeSettingsFile( + VOID + ); + // extract.c BOOLEAN SetupExtractBuild( - _In_ PVOID Arguments + _In_ PSETUP_PROGRESS_THREAD Context ); // update.c @@ -172,4 +185,10 @@ VOID SetupShowUpdateDialog( VOID ); +// uninstall.c + +VOID SetupShowUninstallDialog( + VOID + ); + #endif \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 694a1809cb9c..19103442a8c2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -182,6 +182,7 @@ INT WINAPI wWinMain( } break; case SETUP_COMMAND_UNINSTALL: + SetupShowUninstallDialog(); break; case SETUP_COMMAND_UPDATE: SetupShowUpdateDialog(); diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index 485fa6370bd3..d8d55b33f949 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -41,24 +41,25 @@ NTSTATUS SetupProgressThread( if (!SetupUninstallKph()) goto CleanupExit; - // Remove the previous installation. - if (SetupResetSettings) - RemoveDirectoryPath(PhGetString(SetupInstallPath)); - // Create the install folder path. if (!CreateDirectoryPath(PhGetString(SetupInstallPath))) goto CleanupExit; + SetupUpgradeSettingsFile(); + + // Remove the previous installation. + if (SetupResetSettings) + RemoveDirectoryPath(PhGetString(SetupInstallPath)); + // Create the ARP uninstall entries. SetupCreateUninstallKey(); // Create the uninstaller. SetupCreateUninstallFile(); - - // + // Create autorun and shortcuts. SetupSetWindowsOptions(); // Setup new installation. - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; // Install updated kernel driver diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index f73deb19f54e..d32e501f5282 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -97,7 +97,33 @@ NTSTATUS SetupCreateUninstallKey( return status; } -VOID SetupFindInstallDirectory(VOID) +NTSTATUS SetupDeleteUninstallKey( + VOID + ) +{ + NTSTATUS status; + HANDLE keyHandle; + + status = PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &UninstallKeyName, + 0 + ); + + if (NT_SUCCESS(status)) + { + status = NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + return status; +} + +VOID SetupFindInstallDirectory( + VOID + ) { // Find the current installation path. SetupInstallPath = GetProcessHackerInstallPath(); @@ -207,7 +233,9 @@ VOID SetupInstallKph( PhDereferenceObject(clientPath); } -ULONG SetupUninstallKph(VOID) +ULONG SetupUninstallKph( + VOID + ) { ULONG status = ERROR_SUCCESS; SC_HANDLE scmHandle; @@ -273,7 +301,7 @@ VOID SetupSetWindowsOptions( if (SetupResetSettings) { - PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\settings.xml"); + PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); SetupDeleteDirectoryFile(settingsFileName->Buffer); PhDereferenceObject(settingsFileName); @@ -359,6 +387,68 @@ VOID SetupSetWindowsOptions( } } +VOID SetupDeleteWindowsOptions( + VOID + ) +{ + 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"); + PPH_STRING startmenuFolderString; + HANDLE keyHandle; + + if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) + { + SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); + } + + if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk")) + { + SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); + } + + if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")); + { + SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); + } + + if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + { + SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); + } + + //PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); + //SetupDeleteDirectoryFile(settingsFileName->Buffer); + //PhDereferenceObject(settingsFileName); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &TaskMgrImageOptionsKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE, + PH_KEY_CURRENT_USER, + &CurrentUserRunKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } +} + BOOLEAN SetupExecuteProcessHacker( _In_ HWND Parent ) @@ -383,4 +473,23 @@ BOOLEAN SetupExecuteProcessHacker( PhDereferenceObject(clientPath); return success; +} + +VOID SetupUpgradeSettingsFile( + VOID + ) +{ + PPH_STRING settingsFilePath; + PPH_STRING oldSettingsFileName; + + settingsFilePath = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); + oldSettingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\settings.xml"); + + if (!RtlDoesFileExists_U(settingsFilePath->Buffer)) + { + CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); + } + + PhDereferenceObject(oldSettingsFileName); + PhDereferenceObject(settingsFilePath); } \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c new file mode 100644 index 000000000000..f2d5124f120a --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -0,0 +1,326 @@ +/* + * 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 + +typedef struct _PH_SETUP_UNINSTALL_CONTEXT +{ + HWND DialogHandle; + HICON IconSmallHandle; + HICON IconLargeHandle; +} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; + +#define WM_TASKDIALOGINIT (WM_APP + 550) +HWND UninstallDialogHandle = NULL; +HANDLE UninstallDialogThreadHandle = NULL; +PH_EVENT UninstallInitializedEvent = PH_EVENT_INIT; + +VOID ShowUninstallConfirmDialog( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); +VOID ShowUninstallDialog( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); +VOID ShowUninstallCompleteDialog( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); +VOID ShowUninstallErrorDialog( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); + +NTSTATUS SetupUninstallBuild( + _In_ PVOID Context + ) +{ + PPH_SETUP_UNINSTALL_CONTEXT context = Context; + + SetupFindInstallDirectory(); + + // Stop Process Hacker. + if (!ShutdownProcessHacker()) + goto CleanupExit; + + // Stop the kernel driver(s). + if (!SetupUninstallKph()) + goto CleanupExit; + + // Remove autorun and shortcuts. + SetupDeleteWindowsOptions(); + + // Remove the previous installation. + if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) + goto CleanupExit; + + // Remove the ARP uninstall entry. + SetupDeleteUninstallKey(); + + ShowUninstallCompleteDialog(context); + return STATUS_SUCCESS; + +CleanupExit: + ShowUninstallErrorDialog(context); + return STATUS_SUCCESS; +} + +static VOID TaskDialogCreateIcons( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ) +{ + HICON largeIcon; + HICON smallIcon; + + largeIcon = PhLoadIcon( + NtCurrentPeb()->ImageBaseAddress, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON) + ); + smallIcon = PhLoadIcon( + NtCurrentPeb()->ImageBaseAddress, + 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_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_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_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_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_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_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_UNINSTALL_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_UNINSTALL_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_UNINSTALL_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_UNINSTALL_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_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_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_UNINSTALL_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_UNINSTALL_CONTEXT)); + memset(context, 0, sizeof(PH_SETUP_UNINSTALL_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..5b02a4002cd3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -24,7 +24,6 @@ typedef struct _PH_SETUP_UPDATE_CONTEXT { - BOOLEAN FixedWindowStyles; HWND DialogHandle; HICON IconSmallHandle; HICON IconLargeHandle; @@ -35,7 +34,7 @@ typedef struct _PH_SETUP_UPDATE_CONTEXT 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; @@ -56,23 +55,22 @@ NTSTATUS SetupUpdateBuild( SetupCreateUninstallFile(); //SetupSetWindowsOptions(); - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; if (SetupInstallKphService) SetupInstallKph(); - if (SetupExecuteProcessHacker(Context->DialogHandle)) - { - PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); - } + if (!SetupExecuteProcessHacker(Context->DialogHandle)) + 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, IDD_ERROR, 0, 0); PhDereferenceObject(Context); return STATUS_FAIL_CHECK; } @@ -117,29 +115,29 @@ VOID TaskDialogCreateIcons( _In_ PPH_SETUP_UPDATE_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); + HICON largeIcon; + HICON smallIcon; + + largeIcon = PhLoadIcon( + NtCurrentPeb()->ImageBaseAddress, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON) + ); + smallIcon = PhLoadIcon( + NtCurrentPeb()->ImageBaseAddress, + 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 TaskDialogLinkClicked( @@ -162,12 +160,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( switch (uMsg) { - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_SHOWDIALOG: + case WM_TASKDIALOGINIT: { if (IsMinimized(hwndDlg)) ShowWindow(hwndDlg, SW_RESTORE); @@ -177,26 +170,11 @@ 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_NCDESTROY: + { + RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); + } + break; } return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); @@ -241,8 +219,7 @@ VOID ShowCheckForUpdatesDialog( config.pfCallback = CheckingForUpdatesCallbackProc; 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.pszMainInstruction = PhaFormatString(L"Updating to version %lu.%lu.%lu...", PHAPP_VERSION_MAJOR, PHAPP_VERSION_MINOR, PHAPP_VERSION_REVISION)->Buffer; config.cxWidth = 200; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); @@ -273,7 +250,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( // Navigate to the first page ShowCheckForUpdatesDialog(context); - PostMessage(hwndDlg, WM_SHOWDIALOG, 0, 0); + SendMessage(hwndDlg, WM_TASKDIALOGINIT, 0, 0); } break; } From 8d4f8bf819706a228080372b448cafa725bf446f Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 03:36:02 +1000 Subject: [PATCH 010/839] Fix build warnings --- plugins/Updater/updater.c | 14 +++++++------- plugins/Updater/updater.h | 1 - tools/CustomSetupTool/CustomSetupTool/extract.c | 6 +++--- .../CustomSetupTool/include/setup.h | 2 +- tools/CustomSetupTool/CustomSetupTool/page4.c | 2 +- tools/CustomSetupTool/CustomSetupTool/update.c | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 156148a2b59b..51f75f11c329 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -1015,12 +1015,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( switch (uMsg) { - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_SHOWDIALOG: + case WM_INITDIALOG: { if (IsMinimized(hwndDlg)) ShowWindow(hwndDlg, SW_RESTORE); @@ -1030,6 +1025,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( SetForegroundWindow(hwndDlg); } break; + case WM_NCDESTROY: + { + RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); + } + break; case PH_UPDATEAVAILABLE: { ShowAvailableDialog(context); @@ -1201,7 +1201,7 @@ VOID ShowUpdateDialog( PhWaitForEvent(&InitializedEvent, NULL); } - PostMessage(UpdateDialogHandle, WM_SHOWDIALOG, 0, 0); + PostMessage(UpdateDialogHandle, WM_INITDIALOG, 0, 0); } VOID StartInitialCheck( diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index b2032e6f432d..41899d0c5cbf 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -43,7 +43,6 @@ #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") diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 1c600f73a771..f89aa45dbbef 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -50,7 +50,7 @@ PVOID GetZipResourceData( } BOOLEAN SetupExtractBuild( - _In_ PSETUP_PROGRESS_THREAD Context + _In_ HWND Context ) { mz_bool status = MZ_FALSE; @@ -97,7 +97,7 @@ BOOLEAN SetupExtractBuild( InterlockedExchange64(&ExtractTotalLength, totalLength); } - SendMessage(Context->DialogHandle, WM_START_SETUP, 0, 0); + SendMessage(Context, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) { @@ -215,7 +215,7 @@ BOOLEAN SetupExtractBuild( currentLength += bufferLength; InterlockedExchange64(&ExtractCurrentLength, currentLength); - SendMessage(Context->DialogHandle, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); + SendMessage(Context, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); NtClose(fileHandle); mz_free(buffer); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index bfbb5e84a9cf..6bfa3a0ad149 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -176,7 +176,7 @@ VOID SetupUpgradeSettingsFile( // extract.c BOOLEAN SetupExtractBuild( - _In_ PSETUP_PROGRESS_THREAD Context + _In_ HWND Context ); // update.c diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index d8d55b33f949..0fa1f085be1e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -59,7 +59,7 @@ NTSTATUS SetupProgressThread( SetupSetWindowsOptions(); // Setup new installation. - if (!SetupExtractBuild(Context)) + if (!SetupExtractBuild(Context->DialogHandle)) goto CleanupExit; // Install updated kernel driver diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 5b02a4002cd3..01db439aa0e1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -55,7 +55,7 @@ NTSTATUS SetupUpdateBuild( SetupCreateUninstallFile(); //SetupSetWindowsOptions(); - if (!SetupExtractBuild(Context)) + if (!SetupExtractBuild(Context->DialogHandle)) goto CleanupExit; if (SetupInstallKphService) From b90c25f6899fbb0be68a3018d897325d6e8e6e59 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 12:28:52 +1000 Subject: [PATCH 011/839] peview: Improve propsheet layout --- tools/peview/prpsh.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 70f9d0ed53da..14e7557875e1 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -228,26 +228,25 @@ LRESULT CALLBACK PvpPropSheetWndProc( } BOOLEAN PhpInitializePropSheetLayoutStage1( + _In_ PPV_PROPSHEETCONTEXT PropSheetContext, _In_ HWND hwnd ) { - PPV_PROPSHEETCONTEXT propSheetContext = PvpGetPropSheetContext(hwnd); - - if (!propSheetContext->LayoutInitialized) + if (!Context->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. @@ -255,7 +254,7 @@ BOOLEAN PhpInitializePropSheetLayoutStage1( // Set the Cancel button's text to "Close". SetDlgItemText(hwnd, IDCANCEL, L"Close"); - propSheetContext->LayoutInitialized = TRUE; + PropSheetContext->LayoutInitialized = TRUE; return TRUE; } @@ -385,7 +384,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; From 3161bba67e0c99a5e83ef9cfd079ef6c002ecbd8 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 15:42:21 +1000 Subject: [PATCH 012/839] Fix token properties highlighting, Update handles tab layout, Fix peview typo --- ProcessHacker/ProcessHacker.rc | 14 ++--- ProcessHacker/ProcessHacker.vcxproj | 1 + ProcessHacker/ProcessHacker.vcxproj.filters | 3 + ProcessHacker/hndllist.c | 5 +- ProcessHacker/include/appsup.h | 67 -------------------- ProcessHacker/include/splitter.h | 70 +++++++++++++++++++++ ProcessHacker/splitter.c | 42 +++++-------- ProcessHacker/tokprp.c | 52 +++++++-------- tools/peview/prpsh.c | 2 +- 9 files changed, 122 insertions(+), 134 deletions(-) create mode 100644 ProcessHacker/include/splitter.h diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index db1968f60b85..74667f423a66 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -601,9 +601,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 - EDITTEXT IDC_HANDLESEARCH,110,4,143,14,ES_AUTOHSCROLL + CONTROL "Hide unnamed handles",IDC_HIDEUNNAMEDHANDLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,5,88,10 + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,256,239,WS_EX_CLIENTEDGE + EDITTEXT IDC_HANDLESEARCH,114,3,144,14,ES_AUTOHSCROLL END IDD_PROCENVIRONMENT DIALOGEX 0, 0, 260, 260 @@ -1956,10 +1956,10 @@ BEGIN IDD_PROCHANDLES, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 END IDD_PROCENVIRONMENT, DIALOG diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index c2e557e676dc..dc1045b9f277 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -385,6 +385,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index fbed89f82328..0af73f22ef55 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -584,6 +584,9 @@ Headers + + Headers + diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index c34947dd9502..110316c924ed 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -3,6 +3,7 @@ * handle list * * Copyright (C) 2011-2013 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -95,12 +96,12 @@ VOID PhInitializeHandleList( // 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_GRANTEDACCESSSYMBOLIC, TRUE, L"Granted access (symbolic)", 140, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLE, FALSE, L"Handle", 80, PH_ALIGN_LEFT, -1, 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); diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 27912c57b459..e631485c30b9 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -499,71 +499,4 @@ FORCEINLINE PVOID PhpGenericPropertyPageHeader( #define SWP_SHOWWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_SHOWWINDOW) #define SWP_HIDEWINDOW_ONLY (SWP_NO_ACTIVATE_MOVE_SIZE_ZORDER | SWP_HIDEWINDOW) -// splitter - -typedef struct _PH_HSPLITTER_CONTEXT -{ - union - { - ULONG Flags; - struct - { - ULONG Hot : 1; - ULONG Pushed : 1; - ULONG Moved : 1; - ULONG DragMode : 1; - ULONG Spare : 28; - }; - }; - - ULONG Height; - PH_LAYOUT_MANAGER LayoutManager; - PPH_LAYOUT_ITEM Topitem; - PPH_LAYOUT_ITEM Bottomitem; -} PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; - -PPH_HSPLITTER_CONTEXT PhInitializeHSplitterSupport( - _In_ HWND Parent, - _In_ HWND TopChild, - _In_ HWND BottomChild - ); - -VOID PhDeleteHSplitterSupportSupport( - _Inout_ PPH_HSPLITTER_CONTEXT Context - ); - -VOID PhHSplitterHandleWmSize( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ INT Width, - _In_ INT Height - ); - -VOID PhHSplitterHandleLButtonDown( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleLButtonUp( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleMouseMove( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleMouseLeave( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - #endif diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h new file mode 100644 index 000000000000..50b9c1fb7bf4 --- /dev/null +++ b/ProcessHacker/include/splitter.h @@ -0,0 +1,70 @@ +#ifndef PH_HSPLITTER_H +#define PH_HSPLITTER_H + +typedef struct _PH_HSPLITTER_CONTEXT +{ + union + { + ULONG Flags; + struct + { + ULONG Hot : 1; + ULONG Pushed : 1; + ULONG Moved : 1; + ULONG DragMode : 1; + ULONG Spare : 28; + }; + }; + + LONG SplitterOffset; + LONG SplitterPosition; + PH_LAYOUT_MANAGER LayoutManager; + PPH_LAYOUT_ITEM Topitem; + PPH_LAYOUT_ITEM Bottomitem; +} PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; + +PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( + _In_ HWND Parent, + _In_ HWND TopChild, + _In_ HWND BottomChild + ); + +VOID PhDeleteHSplitter( + _Inout_ PPH_HSPLITTER_CONTEXT Context + ); + +VOID PhHSplitterHandleWmSize( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhHSplitterHandleLButtonDown( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleLButtonUp( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleMouseMove( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhHSplitterHandleMouseLeave( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ HWND hwnd, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#endif diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 23682b89c28d..4f0352f7e1aa 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -22,16 +22,16 @@ */ #include +#include #include #include static INT nSplitterPos = 250; -static INT nSplitterBorder = 6; static INT SplitterOffset = -4; VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height); -PPH_HSPLITTER_CONTEXT PhInitializeHSplitterSupport( +PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( _In_ HWND Parent, _In_ HWND TopChild, _In_ HWND BottomChild @@ -43,14 +43,13 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitterSupport( memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); PhInitializeLayoutManager(&context->LayoutManager, Parent); - context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); return context; } -VOID PhDeleteHSplitterSupportSupport( +VOID PhDeleteHSplitter( _Inout_ PPH_HSPLITTER_CONTEXT Context ) { @@ -64,12 +63,11 @@ VOID PhHSplitterHandleWmSize( ) { // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. - +#define SPLITTER_PADDING 6 // Set the bottom margin of the top control. - Context->Topitem->Margin.bottom = Height - nSplitterPos - nSplitterBorder; - + Context->Topitem->Margin.bottom = Height - nSplitterPos - SPLITTER_PADDING; // Set the top margin of the bottom control. - Context->Bottomitem->Margin.top = nSplitterPos + nSplitterBorder * 2; + Context->Bottomitem->Margin.top = nSplitterPos + SPLITTER_PADDING * 2; PhLayoutManagerLayout(&Context->LayoutManager); } @@ -209,21 +207,16 @@ VOID PhHSplitterHandleMouseMove( } else { - //if (!Context->Hot) - { - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; + TRACKMOUSEEVENT trackMouseEvent; - Context->Hot = TRUE; + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; - SetCursor(LoadCursor(NULL, IDC_SIZENS)); + SetCursor(LoadCursor(NULL, IDC_SIZENS)); - TrackMouseEvent(&trackMouseEvent); - } + TrackMouseEvent(&trackMouseEvent); } ReleaseDC(hwnd, hdc); @@ -238,13 +231,8 @@ VOID PhHSplitterHandleMouseLeave( _In_ LPARAM lParam ) { - if (Context->Hot) - { - Context->Hot = FALSE; - - // Reset the original cursor. - SetCursor(LoadCursor(NULL, IDC_ARROW)); - } + // Reset the original cursor. + SetCursor(LoadCursor(NULL, IDC_ARROW)); } // http://www.catch22.net/tuts/splitter-windows diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 034d6c65d1bb..27263e020fac 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -3,6 +3,7 @@ * token properties * * Copyright (C) 2010-2012 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -21,13 +22,13 @@ */ #include - -#include - #include #include #include #include +#include + +#include typedef struct _ATTRIBUTE_NODE { @@ -424,7 +425,7 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSetControlTheme(groupsLv, L"explorer"); PhSetControlTheme(privilegesLv, L"explorer"); - tokenPageContext->HSplitterContext = PhInitializeHSplitterSupport( + tokenPageContext->HSplitterContext = PhInitializeHSplitter( hwndDlg, tokenPageContext->GroupsListViewHandle, tokenPageContext->PrivilegesListViewHandle @@ -582,19 +583,6 @@ INT_PTR CALLBACK PhpTokenPageProc( ExtendedListView_SortItems(privilegesLv); } - 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); - } - NtClose(tokenHandle); } } @@ -603,7 +591,7 @@ INT_PTR CALLBACK PhpTokenPageProc( { if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); - if (tokenPageContext->HSplitterContext) PhDeleteHSplitterSupportSupport(tokenPageContext->HSplitterContext); + if (tokenPageContext->HSplitterContext) PhDeleteHSplitter(tokenPageContext->HSplitterContext); } break; case WM_COMMAND: @@ -902,6 +890,19 @@ INT_PTR CALLBACK PhpTokenPageProc( { case PSN_QUERYINITIALFOCUS: { + if (ListView_GetItemCount(tokenPageContext->GroupsListViewHandle) != 0) + { + ListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 0, LVSCW_AUTOSIZE); + ExtendedListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + + if (ListView_GetItemCount(tokenPageContext->PrivilegesListViewHandle) != 0) + { + ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 0, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 1, LVSCW_AUTOSIZE); + ExtendedListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 2, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_SESSIONID)); return TRUE; } @@ -939,24 +940,15 @@ INT_PTR CALLBACK PhpTokenPageProc( break; case WM_SIZE: PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); - return 0; + break; case WM_LBUTTONDOWN: PhHSplitterHandleLButtonDown(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - return 0; + break; case WM_LBUTTONUP: PhHSplitterHandleLButtonUp(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - return 0; + break; case WM_MOUSEMOVE: PhHSplitterHandleMouseMove(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - return 0; - case WM_NCMOUSELEAVE: - { - //if (context->Hot) - { - //context->Hot = FALSE; - //RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); - } - } break; } diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 14e7557875e1..ab84021aa15b 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -232,7 +232,7 @@ BOOLEAN PhpInitializePropSheetLayoutStage1( _In_ HWND hwnd ) { - if (!Context->LayoutInitialized) + if (!PropSheetContext->LayoutInitialized) { HWND tabControlHandle; PPH_LAYOUT_ITEM tabControlItem; From 10d4224ca301571beeaa49035037cb0d92bef979 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 15:48:45 +1000 Subject: [PATCH 013/839] NetworkTools: Fix layout --- plugins/NetworkTools/main.c | 2 +- plugins/NetworkTools/ping.c | 7 +++++-- plugins/NetworkTools/tracert.c | 6 +++++- plugins/NetworkTools/whois.c | 9 ++++++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 8134d8ed4f8c..9dfaf8fc6fc9 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -645,7 +645,7 @@ LOGICAL DllMain( { 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" }, + { ScalableIntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_SIZE, L"@96|850,490" }, { StringSettingType, SETTING_NAME_TRACERT_LIST_COLUMNS, L"" }, { StringSettingType, SETTING_NAME_TRACERT_HISTORY, L"" }, { IntegerSettingType, SETTING_NAME_TRACERT_MAX_HOPS, L"30" }, diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index a971a9c83a86..eb9224c79872 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -290,7 +290,6 @@ INT_PTR CALLBACK NetworkPingWndProc( // 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); @@ -326,7 +325,11 @@ INT_PTR CALLBACK NetworkPingWndProc( 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); + + 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); SetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer); SetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with %lu bytes of data...", diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index b1c63a06a26f..0484e9bc1055 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -588,7 +588,11 @@ INT_PTR CALLBACK TracertDlgProc( 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); + + 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); PhReferenceObject(context); diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 4755af27061f..4a2b968ffe26 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -397,7 +397,6 @@ INT_PTR CALLBACK NetworkOutputDlgProc( HANDLE dialogThread; SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); - PhCenterWindow(hwndDlg, PhMainWndHandle); context->WindowHandle = hwndDlg; context->RichEditHandle = GetDlgItem(hwndDlg, IDC_NETOUTPUTEDIT); @@ -412,8 +411,12 @@ INT_PTR CALLBACK NetworkOutputDlgProc( context->FontHandle = PhCreateCommonFont(-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); + 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); if (dialogThread = PhCreateThread(0, NetworkWhoisThreadStart, (PVOID)context)) NtClose(dialogThread); From 9766f20c37e9ac2db2e2b6173adb54fb5d54f945 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 16:16:58 +1000 Subject: [PATCH 014/839] Fix token properties splitter min/max resize --- ProcessHacker/splitter.c | 81 +++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 4f0352f7e1aa..01532f810794 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -26,6 +26,8 @@ #include #include +#define SPLITTER_PADDING 6 +#define SPLITTER_MIN_HEIGHT 200 static INT nSplitterPos = 250; static INT SplitterOffset = -4; @@ -63,7 +65,8 @@ VOID PhHSplitterHandleWmSize( ) { // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. -#define SPLITTER_PADDING 6 + // TODO: Check min_height and adjust splitter position if invisible. + // Set the bottom margin of the top control. Context->Topitem->Margin.bottom = Height - nSplitterPos - SPLITTER_PADDING; // Set the top margin of the bottom control. @@ -95,10 +98,10 @@ VOID PhHSplitterHandleLButtonDown( // Adjust the coordinates (start from 0,0). OffsetRect(&rect, -rect.left, -rect.top); - if (pt.y < 0) - pt.y = 0; - if (pt.y > rect.bottom - 4) - pt.y = rect.bottom - 4; + if (pt.y < Context->Topitem->OrigRect.top * 2) + pt.y = Context->Topitem->OrigRect.top * 2; + if (pt.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) + pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; Context->DragMode = TRUE; @@ -137,10 +140,10 @@ VOID PhHSplitterHandleLButtonUp( // Adjust the coordinates (start from 0,0). OffsetRect(&rect, -rect.left, -rect.top); - if (pt.y < 0) - pt.y = 0; - if (pt.y > rect.bottom - 4) - pt.y = rect.bottom - 4; + if (pt.y < Context->Topitem->OrigRect.top * 2) + pt.y = Context->Topitem->OrigRect.top * 2; + if (pt.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) + pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; hdc = GetWindowDC(hwnd); DrawXorBar(hdc, 1, SplitterOffset - 2, rect.right - 2, 4); @@ -177,50 +180,50 @@ VOID PhHSplitterHandleMouseMove( RECT windowRect; POINT windowPoint; - //if (Context->DragMode == FALSE) - // return; - windowPoint.x = GET_X_LPARAM(lParam); windowPoint.y = GET_Y_LPARAM(lParam); GetWindowRect(hwnd, &windowRect); ClientToScreen(hwnd, &windowPoint); - windowPoint.x -= windowRect.left; - windowPoint.y -= windowRect.top; - - OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + if (Context->DragMode) + { + windowPoint.x -= windowRect.left; + windowPoint.y -= windowRect.top; - if (windowPoint.y < 0) - windowPoint.y = 0; - if (windowPoint.y > windowRect.bottom - 4) - windowPoint.y = windowRect.bottom - 4; + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); - if (windowPoint.y != SplitterOffset) - { - hdc = GetWindowDC(hwnd); + if (windowPoint.y < Context->Topitem->OrigRect.top * 2) + windowPoint.y = Context->Topitem->OrigRect.top * 2; + if (windowPoint.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) + windowPoint.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; - if (wParam & MK_LBUTTON) + if (windowPoint.y != SplitterOffset) { - DrawXorBar(hdc, 1, SplitterOffset - 2, windowRect.right - 2, 4); - DrawXorBar(hdc, 1, windowPoint.y - 2, windowRect.right - 2, 4); - } - else - { - TRACKMOUSEEVENT trackMouseEvent; + hdc = GetWindowDC(hwnd); - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; + if (wParam & MK_LBUTTON) + { + DrawXorBar(hdc, 1, SplitterOffset - 2, windowRect.right - 2, 4); + DrawXorBar(hdc, 1, windowPoint.y - 2, windowRect.right - 2, 4); + } + else + { + TRACKMOUSEEVENT trackMouseEvent; - SetCursor(LoadCursor(NULL, IDC_SIZENS)); + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; - TrackMouseEvent(&trackMouseEvent); - } + SetCursor(LoadCursor(NULL, IDC_SIZENS)); - ReleaseDC(hwnd, hdc); - SplitterOffset = windowPoint.y; + TrackMouseEvent(&trackMouseEvent); + } + + ReleaseDC(hwnd, hdc); + SplitterOffset = windowPoint.y; + } } } From 2c3dfa9cc2d18535a0813de1b845905a7abb89d2 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 16:40:10 +1000 Subject: [PATCH 015/839] Update splitter control --- ProcessHacker/splitter.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 01532f810794..55c28b97785a 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -28,8 +28,6 @@ #define SPLITTER_PADDING 6 #define SPLITTER_MIN_HEIGHT 200 -static INT nSplitterPos = 250; -static INT SplitterOffset = -4; VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height); @@ -45,6 +43,9 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); PhInitializeLayoutManager(&context->LayoutManager, Parent); + + context->SplitterOffset = -4; + context->SplitterPosition = 250; context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); @@ -65,12 +66,12 @@ VOID PhHSplitterHandleWmSize( ) { // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. - // TODO: Check min_height and adjust splitter position if invisible. + // TODO: Check min_height and adjust second control if invisible. // Set the bottom margin of the top control. - Context->Topitem->Margin.bottom = Height - nSplitterPos - SPLITTER_PADDING; + Context->Topitem->Margin.bottom = Height - Context->SplitterPosition - SPLITTER_PADDING; // Set the top margin of the bottom control. - Context->Bottomitem->Margin.top = nSplitterPos + SPLITTER_PADDING * 2; + Context->Bottomitem->Margin.top = Context->SplitterPosition + SPLITTER_PADDING * 2; PhLayoutManagerLayout(&Context->LayoutManager); } @@ -104,14 +105,13 @@ VOID PhHSplitterHandleLButtonDown( pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; Context->DragMode = TRUE; - SetCapture(hwnd); hdc = GetWindowDC(hwnd); DrawXorBar(hdc, 1, pt.y - 2, rect.right - 2, 4); ReleaseDC(hwnd, hdc); - SplitterOffset = pt.y; + Context->SplitterOffset = pt.y; } VOID PhHSplitterHandleLButtonUp( @@ -128,7 +128,7 @@ VOID PhHSplitterHandleLButtonUp( pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); - if (Context->DragMode == FALSE) + if (!Context->DragMode) return; GetWindowRect(hwnd, &rect); @@ -146,22 +146,20 @@ VOID PhHSplitterHandleLButtonUp( pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; hdc = GetWindowDC(hwnd); - DrawXorBar(hdc, 1, SplitterOffset - 2, rect.right - 2, 4); + DrawXorBar(hdc, 1, Context->SplitterOffset - 2, rect.right - 2, 4); ReleaseDC(hwnd, hdc); - SplitterOffset = pt.y; + Context->SplitterOffset = pt.y; Context->DragMode = FALSE; GetWindowRect(hwnd, &rect); - pt.x += rect.left; pt.y += rect.top; - ScreenToClient(hwnd, &pt); GetClientRect(hwnd, &rect); - nSplitterPos = pt.y; + Context->SplitterPosition = pt.y; // position the child controls PhHSplitterHandleWmSize(Context, rect.right, rect.bottom); @@ -187,7 +185,7 @@ VOID PhHSplitterHandleMouseMove( ClientToScreen(hwnd, &windowPoint); if (Context->DragMode) - { + { windowPoint.x -= windowRect.left; windowPoint.y -= windowRect.top; @@ -198,13 +196,13 @@ VOID PhHSplitterHandleMouseMove( if (windowPoint.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) windowPoint.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; - if (windowPoint.y != SplitterOffset) + if (windowPoint.y != Context->SplitterOffset) { hdc = GetWindowDC(hwnd); if (wParam & MK_LBUTTON) { - DrawXorBar(hdc, 1, SplitterOffset - 2, windowRect.right - 2, 4); + DrawXorBar(hdc, 1, Context->SplitterOffset - 2, windowRect.right - 2, 4); DrawXorBar(hdc, 1, windowPoint.y - 2, windowRect.right - 2, 4); } else @@ -222,7 +220,7 @@ VOID PhHSplitterHandleMouseMove( } ReleaseDC(hwnd, hdc); - SplitterOffset = windowPoint.y; + Context->SplitterOffset = windowPoint.y; } } } From f98ce0ad557add098347de553475571860211f60 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 17:24:40 +1000 Subject: [PATCH 016/839] CustomSetupTool: Add settings file upgrade, Fix uinstall cleanup --- .../CustomSetupTool/CustomSetupTool/appsup.c | 2 +- .../CustomSetupTool/include/setup.h | 4 + tools/CustomSetupTool/CustomSetupTool/page4.c | 1 + tools/CustomSetupTool/CustomSetupTool/setup.c | 81 +++++++++++++++++-- .../CustomSetupTool/uninstall.c | 3 + 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 0de46b959b1b..fa09d200d337 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -630,7 +630,7 @@ BOOLEAN RemoveDirectoryPath(_In_ PWSTR DirPath) FindClose(findHandle); // Delete the parent directory - SetupDeleteDirectoryFile(DirPath); + RemoveDirectory(DirPath); PhDereferenceObject(findPath); return TRUE; diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 6bfa3a0ad149..bca1fad59bc9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -165,6 +165,10 @@ VOID SetupCreateUninstallFile( VOID ); +VOID SetupDeleteUninstallFile( + VOID + ); + BOOLEAN SetupExecuteProcessHacker( _In_ HWND Parent ); diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index 0fa1f085be1e..236086b28f38 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -45,6 +45,7 @@ NTSTATUS SetupProgressThread( if (!CreateDirectoryPath(PhGetString(SetupInstallPath))) goto CleanupExit; + // Upgrade the 2.x settings file. SetupUpgradeSettingsFile(); // Remove the previous installation. diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index d32e501f5282..9309fa223e2f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -190,6 +190,77 @@ VOID SetupCreateUninstallFile( PhDereferenceObject(uninstallFilePath); } +VOID SetupDeleteUninstallFile( + VOID + ) +{ + PPH_STRING uninstallFilePath; + + // NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer + uninstallFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.exe"); + + if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) + { + ULONG indexOfFileName = -1; + GUID randomGuid; + PPH_STRING setupTempPath = NULL; + PPH_STRING randomGuidString = NULL; + PPH_STRING fullSetupPath = NULL; + PPH_STRING tempFilePath = NULL; + + 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-setup.exe + // Example: C:\\Users\\dmex\\AppData\\Temp\\processhacker-setup.exe + tempFilePath = PhFormatString( + L"%s%s\\processhacker-setup.exe", + PhGetStringOrEmpty(setupTempPath), + PhGetStringOrEmpty(randomGuidString) + ); + if (PhIsNullOrEmptyString(tempFilePath)) + goto CleanupExit; + + // Create the directory if it does not exist. + if (fullSetupPath = PhGetFullPath(PhGetString(tempFilePath), &indexOfFileName)) + { + PPH_STRING directoryPath; + + if (indexOfFileName == -1) + goto CleanupExit; + + if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) + { + SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); + PhDereferenceObject(directoryPath); + } + } + + MoveFile(uninstallFilePath->Buffer, fullSetupPath->Buffer); + } + +CleanupExit: + + PhDereferenceObject(uninstallFilePath); +} + VOID SetupInstallKph( VOID ) @@ -485,11 +556,11 @@ VOID SetupUpgradeSettingsFile( settingsFilePath = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); oldSettingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\settings.xml"); - if (!RtlDoesFileExists_U(settingsFilePath->Buffer)) - { - CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); - } - + if (RtlDoesFileExists_U(settingsFilePath->Buffer)) + SetupDeleteDirectoryFile(settingsFilePath->Buffer); + + CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); + PhDereferenceObject(oldSettingsFileName); PhDereferenceObject(settingsFilePath); } \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index f2d5124f120a..da0493de2d9b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -66,6 +66,9 @@ NTSTATUS SetupUninstallBuild( // Remove autorun and shortcuts. SetupDeleteWindowsOptions(); + // Remove the uninstaller. + SetupDeleteUninstallFile(); + // Remove the previous installation. if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) goto CleanupExit; From 6d4dd79b303000900e10958f1792f6b21dfe5e0d Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Apr 2017 18:17:06 +1000 Subject: [PATCH 017/839] peview: Improve flags layout --- tools/peview/peview.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 6f48fb7e7d01..28530525caa9 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -133,7 +133,7 @@ BEGIN 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 + 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 From 178cd0ed539a1ae385cd5e717277743d08aa7e0e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 13:19:38 +1000 Subject: [PATCH 018/839] peview: Fix file encoding, Fix long library name text clipping --- tools/peview/peprp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 7228b6a4ed84..22512aeac285 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -572,6 +572,8 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( 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), From 10ba1babb7b28050170207a4ef3ca47edec412af Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 13:54:50 +1000 Subject: [PATCH 019/839] Update git config --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 7d0255299ac0..b895d0391026 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files -* text=auto +* text=crlf # Custom for Visual Studio *.cs diff=csharp From d497e61800dcb36a34208d8e3bd72ce556c84afa Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 13:56:14 +1000 Subject: [PATCH 020/839] DotNetTools: Autosize appdomains column --- plugins/DotNetTools/perfpage.c | 1907 ++++++++++++++++---------------- 1 file changed, 958 insertions(+), 949 deletions(-) diff --git a/plugins/DotNetTools/perfpage.c b/plugins/DotNetTools/perfpage.c index a86c0a565b99..c5cf54c72bcf 100644 --- a/plugins/DotNetTools/perfpage.c +++ b/plugins/DotNetTools/perfpage.c @@ -1,950 +1,959 @@ -/* - * 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) - ); +/* + * 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"); + PhSetExtendedListView(context->AppDomainsLv); + + 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"); + PhSetExtendedListView(context->CountersLv); + 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); + } + + ExtendedListView_SetColumnWidth(context->AppDomainsLv, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + 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; + case WM_SIZE: + { + ExtendedListView_SetColumnWidth(context->AppDomainsLv, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + 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 From 1e841be0d6756f0bb632dbb268017b0a9f84e7bb Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 17:41:08 +1000 Subject: [PATCH 021/839] phlib: Fix delay load structure type and checks --- phlib/include/mapimg.h | 4 +- phlib/mapimg.c | 2804 ++++++++++++++++++++-------------------- 2 files changed, 1403 insertions(+), 1405 deletions(-) diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index dca55b9b2274..306704dfb44c 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -203,7 +203,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,7 +217,7 @@ typedef struct _PH_MAPPED_IMAGE_IMPORT_DLL union { PIMAGE_IMPORT_DESCRIPTOR Descriptor; - PVOID DelayDescriptor; + PIMAGE_DELAYLOAD_DESCRIPTOR DelayDescriptor; }; PVOID *LookupTable; } PH_MAPPED_IMAGE_IMPORT_DLL, *PPH_MAPPED_IMAGE_IMPORT_DLL; diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 05d179f6bcf4..4366e633d1df 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1,1404 +1,1402 @@ -/* - * 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; +/* + * 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 + +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 = &((PIMAGE_DELAYLOAD_DESCRIPTOR)Imports->DelayDescriptorTable)[Index]; + + ImportDll->Name = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ((PIMAGE_DELAYLOAD_DESCRIPTOR)ImportDll->DelayDescriptor)->DllNameRVA, + NULL + ); + + if (!ImportDll->Name) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + + ImportDll->LookupTable = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ((PIMAGE_DELAYLOAD_DESCRIPTOR)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) + { + 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; + 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 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 From f2b0ce19c67860a0a623a8faf406a3f376567055 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 19:12:40 +1000 Subject: [PATCH 022/839] Fix github file encoding issues --- CHANGELOG.txt | 1336 +- COPYRIGHT.txt | 264 +- KProcessHacker/KProcessHacker.vcxproj | 276 +- KProcessHacker/devctrl.c | 1132 +- KProcessHacker/dyndata.c | 334 +- KProcessHacker/dynimp.c | 120 +- KProcessHacker/include/dyndata.h | 94 +- KProcessHacker/include/kph.h | 730 +- KProcessHacker/include/ntfill.h | 702 +- KProcessHacker/main.c | 712 +- KProcessHacker/object.c | 2586 +-- KProcessHacker/process.c | 1140 +- KProcessHacker/qrydrv.c | 476 +- KProcessHacker/resource.rc | 106 +- KProcessHacker/sign.cmd | 14 +- KProcessHacker/thread.c | 1428 +- KProcessHacker/vm.c | 898 +- LICENSE.txt | 1370 +- ProcessHacker/ProcessHacker.manifest | 96 +- ProcessHacker/ProcessHacker.rc | 5264 ++--- ProcessHacker/ProcessHacker.vcxproj | 906 +- ProcessHacker/about.c | 380 +- ProcessHacker/actions.c | 6260 +++--- ProcessHacker/affinity.c | 634 +- ProcessHacker/anawait.c | 2152 +- ProcessHacker/appsup.c | 4414 ++-- ProcessHacker/chcol.c | 850 +- ProcessHacker/chdlg.c | 720 +- ProcessHacker/chproc.c | 632 +- ProcessHacker/cmdmode.c | 904 +- ProcessHacker/colmgr.c | 1420 +- ProcessHacker/dbgcon.c | 3390 +-- ProcessHacker/extmgr.c | 452 +- ProcessHacker/findobj.c | 2036 +- ProcessHacker/gdihndl.c | 764 +- ProcessHacker/hidnproc.c | 2498 +-- ProcessHacker/hndllist.c | 1498 +- ProcessHacker/hndlprp.c | 672 +- ProcessHacker/hndlprv.c | 1440 +- ProcessHacker/hndlstat.c | 480 +- ProcessHacker/include/colmgr.h | 236 +- ProcessHacker/include/extmgr.h | 102 +- ProcessHacker/include/extmgri.h | 122 +- ProcessHacker/include/heapstruct.h | 138 +- ProcessHacker/include/hidnproc.h | 150 +- ProcessHacker/include/mainwndp.h | 1024 +- ProcessHacker/include/memsrch.h | 122 +- ProcessHacker/include/miniinfo.h | 442 +- ProcessHacker/include/miniinfop.h | 808 +- ProcessHacker/include/notifico.h | 342 +- ProcessHacker/include/phapp.h | 1506 +- ProcessHacker/include/phappres.h | 68 +- ProcessHacker/include/phapprev_in.h | 12 +- ProcessHacker/include/phplug.h | 1350 +- ProcessHacker/include/phsvc.h | 476 +- ProcessHacker/include/phsvcapi.h | 620 +- ProcessHacker/include/phsvccl.h | 310 +- ProcessHacker/include/procgrp.h | 62 +- ProcessHacker/include/procprpp.h | 692 +- ProcessHacker/include/settings.h | 458 +- ProcessHacker/include/settingsp.h | 92 +- ProcessHacker/include/sysinfo.h | 326 +- ProcessHacker/include/sysinfop.h | 878 +- ProcessHacker/infodlg.c | 430 +- ProcessHacker/itemtips.c | 1408 +- ProcessHacker/jobprp.c | 1320 +- ProcessHacker/log.c | 480 +- ProcessHacker/logwnd.c | 712 +- ProcessHacker/main.c | 2144 +- ProcessHacker/mainwnd.c | 7282 +++---- ProcessHacker/mdump.c | 932 +- ProcessHacker/memedit.c | 1072 +- ProcessHacker/memlist.c | 1836 +- ProcessHacker/memlists.c | 576 +- ProcessHacker/memprot.c | 324 +- ProcessHacker/memprv.c | 1620 +- ProcessHacker/memrslt.c | 1244 +- ProcessHacker/memsrch.c | 1462 +- ProcessHacker/miniinfo.c | 4546 ++-- ProcessHacker/modlist.c | 2024 +- ProcessHacker/modprv.c | 1178 +- ProcessHacker/mxml/mxml.h | 8 +- ProcessHacker/netlist.c | 1594 +- ProcessHacker/netprv.c | 1992 +- ProcessHacker/netstk.c | 480 +- ProcessHacker/notifico.c | 2690 +-- ProcessHacker/ntobjprp.c | 1514 +- ProcessHacker/options.c | 2530 +-- ProcessHacker/pagfiles.c | 318 +- ProcessHacker/pcre/pcre2_compile.c | 18172 ++++++++-------- ProcessHacker/pcre/pcre2_match.c | 4 +- ProcessHacker/pcre/pcre2posix.c | 4 +- ProcessHacker/phsvc/clapi.c | 2434 +-- ProcessHacker/phsvc/svcapi.c | 2892 +-- ProcessHacker/phsvc/svcapiport.c | 636 +- ProcessHacker/phsvc/svcclient.c | 318 +- ProcessHacker/phsvc/svcmain.c | 222 +- ProcessHacker/plugin.c | 2086 +- ProcessHacker/plugman.c | 890 +- ProcessHacker/procgrp.c | 686 +- ProcessHacker/procprp.c | 1448 +- ProcessHacker/procprv.c | 5958 ++--- ProcessHacker/procrec.c | 524 +- ProcessHacker/proctree.c | 7016 +++--- ProcessHacker/resource.h | 1502 +- ProcessHacker/runas.c | 2334 +- ProcessHacker/sdk/phdk.h | 52 +- ProcessHacker/sdk/readme.txt | 74 +- ProcessHacker/sessmsg.c | 320 +- ProcessHacker/sessprp.c | 474 +- ProcessHacker/sessshad.c | 478 +- ProcessHacker/settings.c | 2396 +- ProcessHacker/srvcr.c | 452 +- ProcessHacker/srvctl.c | 824 +- ProcessHacker/srvlist.c | 1808 +- ProcessHacker/srvprp.c | 1148 +- ProcessHacker/srvprv.c | 2082 +- ProcessHacker/sysinfo.c | 4108 ++-- ProcessHacker/thrdlist.c | 1446 +- ProcessHacker/thrdprv.c | 2230 +- ProcessHacker/thrdstk.c | 1394 +- ProcessHacker/tokprp.c | 3936 ++-- ProcessHacker/version.rc | 70 +- phlib/apiimport.c | 136 +- phlib/basesup.c | 11970 +++++----- phlib/circbuf.c | 44 +- phlib/circbuf_i.h | 242 +- phlib/colorbox.c | 460 +- phlib/cpysave.c | 1148 +- phlib/data.c | 438 +- phlib/dspick.c | 502 +- phlib/emenu.c | 1714 +- phlib/error.c | 184 +- phlib/extlv.c | 1468 +- phlib/fastlock.c | 768 +- phlib/filepool.c | 3022 +-- phlib/format.c | 554 +- phlib/format_i.h | 1136 +- phlib/global.c | 478 +- phlib/graph.c | 2670 +-- phlib/guisup.c | 2748 +-- phlib/handle.c | 2194 +- phlib/hexedit.c | 3736 ++-- phlib/hndlinfo.c | 3552 +-- phlib/icotobmp.c | 384 +- phlib/include/apiimport.h | 172 +- phlib/include/circbuf.h | 52 +- phlib/include/circbuf_h.h | 280 +- phlib/include/colorbox.h | 60 +- phlib/include/cpysave.h | 156 +- phlib/include/dltmgr.h | 70 +- phlib/include/dspick.h | 96 +- phlib/include/emenu.h | 438 +- phlib/include/fastlock.h | 186 +- phlib/include/filepool.h | 392 +- phlib/include/filepoolp.h | 408 +- phlib/include/graph.h | 510 +- phlib/include/guisupp.h | 86 +- phlib/include/handlep.h | 294 +- phlib/include/hexedit.h | 98 +- phlib/include/hexeditp.h | 402 +- phlib/include/kphapi.h | 480 +- phlib/include/kphuser.h | 600 +- phlib/include/ph.h | 22 +- phlib/include/phbase.h | 72 +- phlib/include/phintrnl.h | 98 +- phlib/include/phnet.h | 278 +- phlib/include/phsup.h | 1092 +- phlib/include/queuedlock.h | 698 +- phlib/include/ref.h | 618 +- phlib/include/refp.h | 350 +- phlib/include/secedit.h | 328 +- phlib/include/seceditp.h | 186 +- phlib/include/symprv.h | 606 +- phlib/include/symprvp.h | 338 +- phlib/include/templ.h | 14 +- phlib/include/treenew.h | 1344 +- phlib/include/treenewp.h | 1598 +- phlib/include/verify.h | 154 +- phlib/include/verifyp.h | 236 +- phlib/kph.c | 2204 +- phlib/kphdata.c | 658 +- phlib/maplib.c | 804 +- phlib/md5.c | 450 +- phlib/native.c | 12924 +++++------ phlib/phlib.vcxproj | 496 +- phlib/provider.c | 940 +- phlib/queuedlock.c | 2404 +- phlib/ref.c | 1508 +- phlib/secdata.c | 1576 +- phlib/secedit.c | 1154 +- phlib/sha.c | 344 +- phlib/svcsup.c | 1108 +- phlib/symprv.c | 3520 +-- phlib/sync.c | 992 +- phlib/treenew.c | 13290 +++++------ phlib/verify.c | 1342 +- phlib/workqueue.c | 1012 +- plugins/DotNetTools/CHANGELOG.txt | 50 +- plugins/DotNetTools/DotNetTools.rc | 364 +- plugins/DotNetTools/DotNetTools.vcxproj | 206 +- plugins/DotNetTools/asmpage.c | 2628 +-- plugins/DotNetTools/clr/dbgappdomain.h | 276 +- plugins/DotNetTools/clr/ipcenums.h | 152 +- plugins/DotNetTools/clr/ipcheader.h | 1088 +- plugins/DotNetTools/clr/ipcshared.h | 112 +- plugins/DotNetTools/clr/perfcounterdefs.h | 664 +- plugins/DotNetTools/clretw.h | 292 +- plugins/DotNetTools/clrsup.c | 1150 +- plugins/DotNetTools/clrsup.h | 1290 +- plugins/DotNetTools/counters.c | 1858 +- plugins/DotNetTools/dn.h | 288 +- plugins/DotNetTools/main.c | 670 +- plugins/DotNetTools/resource.h | 52 +- plugins/DotNetTools/stackext.c | 642 +- plugins/DotNetTools/svcext.c | 418 +- plugins/DotNetTools/svcext.h | 172 +- plugins/DotNetTools/treeext.c | 592 +- plugins/ExtendedNotifications/CHANGELOG.txt | 22 +- .../ExtendedNotifications.rc | 428 +- .../ExtendedNotifications.vcxproj | 196 +- plugins/ExtendedNotifications/extnoti.h | 60 +- plugins/ExtendedNotifications/filelog.c | 158 +- .../gntp-send/LICENSE.txt | 50 +- .../ExtendedNotifications/gntp-send/growl.c | 1074 +- .../ExtendedNotifications/gntp-send/growl.h | 66 +- plugins/ExtendedNotifications/gntp-send/md5.h | 52 +- plugins/ExtendedNotifications/gntp-send/tcp.c | 370 +- plugins/ExtendedNotifications/gntp-send/tcp.h | 28 +- plugins/ExtendedNotifications/main.c | 2224 +- plugins/ExtendedNotifications/resource.h | 66 +- plugins/ExtendedServices/CHANGELOG.txt | 76 +- plugins/ExtendedServices/ExtendedServices.rc | 698 +- .../ExtendedServices/ExtendedServices.vcxproj | 148 +- plugins/ExtendedServices/depend.c | 688 +- plugins/ExtendedServices/extsrv.h | 256 +- plugins/ExtendedServices/main.c | 960 +- plugins/ExtendedServices/options.c | 146 +- plugins/ExtendedServices/other.c | 1462 +- plugins/ExtendedServices/recovery.c | 1418 +- plugins/ExtendedServices/resource.h | 146 +- plugins/ExtendedServices/srvprgrs.c | 300 +- plugins/ExtendedServices/trigger.c | 3440 +-- plugins/ExtendedServices/triggpg.c | 364 +- plugins/ExtendedTools/CHANGELOG.txt | 134 +- plugins/ExtendedTools/ExtendedTools.rc | 1086 +- plugins/ExtendedTools/ExtendedTools.vcxproj | 234 +- plugins/ExtendedTools/d3dkmt.h | 992 +- plugins/ExtendedTools/disktab.c | 2344 +- plugins/ExtendedTools/disktabp.h | 348 +- plugins/ExtendedTools/etwdisk.c | 1072 +- plugins/ExtendedTools/etwmini.c | 528 +- plugins/ExtendedTools/etwmini.h | 74 +- plugins/ExtendedTools/etwmon.c | 1124 +- plugins/ExtendedTools/etwmon.h | 280 +- plugins/ExtendedTools/etwprprp.c | 1228 +- plugins/ExtendedTools/etwstat.c | 838 +- plugins/ExtendedTools/etwsys.c | 1622 +- plugins/ExtendedTools/etwsys.h | 180 +- plugins/ExtendedTools/exttools.h | 1098 +- plugins/ExtendedTools/gpumini.c | 258 +- plugins/ExtendedTools/gpumini.h | 40 +- plugins/ExtendedTools/gpumon.c | 1650 +- plugins/ExtendedTools/gpumon.h | 86 +- plugins/ExtendedTools/gpunodes.c | 860 +- plugins/ExtendedTools/gpuprprp.c | 1532 +- plugins/ExtendedTools/gpusys.c | 1444 +- plugins/ExtendedTools/gpusys.h | 158 +- plugins/ExtendedTools/iconext.c | 932 +- plugins/ExtendedTools/main.c | 1232 +- plugins/ExtendedTools/modsrv.c | 346 +- plugins/ExtendedTools/objprp.c | 624 +- plugins/ExtendedTools/options.c | 174 +- plugins/ExtendedTools/procicon.c | 190 +- plugins/ExtendedTools/resource.h | 228 +- plugins/ExtendedTools/thrdact.c | 128 +- plugins/ExtendedTools/treeext.c | 1566 +- plugins/ExtendedTools/unldll.c | 714 +- plugins/ExtendedTools/utils.c | 96 +- plugins/ExtendedTools/wswatch.c | 1142 +- plugins/NetworkTools/CHANGELOG.txt | 64 +- plugins/NetworkTools/NetworkTools.rc | 1446 +- plugins/NetworkTools/NetworkTools.vcxproj | 794 +- plugins/NetworkTools/main.c | 1462 +- plugins/NetworkTools/nettools.h | 660 +- plugins/NetworkTools/options.c | 160 +- plugins/NetworkTools/ping.c | 1218 +- plugins/NetworkTools/resource.h | 564 +- plugins/NetworkTools/tracert.c | 1542 +- plugins/NetworkTools/whois.c | 1206 +- plugins/OnlineChecks/CHANGELOG.txt | 58 +- plugins/OnlineChecks/OnlineChecks.rc | 302 +- plugins/OnlineChecks/OnlineChecks.vcxproj | 200 +- plugins/OnlineChecks/main.c | 1704 +- plugins/OnlineChecks/onlnchk.h | 538 +- plugins/OnlineChecks/resource.h | 42 +- plugins/OnlineChecks/upload.c | 2674 +-- plugins/Plugins.sln | 330 +- plugins/SamplePlugin/SamplePlugin.sln | 52 +- plugins/SamplePlugin/SamplePlugin.vcxproj | 342 +- plugins/SamplePlugin/main.c | 522 +- plugins/SbieSupport/SbieSupport.rc | 292 +- plugins/SbieSupport/SbieSupport.vcxproj | 128 +- plugins/SbieSupport/main.c | 1110 +- plugins/SbieSupport/resource.h | 36 +- plugins/SbieSupport/sbiedll.h | 84 +- plugins/ToolStatus/CHANGELOG.txt | 126 +- plugins/ToolStatus/ToolStatus.rc | 554 +- plugins/ToolStatus/ToolStatus.vcxproj | 230 +- plugins/ToolStatus/customizesb.c | 1264 +- plugins/ToolStatus/customizetb.c | 1884 +- plugins/ToolStatus/filter.c | 1340 +- plugins/ToolStatus/graph.c | 1322 +- plugins/ToolStatus/main.c | 2888 +-- plugins/ToolStatus/options.c | 194 +- plugins/ToolStatus/resource.h | 114 +- plugins/ToolStatus/searchbox.c | 94 +- plugins/ToolStatus/statusbar.c | 1160 +- plugins/ToolStatus/toolbar.c | 1702 +- plugins/ToolStatus/toolstatus.h | 762 +- plugins/Updater/CHANGELOG.txt | 90 +- plugins/Updater/Updater.vcxproj | 198 +- plugins/UserNotes/CHANGELOG.txt | 48 +- plugins/UserNotes/UserNotes.rc | 364 +- plugins/UserNotes/UserNotes.vcxproj | 178 +- plugins/UserNotes/db.c | 894 +- plugins/UserNotes/db.h | 184 +- plugins/UserNotes/main.c | 3782 ++-- plugins/UserNotes/resource.h | 50 +- plugins/WindowExplorer/CHANGELOG.txt | 42 +- plugins/WindowExplorer/WindowExplorer.rc | 616 +- plugins/WindowExplorer/WindowExplorer.vcxproj | 186 +- plugins/WindowExplorer/hook.c | 1018 +- plugins/WindowExplorer/main.c | 650 +- plugins/WindowExplorer/resource.h | 164 +- plugins/WindowExplorer/utils.c | 208 +- plugins/WindowExplorer/wnddlg.c | 2384 +- plugins/WindowExplorer/wndexp.h | 270 +- plugins/WindowExplorer/wndprp.c | 2420 +- plugins/WindowExplorer/wndtree.c | 1142 +- plugins/WindowExplorer/wndtree.h | 196 +- plugins/include/toolstatusintf.h | 98 +- plugins/readme.txt | 4 +- tests/phlib-test/main.c | 32 +- tests/phlib-test/phlib-test.vcxproj | 230 +- tests/phlib-test/t_basesup.c | 704 +- tests/phlib-test/t_format.c | 1022 +- tests/phlib-test/tests.h | 44 +- tools/GenerateZw/GenerateZw.sln | 44 +- tools/GenerateZw/GenerateZw/GenerateZw.csproj | 120 +- tools/fixlib/fixlib.c | 226 +- tools/fixlib/fixlib.vcxproj | 216 +- tools/peview/include/peview.h | 78 +- tools/peview/libprp.c | 362 +- tools/peview/main.c | 256 +- tools/peview/misc.c | 204 +- tools/peview/peprp.c | 2816 +-- tools/peview/peview.manifest | 90 +- tools/peview/peview.rc | 480 +- tools/peview/peview.vcxproj | 440 +- tools/peview/resource.h | 80 +- tools/peview/version.rc | 70 +- 362 files changed, 190721 insertions(+), 190721 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 493211cc7e7d..a2a8cbddb6e7 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,668 +1,668 @@ -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. +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.vcxproj b/KProcessHacker/KProcessHacker.vcxproj index 98820bf88ad9..87fb632baad2 100644 --- a/KProcessHacker/KProcessHacker.vcxproj +++ b/KProcessHacker/KProcessHacker.vcxproj @@ -1,139 +1,139 @@ - - - - - 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/KProcessHacker/devctrl.c b/KProcessHacker/devctrl.c index f6a048fae306..50a4a645fd28 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, + __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; +} diff --git a/KProcessHacker/dyndata.c b/KProcessHacker/dyndata.c index 616e3aa7125c..40d4cfe5d470 100644 --- a/KProcessHacker/dyndata.c +++ b/KProcessHacker/dyndata.c @@ -1,167 +1,167 @@ -/* - * 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 + +#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; +} diff --git a/KProcessHacker/dynimp.c b/KProcessHacker/dynimp.c index 979364464879..d432611a3ca8 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..d31f0f0e10e7 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..8ee115f9b7ab 100644 --- a/KProcessHacker/include/kph.h +++ b/KProcessHacker/include/kph.h @@ -1,365 +1,365 @@ -#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 + +// 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 diff --git a/KProcessHacker/include/ntfill.h b/KProcessHacker/include/ntfill.h index f430f49983e5..865bbe877cb1 100644 --- a/KProcessHacker/include/ntfill.h +++ b/KProcessHacker/include/ntfill.h @@ -1,351 +1,351 @@ -#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_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 diff --git a/KProcessHacker/main.c b/KProcessHacker/main.c index d4eef0fc45b1..31623234b278 100644 --- a/KProcessHacker/main.c +++ b/KProcessHacker/main.c @@ -1,356 +1,356 @@ -/* - * 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; +__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; +} diff --git a/KProcessHacker/object.c b/KProcessHacker/object.c index 65e30548cfb3..f272a16f9826 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 *)((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; +} diff --git a/KProcessHacker/process.c b/KProcessHacker/process.c index bdb27261fa3b..c0aa6d1b63d2 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 + ); + } + + 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; +} diff --git a/KProcessHacker/qrydrv.c b/KProcessHacker/qrydrv.c index 3ecfb6197f00..d2cb31b8ba18 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_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; + } +} 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 index fe9c32599e2a..90e2927feed0 100644 --- a/KProcessHacker/sign.cmd +++ b/KProcessHacker/sign.cmd @@ -1,7 +1,7 @@ -@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 +@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/thread.c b/KProcessHacker/thread.c index 1ac2b750aab7..da8154c0c5d0 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 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; +} diff --git a/KProcessHacker/vm.c b/KProcessHacker/vm.c index 0ffbb578181d..41dc7ef8a85f 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_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; +} 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/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 74667f423a66..4bc346f7cf65 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1,2632 +1,2632 @@ -// 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,3,5,88,10 - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,256,239,WS_EX_CLIENTEDGE - EDITTEXT IDC_HANDLESEARCH,114,3,144,14,ES_AUTOHSCROLL -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,224,14,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,112 - CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,168,246,57 - 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, 2 - RIGHTMARGIN, 258 - TOPMARGIN, 3 - BOTTOMMARGIN, 258 - 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" - -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" - - -///////////////////////////////////////////////////////////////////////////// -// -// 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 - -IDD_PROCHANDLES AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_OBJTOKEN AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// 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 - +// 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,3,5,88,10 + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,256,239,WS_EX_CLIENTEDGE + EDITTEXT IDC_HANDLESEARCH,114,3,144,14,ES_AUTOHSCROLL +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,224,14,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,112 + CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,168,246,57 + 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, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 + 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" + +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" + + +///////////////////////////////////////////////////////////////////////////// +// +// 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 + +IDD_PROCHANDLES AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_OBJTOKEN AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// 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 dc1045b9f277..8f2a2bbc1f1f 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -1,454 +1,454 @@ - - - - - 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;windowscodecs.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) - - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - - - - - 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;windowscodecs.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) - - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - - - - - 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;windowscodecs.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) - - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - - - - - 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;windowscodecs.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) - - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - - - - - - - - - - - - - + + + + + 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;windowscodecs.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) + + + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev + Generating revision number... + + + + + + + + + + + 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;windowscodecs.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) + + + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev + Generating revision number... + + + + + + + + + + + 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;windowscodecs.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) + + + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev + Generating revision number... + + + + + + + + + + + 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;windowscodecs.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) + + + ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev + Generating revision number... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index fd9872cc7edf..834b055bd054 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -1,190 +1,190 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 36eaf7a16d21..fbec1593805a 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1,3130 +1,3130 @@ -/* - * 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 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; +} diff --git a/ProcessHacker/affinity.c b/ProcessHacker/affinity.c index 9515cb6933ee..d019c3e2ac65 100644 --- a/ProcessHacker/affinity.c +++ b/ProcessHacker/affinity.c @@ -1,317 +1,317 @@ -/* - * 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 +#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; +} diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index 63f355a0e008..bc7c2af90f70 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -1,1076 +1,1076 @@ -/* - * 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 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; +} diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index fa61833b5fda..1d09f88b4014 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1,2207 +1,2207 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/chcol.c b/ProcessHacker/chcol.c index 58f06e824743..5774a3a54550 100644 --- a/ProcessHacker/chcol.c +++ b/ProcessHacker/chcol.c @@ -1,425 +1,425 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/chdlg.c b/ProcessHacker/chdlg.c index 97c4bdac6eee..bb4683bc5d0e 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; + + 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; +} diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index ef2b32659a40..fd0578842349 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -1,316 +1,316 @@ -/* - * 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 + +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; +} diff --git a/ProcessHacker/cmdmode.c b/ProcessHacker/cmdmode.c index c47b048ecef2..481e03937358 100644 --- a/ProcessHacker/cmdmode.c +++ b/ProcessHacker/cmdmode.c @@ -1,452 +1,452 @@ -/* - * 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 = 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; +} diff --git a/ProcessHacker/colmgr.c b/ProcessHacker/colmgr.c index ba95bbcad89f..3f4aafb68c40 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->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); +} diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index d327bb58c58b..7708ddbae347 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -1,1695 +1,1695 @@ -/* - * 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 + +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; +} diff --git a/ProcessHacker/extmgr.c b/ProcessHacker/extmgr.c index 8d93706205d5..d86051f0bb61 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 (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; +} diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 182f4055e882..926cc9549c2a 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -1,1018 +1,1018 @@ -/* - * 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); - - PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), NULL); - - 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); + + PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), NULL); + + 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; +} diff --git a/ProcessHacker/gdihndl.c b/ProcessHacker/gdihndl.c index 69184df2e480..c8d192d049a0 100644 --- a/ProcessHacker/gdihndl.c +++ b/ProcessHacker/gdihndl.c @@ -1,382 +1,382 @@ -/* - * 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 + ) +{ + 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; +} diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index f28d5d5e260d..926b16cb00eb 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -1,1249 +1,1249 @@ -/* - * 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 +#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; +} diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index 110316c924ed..517f838e79ee 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -1,749 +1,749 @@ -/* - * Process Hacker - - * handle list - * - * Copyright (C) 2011-2013 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 - -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, -1, 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_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); - - PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList); -} - -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 - ) -{ - if (Context->HideUnnamedHandles != HideUnnamedHandles) - { - Context->HideUnnamedHandles = HideUnnamedHandles; - } -} - -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)) != -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 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 = 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 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 + +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, -1, 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_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); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList); +} + +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 + ) +{ + if (Context->HideUnnamedHandles != HideUnnamedHandles) + { + Context->HideUnnamedHandles = HideUnnamedHandles; + } +} + +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)) != -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 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 = 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); +} diff --git a/ProcessHacker/hndlprp.c b/ProcessHacker/hndlprp.c index 4c1123c36cf2..339a95476d44 100644 --- a/ProcessHacker/hndlprp.c +++ b/ProcessHacker/hndlprp.c @@ -1,336 +1,336 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 97819bd18658..5a21fe2d26f7 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -1,720 +1,720 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/hndlstat.c b/ProcessHacker/hndlstat.c index 810948439965..3835d47f750d 100644 --- a/ProcessHacker/hndlstat.c +++ b/ProcessHacker/hndlstat.c @@ -1,240 +1,240 @@ -/* - * 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; + + 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; +} 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/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..471f9f9482ce 100644 --- a/ProcessHacker/include/hidnproc.h +++ b/ProcessHacker/include/hidnproc.h @@ -1,75 +1,75 @@ -#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 +} 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/mainwndp.h b/ProcessHacker/include/mainwndp.h index 04e1cfd8c8f6..239f6f720bbd 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -1,512 +1,512 @@ -#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 + +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 diff --git a/ProcessHacker/include/memsrch.h b/ProcessHacker/include/memsrch.h index 76f1318af3f5..5804a8f1f9d9 100644 --- a/ProcessHacker/include/memsrch.h +++ b/ProcessHacker/include/memsrch.h @@ -1,61 +1,61 @@ -#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; + 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 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..64e5141ac26a 100644 --- a/ProcessHacker/include/miniinfop.h +++ b/ProcessHacker/include/miniinfop.h @@ -1,405 +1,405 @@ -#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_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 + ); + #endif \ No newline at end of file diff --git a/ProcessHacker/include/notifico.h b/ProcessHacker/include/notifico.h index fe0c17781497..b0a8d613c34e 100644 --- a/ProcessHacker/include/notifico.h +++ b/ProcessHacker/include/notifico.h @@ -1,171 +1,171 @@ -#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 + +#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 diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index b9fafef05156..f4ff73920fa5 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -1,753 +1,753 @@ -#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 - ); - -// searchbox - -// begin_phapppub -PHAPPAPI -VOID -NTAPI -PhCreateSearchControl( - _In_ HWND Parent, - _In_ HWND WindowHandle, - _In_ PWSTR BannerText - ); - -PHAPPAPI -HBITMAP -NTAPI -PhLoadPngImageFromResource( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ); - -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) - SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE); - - return fontHandle; -} -// 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 - ); - -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 + +#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 + ); + +// searchbox + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhCreateSearchControl( + _In_ HWND Parent, + _In_ HWND WindowHandle, + _In_ PWSTR BannerText + ); + +PHAPPAPI +HBITMAP +NTAPI +PhLoadPngImageFromResource( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ); + +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) + SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE); + + return fontHandle; +} +// 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 + ); + +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 diff --git a/ProcessHacker/include/phappres.h b/ProcessHacker/include/phappres.h index e0ff77b3e913..3f8215df22e9 100644 --- a/ProcessHacker/include/phappres.h +++ b/ProcessHacker/include/phappres.h @@ -1,34 +1,34 @@ -// 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 +// 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 diff --git a/ProcessHacker/include/phapprev_in.h b/ProcessHacker/include/phapprev_in.h index 5f08adeb2006..c1e40313fd88 100644 --- a/ProcessHacker/include/phapprev_in.h +++ b/ProcessHacker/include/phapprev_in.h @@ -1,6 +1,6 @@ -#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION $COMMITS$ - -#endif // PHAPPREV_H +#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..68ea5602ede1 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -1,675 +1,675 @@ -#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] + 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 diff --git a/ProcessHacker/include/phsvc.h b/ProcessHacker/include/phsvc.h index 80fe51e05d21..d2f762541a4d 100644 --- a/ProcessHacker/include/phsvc.h +++ b/ProcessHacker/include/phsvc.h @@ -1,238 +1,238 @@ -#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_ 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 diff --git a/ProcessHacker/include/phsvcapi.h b/ProcessHacker/include/phsvcapi.h index ca4061239101..cb9458073036 100644 --- a/ProcessHacker/include/phsvcapi.h +++ b/ProcessHacker/include/phsvcapi.h @@ -1,310 +1,310 @@ -#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, + 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 diff --git a/ProcessHacker/include/phsvccl.h b/ProcessHacker/include/phsvccl.h index 491669865b80..93fe474f2cf2 100644 --- a/ProcessHacker/include/phsvccl.h +++ b/ProcessHacker/include/phsvccl.h @@ -1,155 +1,155 @@ -#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 PhSvcCallLoadDbgHelp( + _In_ PWSTR DbgHelpPath + ); + +NTSTATUS PhSvcCallWriteMiniDumpProcess( + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE FileHandle, + _In_ ULONG DumpType + ); + +#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/procprpp.h b/ProcessHacker/include/procprpp.h index 64149e5078b4..3d5538b7d97b 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -1,346 +1,346 @@ -#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; - 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; -// 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 +{ + 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; + 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; +// 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 diff --git a/ProcessHacker/include/settings.h b/ProcessHacker/include/settings.h index 476961404c1e..bf0e871bb5c6 100644 --- a/ProcessHacker/include/settings.h +++ b/ProcessHacker/include/settings.h @@ -1,229 +1,229 @@ -#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 +#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 index 4d8350c8daa4..8b31daaaea08 100644 --- a/ProcessHacker/include/settingsp.h +++ b/ProcessHacker/include/settingsp.h @@ -1,46 +1,46 @@ -#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 +#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/sysinfo.h b/ProcessHacker/include/sysinfo.h index 88d2cf5fa8c4..89127b2a383d 100644 --- a/ProcessHacker/include/sysinfo.h +++ b/ProcessHacker/include/sysinfo.h @@ -1,164 +1,164 @@ -#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; +// 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..261c23151325 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -1,439 +1,439 @@ -#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 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 diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 10584a25dfc1..41947bfe8f86 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -1,215 +1,215 @@ -/* - * 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; +} 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 + ); +} diff --git a/ProcessHacker/itemtips.c b/ProcessHacker/itemtips.c index 184cdb7c2239..318592248aaf 100644 --- a/ProcessHacker/itemtips.c +++ b/ProcessHacker/itemtips.c @@ -1,704 +1,704 @@ -/* - * 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 + +#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); +} diff --git a/ProcessHacker/jobprp.c b/ProcessHacker/jobprp.c index e43759b49b5e..c3ecb3db161a 100644 --- a/ProcessHacker/jobprp.c +++ b/ProcessHacker/jobprp.c @@ -1,660 +1,660 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index 64e799ae92a0..a57862b1c614 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -1,240 +1,240 @@ -/* - * 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 + +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(); + } +} diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 4c598eaa8e65..d35acede54ac 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -1,356 +1,356 @@ -/* - * 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 + +#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; +} diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index ce5c652bb60d..3d8b82255a96 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -1,1072 +1,1072 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); + } +} diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 518c71b74c18..033a4850bd32 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1,3641 +1,3641 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index 25fa6284c8e6..92e88db305eb 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -1,466 +1,466 @@ -/* - * 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; + 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; +} diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index b5c78285c8c3..4cf0897ad7e9 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -1,536 +1,536 @@ -/* - * 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 +#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; +} diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index cc36d29727cf..4e5d9373c741 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -1,918 +1,918 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index c1abb62565b2..732e15b07729 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -1,288 +1,288 @@ -/* - * 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 + +#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; +} diff --git a/ProcessHacker/memprot.c b/ProcessHacker/memprot.c index d52168c948c2..5feec758b8b9 100644 --- a/ProcessHacker/memprot.c +++ b/ProcessHacker/memprot.c @@ -1,162 +1,162 @@ -/* - * 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 +#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; +} diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 35e10d10ff50..14e9ca24bc42 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -1,810 +1,810 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index c9c7bb79a94b..36d36deaf05e 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -1,622 +1,622 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index a417630d5686..41c2b0c8876e 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -1,731 +1,731 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 2ff47409664f..743efd387a2b 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1,2273 +1,2273 @@ -/* - * 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; + + 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]); +} diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 45df208186d0..3cca62efabd0 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -1,1012 +1,1012 @@ -/* - * 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 + +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); +} diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index f3483ad3ef41..30c04ac6af7b 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -1,589 +1,589 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/mxml/mxml.h b/ProcessHacker/mxml/mxml.h index 6e8015874882..91c382291be7 100644 --- a/ProcessHacker/mxml/mxml.h +++ b/ProcessHacker/mxml/mxml.h @@ -32,10 +32,10 @@ # include # include -#ifdef _PHAPP_ -#define PHMXMLAPI __declspec(dllexport) -#else -#define PHMXMLAPI +#ifdef _PHAPP_ +#define PHMXMLAPI __declspec(dllexport) +#else +#define PHMXMLAPI #endif /* diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index 4ccd7eac42b1..6efb1357d811 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -1,797 +1,797 @@ -/* - * 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 + +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); +} diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index 9d79ec30219c..d3cc1187db6a 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -1,996 +1,996 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/netstk.c b/ProcessHacker/netstk.c index 2fda236e61d0..2e89b091a71a 100644 --- a/ProcessHacker/netstk.c +++ b/ProcessHacker/netstk.c @@ -1,240 +1,240 @@ -/* - * 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; + 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; +} diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index f7e842a2d6cf..ccaedd1c3915 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -1,1345 +1,1345 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index 344d607ca98a..3864cfc5fd41 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -1,757 +1,757 @@ -/* - * 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 + +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; +} diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 8651a210c01e..32782a527ce9 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1,1265 +1,1265 @@ -/* - * 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 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; +} diff --git a/ProcessHacker/pagfiles.c b/ProcessHacker/pagfiles.c index 067e31ad334c..43fa8df9d759 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), -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; +} diff --git a/ProcessHacker/pcre/pcre2_compile.c b/ProcessHacker/pcre/pcre2_compile.c index 0b93e22c4b23..0677e1fc6f19 100644 --- a/ProcessHacker/pcre/pcre2_compile.c +++ b/ProcessHacker/pcre/pcre2_compile.c @@ -1,9087 +1,9087 @@ -/************************************************* -* 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 */ - +/************************************************* +* 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 diff --git a/ProcessHacker/pcre/pcre2_match.c b/ProcessHacker/pcre/pcre2_match.c index 9a8a472e7361..bf770ab90cb0 100644 --- a/ProcessHacker/pcre/pcre2_match.c +++ b/ProcessHacker/pcre/pcre2_match.c @@ -38,8 +38,8 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -// dmex: Disable warnings -#pragma warning(push) +// dmex: Disable warnings +#pragma warning(push) #pragma warning(disable : 4267) #define HAVE_CONFIG_H diff --git a/ProcessHacker/pcre/pcre2posix.c b/ProcessHacker/pcre/pcre2posix.c index 6a71969340d4..eb5926550119 100644 --- a/ProcessHacker/pcre/pcre2posix.c +++ b/ProcessHacker/pcre/pcre2posix.c @@ -38,8 +38,8 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -// dmex: Disable warnings -#pragma warning(push) +// dmex: Disable warnings +#pragma warning(push) #pragma warning(disable : 4267) /* This module is a wrapper that provides a POSIX API to the underlying PCRE2 diff --git a/ProcessHacker/phsvc/clapi.c b/ProcessHacker/phsvc/clapi.c index 3772cf25e837..5a5cfb5b867a 100644 --- a/ProcessHacker/phsvc/clapi.c +++ b/ProcessHacker/phsvc/clapi.c @@ -1,1217 +1,1217 @@ -/* - * 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; + } + + 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; +} diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index c18ca5045e44..a375f4a6dee4 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -1,1446 +1,1446 @@ -/* - * 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 +#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; + } +} diff --git a/ProcessHacker/phsvc/svcapiport.c b/ProcessHacker/phsvc/svcapiport.c index 7eae7d306460..fb31465e3e3c 100644 --- a/ProcessHacker/phsvc/svcapiport.c +++ b/ProcessHacker/phsvc/svcapiport.c @@ -1,318 +1,318 @@ -/* - * 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; + 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); + } +} diff --git a/ProcessHacker/phsvc/svcclient.c b/ProcessHacker/phsvc/svcclient.c index 537d30440a6d..8b6fe4cea657 100644 --- a/ProcessHacker/phsvc/svcclient.c +++ b/ProcessHacker/phsvc/svcclient.c @@ -1,159 +1,159 @@ -/* - * 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; +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; +} diff --git a/ProcessHacker/phsvc/svcmain.c b/ProcessHacker/phsvc/svcmain.c index dfc9685e4198..660c55cb3618 100644 --- a/ProcessHacker/phsvc/svcmain.c +++ b/ProcessHacker/phsvc/svcmain.c @@ -1,111 +1,111 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 0af42c05aacc..8f30f4e9da33 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -1,1043 +1,1043 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index 680a7dcfce9c..a7c4314126a5 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -1,445 +1,445 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/procgrp.c b/ProcessHacker/procgrp.c index 87f76ababcaa..e0e38b8ca587 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; + +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); +} diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index b106b68a795b..17488331c501 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -1,724 +1,724 @@ -/* - * 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; +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; + } +} diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 5640344ef0d5..9445050002f7 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1,2979 +1,2979 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; +} diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 77d407ef28c9..486a3db8841c 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -1,262 +1,262 @@ -/* - * 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_ 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; +} diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 0bd39228551e..9cdd693494ed 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1,3508 +1,3508 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; +} diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index ce6d174476b4..d5f721adb148 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -1,751 +1,751 @@ -//{{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 IDB_SEARCH_ACTIVE 223 -#define IDB_SEARCH_INACTIVE 224 -#define IDB_SEARCH_ACTIVE_BMP 225 -#define IDB_SEARCH_INACTIVE_BMP 226 -#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 IDC_HANDLESEARCH 1385 -#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 228 -#define _APS_NEXT_COMMAND_VALUE 40295 -#define _APS_NEXT_CONTROL_VALUE 1387 -#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 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 IDB_SEARCH_ACTIVE 223 +#define IDB_SEARCH_INACTIVE 224 +#define IDB_SEARCH_ACTIVE_BMP 225 +#define IDB_SEARCH_INACTIVE_BMP 226 +#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 IDC_HANDLESEARCH 1385 +#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 228 +#define _APS_NEXT_COMMAND_VALUE 40295 +#define _APS_NEXT_CONTROL_VALUE 1387 +#define _APS_NEXT_SYMED_VALUE 169 +#endif +#endif diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index fb71c6fe94f7..6abbef469ff3 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -1,1167 +1,1167 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; +} 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/sessmsg.c b/ProcessHacker/sessmsg.c index 776f8d1995d5..2e48174e87e2 100644 --- a/ProcessHacker/sessmsg.c +++ b/ProcessHacker/sessmsg.c @@ -1,160 +1,160 @@ -/* - * 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; + + 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; +} diff --git a/ProcessHacker/sessprp.c b/ProcessHacker/sessprp.c index 60a1aee807f2..076696e7b71a 100644 --- a/ProcessHacker/sessprp.c +++ b/ProcessHacker/sessprp.c @@ -1,237 +1,237 @@ -/* - * 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; + + 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; +} diff --git a/ProcessHacker/sessshad.c b/ProcessHacker/sessshad.c index 78e4377fbf10..58c03ced17f6 100644 --- a/ProcessHacker/sessshad.c +++ b/ProcessHacker/sessshad.c @@ -1,239 +1,239 @@ -/* - * 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 + +#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; +} diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index ddc8f441af47..addc547df23f 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -1,1198 +1,1198 @@ -/* - * 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 + * + * 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); +} diff --git a/ProcessHacker/srvcr.c b/ProcessHacker/srvcr.c index 5ad4500bd80c..2e19f821bc82 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 29b39231266a..72b9066b80ab 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -1,412 +1,412 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index f39fb8ad392b..897399a6bb29 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -1,904 +1,904 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index fe193f2dd90a..658efd1f439c 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -1,574 +1,574 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index c7f9d45bbc7f..18f258c7aa3d 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -1,1041 +1,1041 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; + } +} diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 709a126b2f5c..e8e389dbce97 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -1,2054 +1,2054 @@ -/* - * 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 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)); +} diff --git a/ProcessHacker/thrdlist.c b/ProcessHacker/thrdlist.c index 1800cbdadfdc..cae5d04594aa 100644 --- a/ProcessHacker/thrdlist.c +++ b/ProcessHacker/thrdlist.c @@ -1,723 +1,723 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 85f3f6a6c79d..8f7a60b85b6a 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -1,1115 +1,1115 @@ -/* - * 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 + +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++; +} diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 3e64ca2dc9f1..5e7d9e053328 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -1,697 +1,697 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 27263e020fac..a80fc3f4aac8 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -1,1968 +1,1968 @@ -/* - * Process Hacker - - * token properties - * - * 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 . - */ - -#include -#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; - PPH_HSPLITTER_CONTEXT HSplitterContext; - - 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"); - - tokenPageContext->HSplitterContext = PhInitializeHSplitter( - hwndDlg, - tokenPageContext->GroupsListViewHandle, - tokenPageContext->PrivilegesListViewHandle - ); - - 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); - if (tokenPageContext->HSplitterContext) PhDeleteHSplitter(tokenPageContext->HSplitterContext); - } - 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: - { - if (ListView_GetItemCount(tokenPageContext->GroupsListViewHandle) != 0) - { - ListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 0, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - if (ListView_GetItemCount(tokenPageContext->PrivilegesListViewHandle) != 0) - { - ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 0, LVSCW_AUTOSIZE); - ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 1, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 2, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - 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; - case WM_SIZE: - PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); - break; - case WM_LBUTTONDOWN: - PhHSplitterHandleLButtonDown(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - break; - case WM_LBUTTONUP: - PhHSplitterHandleLButtonUp(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - break; - case WM_MOUSEMOVE: - PhHSplitterHandleMouseMove(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - 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 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 _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; + PPH_HSPLITTER_CONTEXT HSplitterContext; + + 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"); + + tokenPageContext->HSplitterContext = PhInitializeHSplitter( + hwndDlg, + tokenPageContext->GroupsListViewHandle, + tokenPageContext->PrivilegesListViewHandle + ); + + 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); + if (tokenPageContext->HSplitterContext) PhDeleteHSplitter(tokenPageContext->HSplitterContext); + } + 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: + { + if (ListView_GetItemCount(tokenPageContext->GroupsListViewHandle) != 0) + { + ListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 0, LVSCW_AUTOSIZE); + ExtendedListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + + if (ListView_GetItemCount(tokenPageContext->PrivilegesListViewHandle) != 0) + { + ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 0, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 1, LVSCW_AUTOSIZE); + ExtendedListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 2, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + + 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; + case WM_SIZE: + PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); + break; + case WM_LBUTTONDOWN: + PhHSplitterHandleLButtonDown(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + break; + case WM_LBUTTONUP: + PhHSplitterHandleLButtonUp(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + break; + case WM_MOUSEMOVE: + PhHSplitterHandleMouseMove(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); + 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; +} diff --git a/ProcessHacker/version.rc b/ProcessHacker/version.rc index 719f3d2ab572..6caa53e40114 100644 --- a/ProcessHacker/version.rc +++ b/ProcessHacker/version.rc @@ -1,35 +1,35 @@ -#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 +#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 diff --git a/phlib/apiimport.c b/phlib/apiimport.c index a538d8359f6b..bcf06a49574a 100644 --- a/phlib/apiimport.c +++ b/phlib/apiimport.c @@ -1,68 +1,68 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); diff --git a/phlib/basesup.c b/phlib/basesup.c index e93241089a70..61ee300a3574 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -1,5985 +1,5985 @@ -/* - * 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 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; + } +} 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..530690e9b458 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 = 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); +} diff --git a/phlib/cpysave.c b/phlib/cpysave.c index 3076f9f491cc..9c250999ef65 100644 --- a/phlib/cpysave.c +++ b/phlib/cpysave.c @@ -1,574 +1,574 @@ -/* - * 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); + + 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; +} 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..5bbc7f1cad6e 100644 --- a/phlib/dspick.c +++ b/phlib/dspick.c @@ -1,251 +1,251 @@ -/* - * 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 +#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); +} diff --git a/phlib/emenu.c b/phlib/emenu.c index 128b1757692d..920a2d0f0e2a 100644 --- a/phlib/emenu.c +++ b/phlib/emenu.c @@ -1,857 +1,857 @@ -/* - * 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 + +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; + } +} diff --git a/phlib/error.c b/phlib/error.c index 8c3f2373a917..b0c27c4e909e 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 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; + } +} diff --git a/phlib/extlv.c b/phlib/extlv.c index cb4c1654d951..4ab2b593390c 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -1,734 +1,734 @@ -/* - * 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 + +#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 +} 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..9b76e6fa44a2 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] = -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); +} diff --git a/phlib/format.c b/phlib/format.c index ec036cd0dd6f..b45fd38439c4 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)] = 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; +} 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..fb8a44453ab0 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -1,239 +1,239 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; + } +} diff --git a/phlib/graph.c b/phlib/graph.c index 27dfb6359b3a..8c47136d2ead 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -1,1335 +1,1335 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/phlib/guisup.c b/phlib/guisup.c index 60c354eec614..a9e1436ab891 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1,1374 +1,1374 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; + } +} 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..edd89248995d 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -1,1868 +1,1868 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 5c0109acc8d0..749a6d91ccb9 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -1,1776 +1,1776 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/phlib/icotobmp.c b/phlib/icotobmp.c index c969a01b5e19..9d4ce2878c84 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); + } + + 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; +} diff --git a/phlib/include/apiimport.h b/phlib/include/apiimport.h index 8a046e762054..80769e38bc04 100644 --- a/phlib/include/apiimport.h +++ b/phlib/include/apiimport.h @@ -1,86 +1,86 @@ -#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 + +// 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 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..95c39f355d64 100644 --- a/phlib/include/emenu.h +++ b/phlib/include/emenu.h @@ -1,219 +1,219 @@ -#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_ 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 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/graph.h b/phlib/include/graph.h index 87dc2209483e..bc15a12d6039 100644 --- a/phlib/include/graph.h +++ b/phlib/include/graph.h @@ -1,255 +1,255 @@ -#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 + ); + +// 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/guisupp.h b/phlib/include/guisupp.h index e5e8a5e0daa2..a2372bd3ddb5 100644 --- a/phlib/include/guisupp.h +++ b/phlib/include/guisupp.h @@ -1,43 +1,43 @@ -#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 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 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..3fbd74ac32ce 100644 --- a/phlib/include/hexedit.h +++ b/phlib/include/hexedit.h @@ -1,49 +1,49 @@ -#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 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 diff --git a/phlib/include/hexeditp.h b/phlib/include/hexeditp.h index f7c59bba41ab..53bb4f0d0029 100644 --- a/phlib/include/hexeditp.h +++ b/phlib/include/hexeditp.h @@ -1,201 +1,201 @@ -#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; + 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 diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index e8a41992b376..f905de9a09d3 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; + 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) + #endif \ No newline at end of file diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index 55c90b84db5b..102534b55a62 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -1,300 +1,300 @@ -#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 +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/ph.h b/phlib/include/ph.h index eba1c5032fd1..632ee40d2c57 100644 --- a/phlib/include/ph.h +++ b/phlib/include/ph.h @@ -1,11 +1,11 @@ -#ifndef _PH_PH_H -#define _PH_PH_H - -#pragma once - -#include -#include -#include -#include - -#endif +#ifndef _PH_PH_H +#define _PH_PH_H + +#pragma once + +#include +#include +#include +#include + +#endif diff --git a/phlib/include/phbase.h b/phlib/include/phbase.h index 9dd78bc0b8f7..15ad67e42a4d 100644 --- a/phlib/include/phbase.h +++ b/phlib/include/phbase.h @@ -1,37 +1,37 @@ -#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 - +#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 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/phnet.h b/phlib/include/phnet.h index 6c4dcbf8f95d..50394dfd0258 100644 --- a/phlib/include/phnet.h +++ b/phlib/include/phnet.h @@ -1,139 +1,139 @@ -#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 + +#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 diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 3985914d1f49..55e36cf5160f 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -1,546 +1,546 @@ -#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 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 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..4f3bb1389ae9 100644 --- a/phlib/include/ref.h +++ b/phlib/include/ref.h @@ -1,309 +1,309 @@ -/* - * 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 +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 diff --git a/phlib/include/refp.h b/phlib/include/refp.h index f1059d9e4d36..dfa9478f9768 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) + 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 diff --git a/phlib/include/secedit.h b/phlib/include/secedit.h index 18bf0322632d..50090d4452b1 100644 --- a/phlib/include/secedit.h +++ b/phlib/include/secedit.h @@ -1,165 +1,165 @@ -#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 - +#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 diff --git a/phlib/include/seceditp.h b/phlib/include/seceditp.h index e2773866fc1c..d90cfd43d76c 100644 --- a/phlib/include/seceditp.h +++ b/phlib/include/seceditp.h @@ -1,93 +1,93 @@ -#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; + + 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 diff --git a/phlib/include/symprv.h b/phlib/include/symprv.h index 742f1148d39f..4d02924180bf 100644 --- a/phlib/include/symprv.h +++ b/phlib/include/symprv.h @@ -1,303 +1,303 @@ -#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 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 diff --git a/phlib/include/symprvp.h b/phlib/include/symprvp.h index 8f6bd6bb580e..e4435bfc9bb0 100644 --- a/phlib/include/symprvp.h +++ b/phlib/include/symprvp.h @@ -1,170 +1,170 @@ -#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 - ); - +#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 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..51e24253eaa3 100644 --- a/phlib/include/treenew.h +++ b/phlib/include/treenew.h @@ -1,672 +1,672 @@ -#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_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 diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index b8f1d8472ae3..8904a3a1a7cb 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -1,799 +1,799 @@ -#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 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 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..8a9dbdca2d40 100644 --- a/phlib/include/verifyp.h +++ b/phlib/include/verifyp.h @@ -1,118 +1,118 @@ -#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 _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 diff --git a/phlib/kph.c b/phlib/kph.c index 3e427585210f..0acd7d0baa27 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -1,1102 +1,1102 @@ -/* - * 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; +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) + ); +} diff --git a/phlib/kphdata.c b/phlib/kphdata.c index 179d0df5c90f..1ec3c206008b 100644 --- a/phlib/kphdata.c +++ b/phlib/kphdata.c @@ -1,329 +1,329 @@ -/* - * 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 = 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 diff --git a/phlib/maplib.c b/phlib/maplib.c index 3d01feddb655..41b80cdfbafc 100644 --- a/phlib/maplib.c +++ b/phlib/maplib.c @@ -1,402 +1,402 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; +} 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/native.c b/phlib/native.c index 5173837f70b6..169bcf5d01a1 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1,6462 +1,6462 @@ -/* - * 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 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; +} diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index 38d58d7a1bfe..aba1e17fe699 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,249 +1,249 @@ - - - - - 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.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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/phlib/provider.c b/phlib/provider.c index 2e5366a806aa..99d97a85a450 100644 --- a/phlib/provider.c +++ b/phlib/provider.c @@ -1,470 +1,470 @@ -/* - * 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. + 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; +} 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..53af1c79c325 100644 --- a/phlib/ref.c +++ b/phlib/ref.c @@ -1,754 +1,754 @@ -/* - * 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); + + // 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; +} diff --git a/phlib/secdata.c b/phlib/secdata.c index 43b9e2c72b82..fe08d207ac82 100644 --- a/phlib/secdata.c +++ b/phlib/secdata.c @@ -1,788 +1,788 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/phlib/secedit.c b/phlib/secedit.c index d1641425a106..f1c09a04cb80 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -1,577 +1,577 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} 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..cd796658aca7 100644 --- a/phlib/svcsup.c +++ b/phlib/svcsup.c @@ -1,554 +1,554 @@ -/* - * 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) + { + 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; +} diff --git a/phlib/symprv.c b/phlib/symprv.c index 633be28a2d33..837c92c65efb 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -1,1760 +1,1760 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} 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/treenew.c b/phlib/treenew.c index 0132a52bb4bb..c3e63846612b 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -1,6645 +1,6645 @@ -/* - * 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 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; +} diff --git a/phlib/verify.c b/phlib/verify.c index 17ee9c66bce8..518076a6971f 100644 --- a/phlib/verify.c +++ b/phlib/verify.c @@ -1,671 +1,671 @@ -/* - * 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 . + */ + +#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; + } +} diff --git a/phlib/workqueue.c b/phlib/workqueue.c index d77e8104f2ee..b0f39740670f 100644 --- a/phlib/workqueue.c +++ b/phlib/workqueue.c @@ -1,506 +1,506 @@ -/* - * 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 + +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; +} 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..ded0cbd42baa 100644 --- a/plugins/DotNetTools/DotNetTools.rc +++ b/plugins/DotNetTools/DotNetTools.rc @@ -1,182 +1,182 @@ -// 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_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 + diff --git a/plugins/DotNetTools/DotNetTools.vcxproj b/plugins/DotNetTools/DotNetTools.vcxproj index 1c691037b766..f0b912ea36de 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.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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index a66d44dbb4c2..6ead4b55596d 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -1,1314 +1,1314 @@ -/* - * 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 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; +} 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..f565dd9a5154 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; -// } -//}; - +/* + * 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 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..baf11e2f4dcd 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 - - +/* + * 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 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..3fca660cb6ce 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) * 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; +} diff --git a/plugins/DotNetTools/clrsup.h b/plugins/DotNetTools/clrsup.h index 27cc978e4f1d..82a0b13962ae 100644 --- a/plugins/DotNetTools/clrsup.h +++ b/plugins/DotNetTools/clrsup.h @@ -1,645 +1,645 @@ -/* - * 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 + +#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 diff --git a/plugins/DotNetTools/counters.c b/plugins/DotNetTools/counters.c index bce3983bf243..2092908c9e35 100644 --- a/plugins/DotNetTools/counters.c +++ b/plugins/DotNetTools/counters.c @@ -1,929 +1,929 @@ -/* - * 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-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; +} diff --git a/plugins/DotNetTools/dn.h b/plugins/DotNetTools/dn.h index 23fd34947f91..72f7e24132f3 100644 --- a/plugins/DotNetTools/dn.h +++ b/plugins/DotNetTools/dn.h @@ -1,144 +1,144 @@ -/* - * 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 + +#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 diff --git a/plugins/DotNetTools/main.c b/plugins/DotNetTools/main.c index 80118c7720f0..759559574503 100644 --- a/plugins/DotNetTools/main.c +++ b/plugins/DotNetTools/main.c @@ -1,335 +1,335 @@ -/* - * 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; +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; +} diff --git a/plugins/DotNetTools/resource.h b/plugins/DotNetTools/resource.h index bffca0871ee0..62ce099e861b 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 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 diff --git a/plugins/DotNetTools/stackext.c b/plugins/DotNetTools/stackext.c index c5973a935dc0..13ef6a3aca5c 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, 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 +} diff --git a/plugins/DotNetTools/svcext.c b/plugins/DotNetTools/svcext.c index 2cd5f14b9608..9606c84307b9 100644 --- a/plugins/DotNetTools/svcext.c +++ b/plugins/DotNetTools/svcext.c @@ -1,209 +1,209 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); +} diff --git a/plugins/DotNetTools/svcext.h b/plugins/DotNetTools/svcext.h index fc5d63df0374..1600336277f0 100644 --- a/plugins/DotNetTools/svcext.h +++ b/plugins/DotNetTools/svcext.h @@ -1,87 +1,87 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 + ); + #endif \ No newline at end of file diff --git a/plugins/DotNetTools/treeext.c b/plugins/DotNetTools/treeext.c index 74aea6f14b14..874e899718a9 100644 --- a/plugins/DotNetTools/treeext.c +++ b/plugins/DotNetTools/treeext.c @@ -1,296 +1,296 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} 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..ce5be6f4bd35 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_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 + diff --git a/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj b/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj index 7ad9d1b93021..348d996c5ca4 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.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) + + + + + + + + + + + + + + + + + + + + + + + \ 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..372dc978958b 100644 --- a/plugins/ExtendedNotifications/filelog.c +++ b/plugins/ExtendedNotifications/filelog.c @@ -1,79 +1,79 @@ -/* - * 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 "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 + ); +} 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..f47fa7f4fdd8 100644 --- a/plugins/ExtendedNotifications/gntp-send/tcp.c +++ b/plugins/ExtendedNotifications/gntp-send/tcp.c @@ -1,185 +1,185 @@ -#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 +} + +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; +} 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..293db5c4deab 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -1,1112 +1,1112 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedNotifications/resource.h b/plugins/ExtendedNotifications/resource.h index 2aee867c7e6a..7222e3a05259 100644 --- a/plugins/ExtendedNotifications/resource.h +++ b/plugins/ExtendedNotifications/resource.h @@ -1,33 +1,33 @@ -//{{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 + +// 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 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..82d6e9477509 100644 --- a/plugins/ExtendedServices/ExtendedServices.rc +++ b/plugins/ExtendedServices/ExtendedServices.rc @@ -1,349 +1,349 @@ -// 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",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 + diff --git a/plugins/ExtendedServices/ExtendedServices.vcxproj b/plugins/ExtendedServices/ExtendedServices.vcxproj index e56beba514e7..55fd13d45770 100644 --- a/plugins/ExtendedServices/ExtendedServices.vcxproj +++ b/plugins/ExtendedServices/ExtendedServices.vcxproj @@ -1,75 +1,75 @@ - - - - - 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.15063.0 + + + + DynamicLibrary + Unicode + v141 + + + DynamicLibrary + Unicode + v141 + + + DynamicLibrary + Unicode + v141 + + + DynamicLibrary + Unicode + v141 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ExtendedServices/depend.c b/plugins/ExtendedServices/depend.c index b28b234085c2..dc15aa52f2ad 100644 --- a/plugins/ExtendedServices/depend.c +++ b/plugins/ExtendedServices/depend.c @@ -1,344 +1,344 @@ -/* - * 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)); + + 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; +} diff --git a/plugins/ExtendedServices/extsrv.h b/plugins/ExtendedServices/extsrv.h index 654d5db3637d..ff6ee0bd3648 100644 --- a/plugins/ExtendedServices/extsrv.h +++ b/plugins/ExtendedServices/extsrv.h @@ -1,128 +1,128 @@ -#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 + ); + +// 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 diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index edd8f293477c..c36e3b8f38f7 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -1,481 +1,481 @@ -/* - * 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; +/* + * 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 diff --git a/plugins/ExtendedServices/options.c b/plugins/ExtendedServices/options.c index 42bc796d0951..51cb5d9ce2a1 100644 --- a/plugins/ExtendedServices/options.c +++ b/plugins/ExtendedServices/options.c @@ -1,74 +1,74 @@ -/* - * 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 - ); +/* + * 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..5b7957afa653 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -1,732 +1,732 @@ -/* - * 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; +/* + * 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 diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index 553095aaa1ee..b917b0334498 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -1,709 +1,709 @@ -/* - * 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 + + 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; +} 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..0cde80360efb 100644 --- a/plugins/ExtendedServices/srvprgrs.c +++ b/plugins/ExtendedServices/srvprgrs.c @@ -1,151 +1,151 @@ -/* - * 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; + 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 + ); } \ No newline at end of file diff --git a/plugins/ExtendedServices/trigger.c b/plugins/ExtendedServices/trigger.c index b60fe1134721..e0aa2b667435 100644 --- a/plugins/ExtendedServices/trigger.c +++ b/plugins/ExtendedServices/trigger.c @@ -1,1720 +1,1720 @@ -/* - * 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 && 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; +} diff --git a/plugins/ExtendedServices/triggpg.c b/plugins/ExtendedServices/triggpg.c index d8861dd6e4e8..0188ddaead0f 100644 --- a/plugins/ExtendedServices/triggpg.c +++ b/plugins/ExtendedServices/triggpg.c @@ -1,182 +1,182 @@ -/* - * 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; + 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; +} 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..6b90a093c160 100644 --- a/plugins/ExtendedTools/ExtendedTools.rc +++ b/plugins/ExtendedTools/ExtendedTools.rc @@ -1,543 +1,543 @@ -// 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_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 + diff --git a/plugins/ExtendedTools/ExtendedTools.vcxproj b/plugins/ExtendedTools/ExtendedTools.vcxproj index 478dc51ff4c3..ea3f79282e4e 100644 --- a/plugins/ExtendedTools/ExtendedTools.vcxproj +++ b/plugins/ExtendedTools/ExtendedTools.vcxproj @@ -1,118 +1,118 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ExtendedTools/d3dkmt.h b/plugins/ExtendedTools/d3dkmt.h index 863b3b9a3ee5..880cd9fd5f33 100644 --- a/plugins/ExtendedTools/d3dkmt.h +++ b/plugins/ExtendedTools/d3dkmt.h @@ -1,496 +1,496 @@ -#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 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 diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index 232acf3dd7ca..b8b1ae72e953 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -1,1172 +1,1172 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/disktabp.h b/plugins/ExtendedTools/disktabp.h index 23f259290799..794572c90e7c 100644 --- a/plugins/ExtendedTools/disktabp.h +++ b/plugins/ExtendedTools/disktabp.h @@ -1,174 +1,174 @@ -#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 + ); + +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 diff --git a/plugins/ExtendedTools/etwdisk.c b/plugins/ExtendedTools/etwdisk.c index 7980ce675e79..c8512f20a01d 100644 --- a/plugins/ExtendedTools/etwdisk.c +++ b/plugins/ExtendedTools/etwdisk.c @@ -1,536 +1,536 @@ -/* - * 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; + +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++; +} 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..7c4823ae21ba 100644 --- a/plugins/ExtendedTools/etwmon.c +++ b/plugins/ExtendedTools/etwmon.c @@ -1,562 +1,562 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/etwmon.h b/plugins/ExtendedTools/etwmon.h index cb9bca0aee9d..85f3e399bb98 100644 --- a/plugins/ExtendedTools/etwmon.h +++ b/plugins/ExtendedTools/etwmon.h @@ -1,140 +1,140 @@ -#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 +{ + 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 diff --git a/plugins/ExtendedTools/etwprprp.c b/plugins/ExtendedTools/etwprprp.c index 5d2ca9a1843b..1e87ffbd4672 100644 --- a/plugins/ExtendedTools/etwprprp.c +++ b/plugins/ExtendedTools/etwprprp.c @@ -1,615 +1,615 @@ -/* - * 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) - ); - } +/* + * 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 diff --git a/plugins/ExtendedTools/etwstat.c b/plugins/ExtendedTools/etwstat.c index 8238528249a8..33ee65dd33ee 100644 --- a/plugins/ExtendedTools/etwstat.c +++ b/plugins/ExtendedTools/etwstat.c @@ -1,419 +1,419 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/etwsys.c b/plugins/ExtendedTools/etwsys.c index 2cc5b4b14515..dd813a88c088 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, -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; +} 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..e76d42f99688 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -1,549 +1,549 @@ -#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 "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 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..7f941d54a4be 100644 --- a/plugins/ExtendedTools/gpumon.c +++ b/plugins/ExtendedTools/gpumon.c @@ -1,825 +1,825 @@ -/* - * 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 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; + } + } +} diff --git a/plugins/ExtendedTools/gpumon.h b/plugins/ExtendedTools/gpumon.h index 3643a0bf7a62..0ec6f15f23c0 100644 --- a/plugins/ExtendedTools/gpumon.h +++ b/plugins/ExtendedTools/gpumon.h @@ -1,43 +1,43 @@ -#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; + 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 diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index 65fb6e141816..bc863cb45bc2 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -1,430 +1,430 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index 0cc87551e9de..882b3c453d75 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -1,767 +1,767 @@ -/* - * 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) - ); - } +/* + * 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 diff --git a/plugins/ExtendedTools/gpusys.c b/plugins/ExtendedTools/gpusys.c index b57e88e2ad71..57c9dcb68aae 100644 --- a/plugins/ExtendedTools/gpusys.c +++ b/plugins/ExtendedTools/gpusys.c @@ -1,722 +1,722 @@ -/* - * 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"); + 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); +} 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..e125bf364e64 100644 --- a/plugins/ExtendedTools/iconext.c +++ b/plugins/ExtendedTools/iconext.c @@ -1,466 +1,466 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 4bf9c82f8661..108a747d714e 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -1,617 +1,617 @@ -/* - * 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; +/* + * 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 diff --git a/plugins/ExtendedTools/modsrv.c b/plugins/ExtendedTools/modsrv.c index 1a73346f4624..16b110ae4628 100644 --- a/plugins/ExtendedTools/modsrv.c +++ b/plugins/ExtendedTools/modsrv.c @@ -1,173 +1,173 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/ExtendedTools/objprp.c b/plugins/ExtendedTools/objprp.c index 8cf21194bdc6..6716448708b6 100644 --- a/plugins/ExtendedTools/objprp.c +++ b/plugins/ExtendedTools/objprp.c @@ -1,312 +1,312 @@ -/* - * 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 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; +} diff --git a/plugins/ExtendedTools/options.c b/plugins/ExtendedTools/options.c index 20e0c3323788..a33671d5f1e5 100644 --- a/plugins/ExtendedTools/options.c +++ b/plugins/ExtendedTools/options.c @@ -1,87 +1,87 @@ -/* - * 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 + ); + +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; +} diff --git a/plugins/ExtendedTools/procicon.c b/plugins/ExtendedTools/procicon.c index 9724a62da3f0..bfb202f570df 100644 --- a/plugins/ExtendedTools/procicon.c +++ b/plugins/ExtendedTools/procicon.c @@ -1,95 +1,95 @@ -/* - * 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); - } -} +/* + * 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..27a55506b591 100644 --- a/plugins/ExtendedTools/resource.h +++ b/plugins/ExtendedTools/resource.h @@ -1,114 +1,114 @@ -//{{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 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 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..ac9a2e133b44 100644 --- a/plugins/ExtendedTools/treeext.c +++ b/plugins/ExtendedTools/treeext.c @@ -1,783 +1,783 @@ -/* - * 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" +#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; +} diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index e58672399a45..5145c62348e4 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -1,357 +1,357 @@ -/* - * 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 +{ + 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; +} 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..470092cb3b0d 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -1,571 +1,571 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/NetworkTools/CHANGELOG.txt b/plugins/NetworkTools/CHANGELOG.txt index 2cc9983b5553..17bdec0b9e13 100644 --- a/plugins/NetworkTools/CHANGELOG.txt +++ b/plugins/NetworkTools/CHANGELOG.txt @@ -1,32 +1,32 @@ -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 + +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..928883c44c71 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -1,723 +1,723 @@ -// 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,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 + diff --git a/plugins/NetworkTools/NetworkTools.vcxproj b/plugins/NetworkTools/NetworkTools.vcxproj index 5a770136f286..307ef4de731f 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj +++ b/plugins/NetworkTools/NetworkTools.vcxproj @@ -1,398 +1,398 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 9dfaf8fc6fc9..129ffafa72c9 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -1,732 +1,732 @@ -/* - * 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 = PhLoadPngImageFromResource(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|850,490" }, - { 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; +/* + * 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 = PhLoadPngImageFromResource(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|850,490" }, + { 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 diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index f79de0beb6d7..47b8722f86a8 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -1,331 +1,331 @@ -/* - * 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 - -#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]; - +/* + * 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 + +#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 diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index 69cbeda2cebd..e0483f80af03 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -1,81 +1,81 @@ -/* - * 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 - ); +/* + * 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 diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index eb9224c79872..56f39a697b02 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -1,610 +1,610 @@ -/* - * 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); - - 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); - - 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); - - 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); - } +/* + * 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); + + 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); + + 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); + + 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 diff --git a/plugins/NetworkTools/resource.h b/plugins/NetworkTools/resource.h index 1747f72aae6f..84bd7f5c868c 100644 --- a/plugins/NetworkTools/resource.h +++ b/plugins/NetworkTools/resource.h @@ -1,282 +1,282 @@ -//{{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_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 diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 0484e9bc1055..41550a1f123b 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -1,772 +1,772 @@ -/* - * 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 = 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); - - 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); - - 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); - } +/* + * 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 = 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); + + 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); + + 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 diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 4a2b968ffe26..bf6e602d0270 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -1,604 +1,604 @@ -/* - * 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); - - 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 = 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); - - 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); - } +/* + * 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); + + 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 = 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); + + 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 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..650d108605a9 100644 --- a/plugins/OnlineChecks/OnlineChecks.rc +++ b/plugins/OnlineChecks/OnlineChecks.rc @@ -1,151 +1,151 @@ -// 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, 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 + diff --git a/plugins/OnlineChecks/OnlineChecks.vcxproj b/plugins/OnlineChecks/OnlineChecks.vcxproj index f5190e231071..9683eec73393 100644 --- a/plugins/OnlineChecks/OnlineChecks.vcxproj +++ b/plugins/OnlineChecks/OnlineChecks.vcxproj @@ -1,101 +1,101 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index eeb741df6887..9167e0a386db 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -1,853 +1,853 @@ -/* - * 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; +/* + * 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 diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index ef064d48df00..498933978ecd 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -1,269 +1,269 @@ -/* - * 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 + +#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 diff --git a/plugins/OnlineChecks/resource.h b/plugins/OnlineChecks/resource.h index e0adf0b1da02..6b43b8c86cbf 100644 --- a/plugins/OnlineChecks/resource.h +++ b/plugins/OnlineChecks/resource.h @@ -1,21 +1,21 @@ -//{{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 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 diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index e0d15a71f12b..3c8ff16e74c7 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1,1337 +1,1337 @@ -/* - * 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 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); + } +} diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 4e75dea003a4..ae4f9f9a95bd 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,165 +1,165 @@ - -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.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 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.rc b/plugins/SbieSupport/SbieSupport.rc index e2d61f7fcf25..82c4ae0686ab 100644 --- a/plugins/SbieSupport/SbieSupport.rc +++ b/plugins/SbieSupport/SbieSupport.rc @@ -1,146 +1,146 @@ -// 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" +///////////////////////////////////////////////////////////////////////////// +#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 + diff --git a/plugins/SbieSupport/SbieSupport.vcxproj b/plugins/SbieSupport/SbieSupport.vcxproj index 413788b0c8cd..08a3a4d34cab 100644 --- a/plugins/SbieSupport/SbieSupport.vcxproj +++ b/plugins/SbieSupport/SbieSupport.vcxproj @@ -1,65 +1,65 @@ - - - - - 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 - - - - - - - - - - - - - - - - - + + + + + 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 index bbd753b09245..a355a0acfd99 100644 --- a/plugins/SbieSupport/main.c +++ b/plugins/SbieSupport/main.c @@ -1,555 +1,555 @@ -/* - * 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; -} +/* + * 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 index 343b05f2006b..b338721498ca 100644 --- a/plugins/SbieSupport/resource.h +++ b/plugins/SbieSupport/resource.h @@ -1,18 +1,18 @@ -//{{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 +//{{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 index 073349f2e7b8..fab2af6674ac 100644 --- a/plugins/SbieSupport/sbiedll.h +++ b/plugins/SbieSupport/sbiedll.h @@ -1,42 +1,42 @@ -#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 +#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..b0862532cf6f 100644 --- a/plugins/ToolStatus/ToolStatus.rc +++ b/plugins/ToolStatus/ToolStatus.rc @@ -1,277 +1,277 @@ -// 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, 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 + diff --git a/plugins/ToolStatus/ToolStatus.vcxproj b/plugins/ToolStatus/ToolStatus.vcxproj index 08a9206043e7..b92b848af210 100644 --- a/plugins/ToolStatus/ToolStatus.vcxproj +++ b/plugins/ToolStatus/ToolStatus.vcxproj @@ -1,116 +1,116 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index fc78e31d370b..6b7d51ef4cdf 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -1,633 +1,633 @@ -/* - * 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 - ); +/* + * 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 diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index 73f7be904bd5..f1b906bd17c5 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -1,943 +1,943 @@ -/* - * 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 - ); +/* + * 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 diff --git a/plugins/ToolStatus/filter.c b/plugins/ToolStatus/filter.c index 70dc09f3febb..3e89fa21b0d3 100644 --- a/plugins/ToolStatus/filter.c +++ b/plugins/ToolStatus/filter.c @@ -1,670 +1,670 @@ -/* - * 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 (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; +} diff --git a/plugins/ToolStatus/graph.c b/plugins/ToolStatus/graph.c index 856838fb1eb2..c8effcd4396e 100644 --- a/plugins/ToolStatus/graph.c +++ b/plugins/ToolStatus/graph.c @@ -1,662 +1,662 @@ -/* - * 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; - } +/* + * 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 diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 6f3c1f8c530d..8bd024e7203a 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -1,1445 +1,1445 @@ -/* - * 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; +/* + * 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 diff --git a/plugins/ToolStatus/options.c b/plugins/ToolStatus/options.c index 4245130e9ef9..f46e5b5564ac 100644 --- a/plugins/ToolStatus/options.c +++ b/plugins/ToolStatus/options.c @@ -1,98 +1,98 @@ -/* - * 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 - ); +/* + * 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 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/searchbox.c b/plugins/ToolStatus/searchbox.c index 4cd68dadd33b..1c62529daddf 100644 --- a/plugins/ToolStatus/searchbox.c +++ b/plugins/ToolStatus/searchbox.c @@ -1,48 +1,48 @@ -/* - * 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 - )) - { - PhCreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); - return TRUE; - } - - return FALSE; +/* + * 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 + )) + { + PhCreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); + return TRUE; + } + + return FALSE; } \ No newline at end of file diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index 7bd302f877ed..92a8515eb812 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -1,581 +1,581 @@ -/* - * 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]); - } +/* + * 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 diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index d5609cda8848..67fb6176bab4 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -1,852 +1,852 @@ -/* - * 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 = 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; - - 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); +/* + * 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 = 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; + + 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 diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 45b08fd9f807..0bc63b8f4fd1 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -1,382 +1,382 @@ -/* - * 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 - -#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 - ); - +/* + * 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 + +#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 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.vcxproj b/plugins/Updater/Updater.vcxproj index 2e06be53bac9..0cacd9bc1f76 100644 --- a/plugins/Updater/Updater.vcxproj +++ b/plugins/Updater/Updater.vcxproj @@ -1,100 +1,100 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + + + \ 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..5510b557d445 100644 --- a/plugins/UserNotes/UserNotes.rc +++ b/plugins/UserNotes/UserNotes.rc @@ -1,182 +1,182 @@ -// 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, 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 + diff --git a/plugins/UserNotes/UserNotes.vcxproj b/plugins/UserNotes/UserNotes.vcxproj index 2e4924d7bd4e..45cec0ecac59 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.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) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/UserNotes/db.c b/plugins/UserNotes/db.c index 1412d51cfcbb..3e96290eb91e 100644 --- a/plugins/UserNotes/db.c +++ b/plugins/UserNotes/db.c @@ -1,447 +1,447 @@ -/* - * 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 + ) +{ + 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; +} diff --git a/plugins/UserNotes/db.h b/plugins/UserNotes/db.h index bfae0318271e..bf8018c4b9f2 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 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..e0f3f0278e08 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -1,1892 +1,1892 @@ -/* - * 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; +/* + * 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 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/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..0511d3ff6a8f 100644 --- a/plugins/WindowExplorer/WindowExplorer.rc +++ b/plugins/WindowExplorer/WindowExplorer.rc @@ -1,308 +1,308 @@ -// 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_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 + diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj b/plugins/WindowExplorer/WindowExplorer.vcxproj index bf5b3e58f4a0..f61d6d998529 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj @@ -1,94 +1,94 @@ - - - - - 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.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) + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/WindowExplorer/hook.c b/plugins/WindowExplorer/hook.c index 75d6861b84aa..abecae533993 100644 --- a/plugins/WindowExplorer/hook.c +++ b/plugins/WindowExplorer/hook.c @@ -1,509 +1,509 @@ -/* - * 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; -} +/* + * 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..c3cc7af784de 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -1,325 +1,325 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/plugins/WindowExplorer/resource.h b/plugins/WindowExplorer/resource.h index 055357f62d44..29511b8b8ae5 100644 --- a/plugins/WindowExplorer/resource.h +++ b/plugins/WindowExplorer/resource.h @@ -1,82 +1,82 @@ -//{{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_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 diff --git a/plugins/WindowExplorer/utils.c b/plugins/WindowExplorer/utils.c index d9b6cfb5dfdb..55baeb6161c1 100644 --- a/plugins/WindowExplorer/utils.c +++ b/plugins/WindowExplorer/utils.c @@ -1,104 +1,104 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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); + } +} diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index 189d52ea3ea9..baa2117ac900 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -1,1193 +1,1193 @@ -/* - * 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); - - PhCreateSearchControl(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); - - PhCreateSearchControl(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; +/* + * 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); + + PhCreateSearchControl(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); + + PhCreateSearchControl(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 diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index 05c330d571b8..6be77e09186f 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -1,135 +1,135 @@ -#ifndef WNDEXP_H -#define WNDEXP_H - -#include -#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 "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 diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index aa71849b3bc5..515b17ee9c40 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -1,1210 +1,1210 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; +} diff --git a/plugins/WindowExplorer/wndtree.c b/plugins/WindowExplorer/wndtree.c index db75a7180a75..a18b545b90e5 100644 --- a/plugins/WindowExplorer/wndtree.c +++ b/plugins/WindowExplorer/wndtree.c @@ -1,572 +1,572 @@ -/* - * 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); +/* + * 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 diff --git a/plugins/WindowExplorer/wndtree.h b/plugins/WindowExplorer/wndtree.h index 3157c4d2bd73..e1d3882174f6 100644 --- a/plugins/WindowExplorer/wndtree.h +++ b/plugins/WindowExplorer/wndtree.h @@ -1,98 +1,98 @@ -#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_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 diff --git a/plugins/include/toolstatusintf.h b/plugins/include/toolstatusintf.h index c185b16df030..9f366f928e30 100644 --- a/plugins/include/toolstatusintf.h +++ b/plugins/include/toolstatusintf.h @@ -1,49 +1,49 @@ -#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 + ); + +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 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/tests/phlib-test/main.c b/tests/phlib-test/main.c index 867a5b376909..c6799fbfafeb 100644 --- a/tests/phlib-test/main.c +++ b/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/tests/phlib-test/phlib-test.vcxproj index 50fa90fcf171..aa1ab1641568 100644 --- a/tests/phlib-test/phlib-test.vcxproj +++ b/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/t_basesup.c b/tests/phlib-test/t_basesup.c index 2bd2fe06c060..f32d49d9ecd4 100644 --- a/tests/phlib-test/t_basesup.c +++ b/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/tests/phlib-test/t_format.c index 1e9302e2a46e..825f7f609b8e 100644 --- a/tests/phlib-test/t_format.c +++ b/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/tests.h b/tests/phlib-test/tests.h index 8d1fd9b5b872..9e65f73be328 100644 --- a/tests/phlib-test/tests.h +++ b/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 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/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/include/peview.h b/tools/peview/include/peview.h index 2ac8644b65e4..bcb62f34d4ce 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -1,39 +1,39 @@ -#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 +#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 diff --git a/tools/peview/libprp.c b/tools/peview/libprp.c index 89d473b9103e..b7ee42316c31 100644 --- a/tools/peview/libprp.c +++ b/tools/peview/libprp.c @@ -1,181 +1,181 @@ -/* - * 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; + 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; +} diff --git a/tools/peview/main.c b/tools/peview/main.c index 811f604359e2..d3e580a7ec82 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -1,128 +1,128 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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; +} diff --git a/tools/peview/misc.c b/tools/peview/misc.c index c1e3acb2fbea..fc036c831520 100644 --- a/tools/peview/misc.c +++ b/tools/peview/misc.c @@ -1,102 +1,102 @@ -/* - * 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 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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; + } + } +} diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 22512aeac285..1908df441a52 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -1,1408 +1,1408 @@ -/* - * 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_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; - 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 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_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; + 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; +} 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 28530525caa9..d8c1288b8a65 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -1,240 +1,240 @@ -// 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,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,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, 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,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,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 + diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index d9119eb82275..29c125003e18 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -1,221 +1,221 @@ - - - - - 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.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 + + + + + + + + \ No newline at end of file diff --git a/tools/peview/resource.h b/tools/peview/resource.h index b300f449de11..aa0350eaa2d3 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -1,40 +1,40 @@ -//{{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 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 diff --git a/tools/peview/version.rc b/tools/peview/version.rc index 3e12095c909a..1e412bb16d5e 100644 --- a/tools/peview/version.rc +++ b/tools/peview/version.rc @@ -1,35 +1,35 @@ -#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 +#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 From 648b0ffe466513ea8d96a5c267e32ddb3af9f9e0 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 19:30:01 +1000 Subject: [PATCH 023/839] Update git attributes --- .gitattributes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index b895d0391026..961777c2611a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files -* text=crlf +* text=auto # Custom for Visual Studio *.cs diff=csharp @@ -16,6 +16,7 @@ *.cmd eol=crlf *.csproj eol=crlf *.h eol=crlf +*.md eol=crlf *.manifest eol=crlf *.rc eol=crlf *.sln eol=crlf From 0c8667c4076b88ceca2ae8d62f30700c6a440a03 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Apr 2017 22:43:02 +1000 Subject: [PATCH 024/839] CustomBuildTool: Add debug build support, Fix build_debug.cmd script --- build/build_binaries.cmd | 3 +- build/build_debug.cmd | 2 +- build/build_release.cmd | 16 +- build/build_sdk.cmd | 2 +- build/build_sdk_rebuild.cmd | 2 +- tools/CustomBuildTool/CustomBuildTool.csproj | 2 - tools/CustomBuildTool/Source Files/Build.cs | 187 ++++++++++++------ tools/CustomBuildTool/Source Files/Program.cs | 150 +++++++++----- .../bin/Release/CustomBuildTool.exe | Bin 143360 -> 145920 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 62976 bytes 10 files changed, 235 insertions(+), 129 deletions(-) diff --git a/build/build_binaries.cmd b/build/build_binaries.cmd index 8c42e4bbe1b1..2d17641e43e2 100644 --- a/build/build_binaries.cmd +++ b/build/build_binaries.cmd @@ -1,5 +1,6 @@ @echo off -..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -bin +start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-bin" +start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-exe" pause diff --git a/build/build_debug.cmd b/build/build_debug.cmd index 7f3fbb6d6035..7a06cbedd774 100644 --- a/build/build_debug.cmd +++ b/build/build_debug.cmd @@ -1,5 +1,5 @@ @echo off -..\tools\CustomBuildTool\bin\Release\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 15e528690fb6..c0a55c13da5d 100644 --- a/build/build_release.cmd +++ b/build/build_release.cmd @@ -1,17 +1,5 @@ @echo off - 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% +start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-release" -..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -release - -pause +pause \ No newline at end of file diff --git a/build/build_sdk.cmd b/build/build_sdk.cmd index 11e16deb7d05..0bed04835688 100644 --- a/build/build_sdk.cmd +++ b/build/build_sdk.cmd @@ -1,3 +1,3 @@ @echo off -..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -sdk \ No newline at end of file +start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-sdk" \ No newline at end of file diff --git a/build/build_sdk_rebuild.cmd b/build/build_sdk_rebuild.cmd index bc9493c504d5..57ead1eee13e 100644 --- a/build/build_sdk_rebuild.cmd +++ b/build/build_sdk_rebuild.cmd @@ -1,5 +1,5 @@ @echo off -..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -cleansdk +start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleansdk" pause diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index 08d1323194e2..b981e8ec1f99 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -89,11 +89,9 @@ - Designer - diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 6819e256a7bf..00a9b2533dbb 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -273,12 +273,20 @@ public static bool CopyTextFiles() return true; } - public static bool CopyKProcessHacker() + public static bool CopyKProcessHacker(bool DebugBuild) { try { - File.Copy("KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", "bin\\Release32\\kprocesshacker.sys", true); - File.Copy("KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", "bin\\Release64\\kprocesshacker.sys", true); + if (DebugBuild) + { + File.Copy("KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", "bin\\Debug32\\kprocesshacker.sys", true); + File.Copy("KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", "bin\\Debug64\\kprocesshacker.sys", true); + } + else + { + File.Copy("KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", "bin\\Release32\\kprocesshacker.sys", true); + File.Copy("KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", "bin\\Release64\\kprocesshacker.sys", true); + } } catch (Exception ex) { @@ -289,12 +297,20 @@ public static bool CopyKProcessHacker() return true; } - public static bool CopyLibFiles() + public static bool CopyLibFiles(bool DebugBuild) { try { - File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); - File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + if (DebugBuild) + { + File.Copy("bin\\Debug32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + File.Copy("bin\\Debug64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + } + else + { + File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + } } catch (Exception ex) { @@ -305,20 +321,34 @@ public static bool CopyLibFiles() return true; } - public static bool CopyWow64Files() + public static bool CopyWow64Files(bool DebugBuild) { try { - if (!Directory.Exists("bin\\Release64\\x86")) - Directory.CreateDirectory("bin\\Release64\\x86"); - - if (!Directory.Exists("bin\\Release64\\x86\\plugins")) - Directory.CreateDirectory("bin\\Release64\\x86\\plugins"); - - File.Copy("bin\\Release32\\ProcessHacker.exe", "bin\\Release64\\x86\\ProcessHacker.exe", true); - File.Copy("bin\\Release32\\ProcessHacker.pdb", "bin\\Release64\\x86\\ProcessHacker.pdb", true); - File.Copy("bin\\Release32\\plugins\\DotNetTools.dll", "bin\\Release64\\x86\\plugins\\DotNetTools.dll", true); - File.Copy("bin\\Release32\\plugins\\DotNetTools.pdb", "bin\\Release64\\x86\\plugins\\DotNetTools.pdb", true); + if (DebugBuild) + { + if (!Directory.Exists("bin\\Debug64\\x86")) + Directory.CreateDirectory("bin\\Debug64\\x86"); + if (!Directory.Exists("bin\\Debug64\\x86\\plugins")) + Directory.CreateDirectory("bin\\Debug64\\x86\\plugins"); + + File.Copy("bin\\Debug32\\ProcessHacker.exe", "bin\\Debug64\\x86\\ProcessHacker.exe", true); + File.Copy("bin\\Debug32\\ProcessHacker.pdb", "bin\\Debug64\\x86\\ProcessHacker.pdb", true); + File.Copy("bin\\Debug32\\plugins\\DotNetTools.dll", "bin\\Debug64\\x86\\plugins\\DotNetTools.dll", true); + File.Copy("bin\\Debug32\\plugins\\DotNetTools.pdb", "bin\\Debug64\\x86\\plugins\\DotNetTools.pdb", true); + } + 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"); + + File.Copy("bin\\Release32\\ProcessHacker.exe", "bin\\Release64\\x86\\ProcessHacker.exe", true); + File.Copy("bin\\Release32\\ProcessHacker.pdb", "bin\\Release64\\x86\\ProcessHacker.pdb", true); + File.Copy("bin\\Release32\\plugins\\DotNetTools.dll", "bin\\Release64\\x86\\plugins\\DotNetTools.dll", true); + File.Copy("bin\\Release32\\plugins\\DotNetTools.pdb", "bin\\Release64\\x86\\plugins\\DotNetTools.pdb", true); + } } catch (Exception ex) { @@ -412,7 +442,7 @@ public static bool FixupResourceHeader() return true; } - public static bool BuildKphSignatureFile() + public static bool BuildKphSignatureFile(bool DebugBuild) { string output; @@ -428,50 +458,73 @@ public static bool BuildKphSignatureFile() return true; } - if (!File.Exists("bin\\Release32\\ProcessHacker.exe")) + if (DebugBuild) { - Program.PrintColorMessage("[SKIPPED] Release32\\ProcessHacker.exe not found.", ConsoleColor.Yellow); - return true; - } + if (!File.Exists("bin\\Debug32\\ProcessHacker.exe")) + { + Program.PrintColorMessage("[SKIPPED] Debug32\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + return true; + } - if (!File.Exists("bin\\Release64\\ProcessHacker.exe")) - { - Program.PrintColorMessage("[SKIPPED] Release64\\ProcessHacker.exe not found.", ConsoleColor.Yellow); - return true; - } + if (!File.Exists("bin\\Debug64\\ProcessHacker.exe")) + { + Program.PrintColorMessage("[SKIPPED] Debug64\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + return true; + } - if (File.Exists("bin\\Debug32\\ProcessHacker.sig")) - File.Delete("bin\\Debug32\\ProcessHacker.sig"); - if (File.Exists("bin\\Debug64\\ProcessHacker.sig")) - File.Delete("bin\\Debug64\\ProcessHacker.sig"); - if (File.Exists("bin\\Release32\\ProcessHacker.sig")) - File.Delete("bin\\Release32\\ProcessHacker.sig"); - if (File.Exists("bin\\Release64\\ProcessHacker.sig")) - File.Delete("bin\\Release64\\ProcessHacker.sig"); + if (File.Exists("bin\\Debug32\\ProcessHacker.sig")) + File.Delete("bin\\Debug32\\ProcessHacker.sig"); + if (File.Exists("bin\\Debug64\\ProcessHacker.sig")) + File.Delete("bin\\Debug64\\ProcessHacker.sig"); - File.Create("bin\\Release32\\ProcessHacker.sig").Dispose(); - File.Create("bin\\Release64\\ProcessHacker.sig").Dispose(); + File.Create("bin\\Debug32\\ProcessHacker.sig").Dispose(); + File.Create("bin\\Debug64\\ProcessHacker.sig").Dispose(); - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"))) - { - //Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow); - } + if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"))) + { + Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow); + return false; + } - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"))) - { - //Program.PrintColorMessage("[WARN] (Debug64) " + output, ConsoleColor.Yellow); + if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"))) + { + Program.PrintColorMessage("[WARN] (Debug64) " + output, ConsoleColor.Yellow); + return false; + } } - - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"))) + else { - Program.PrintColorMessage("[ERROR] (Release32) " + output, ConsoleColor.Red); - return false; - } + if (!File.Exists("bin\\Release32\\ProcessHacker.exe")) + { + Program.PrintColorMessage("[SKIPPED] Release32\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + return true; + } - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"))) - { - Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red); - return false; + if (!File.Exists("bin\\Release64\\ProcessHacker.exe")) + { + Program.PrintColorMessage("[SKIPPED] Release64\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + return true; + } + + if (File.Exists("bin\\Release32\\ProcessHacker.sig")) + File.Delete("bin\\Release32\\ProcessHacker.sig"); + if (File.Exists("bin\\Release64\\ProcessHacker.sig")) + File.Delete("bin\\Release64\\ProcessHacker.sig"); + + File.Create("bin\\Release32\\ProcessHacker.sig").Dispose(); + File.Create("bin\\Release64\\ProcessHacker.sig").Dispose(); + + if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"))) + { + Program.PrintColorMessage("[ERROR] (Release32) " + output, ConsoleColor.Red); + return false; + } + + if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"))) + { + Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red); + return false; + } } return true; @@ -521,12 +574,12 @@ public static bool BuildSetupExe() { try { + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", false, false, false)) + return false; + if (File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) File.Delete(BuildOutputFolder + "\\processhacker-build-setup.exe"); - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", false, false)) - return false; - File.Move( "tools\\CustomSetupTool\\CustomSetupTool\\bin\\Release32\\CustomSetupTool.exe", BuildOutputFolder + "\\processhacker-build-setup.exe" @@ -775,7 +828,7 @@ public static void WebServiceUpdateConfig() } } - public static void AppveyorUploadBuildFiles() + public static bool AppveyorUploadBuildFiles() { string[] buildFileArray = { @@ -791,7 +844,7 @@ public static void AppveyorUploadBuildFiles() }; if (!BuildNightly) - return; + return false; for (int i = 0; i < releaseFileArray.Length; i++) { @@ -804,6 +857,7 @@ public static void AppveyorUploadBuildFiles() catch (Exception ex) { Program.PrintColorMessage("[WebServiceUploadBuild] " + ex, ConsoleColor.Red); + return false; } } } @@ -819,6 +873,7 @@ public static void AppveyorUploadBuildFiles() catch (Exception ex) { Program.PrintColorMessage("[WebServiceUploadBuild] " + ex, ConsoleColor.Red); + return false; } } } @@ -833,14 +888,16 @@ public static void AppveyorUploadBuildFiles() { Win32.ExecCommand("appveyor", "PushArtifact " + releaseFileArray[i]); } - catch (Exception) + catch (Exception ex) { - + Program.PrintColorMessage("[WebServicePushArtifact] " + ex, ConsoleColor.Red); + return false; } } } - } + return true; + } public static bool UpdateHeaderFileVersion() { @@ -884,7 +941,7 @@ public static bool BuildPublicHeaderFiles() return true; } - public static bool BuildSolution(string Solution, bool AllPlatforms, bool Verbose) + public static bool BuildSolution(string Solution, bool Build64bit, bool BuildDebug, bool Verbose) { if (Verbose) { @@ -895,7 +952,7 @@ public static bool BuildSolution(string Solution, bool AllPlatforms, bool Verbos string error32 = Win32.ExecCommand( MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=Release /p:Platform=Win32 " + Solution + "/m /nologo /verbosity:quiet /p:Configuration=" + (BuildDebug ? "Debug" : "Release") + " /p:Platform=Win32 " + Solution ); if (!string.IsNullOrEmpty(error32)) @@ -904,7 +961,7 @@ public static bool BuildSolution(string Solution, bool AllPlatforms, bool Verbos return false; } - if (AllPlatforms) + if (Build64bit) { Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false); Program.PrintColorMessage("x64", ConsoleColor.Green, false); @@ -912,7 +969,7 @@ public static bool BuildSolution(string Solution, bool AllPlatforms, bool Verbos string error64 = Win32.ExecCommand( MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=Release /p:Platform=x64 " + Solution + "/m /nologo /verbosity:quiet /p:Configuration=" + (BuildDebug ? "Debug" : "Release") + " /p:Platform=x64 " + Solution ); if (!string.IsNullOrEmpty(error64)) diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 8774e49e97aa..49825dc49032 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -6,7 +6,6 @@ namespace CustomBuildTool public static class Program { public static Dictionary ProgramArgs; - public static bool BuildDebug = false; public static void PrintColorMessage(string Message, ConsoleColor Color, bool Newline = true) { @@ -23,7 +22,6 @@ public static void PrintColorMessage(string Message, ConsoleColor Color, bool Ne public static void Main(string[] args) { ProgramArgs = ParseArgs(args); - BuildDebug = ProgramArgs.ContainsKey("-debug"); if (ProgramArgs.ContainsKey("-cleanup")) { @@ -55,55 +53,57 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-sign")) { - Build.BuildKphSignatureFile(); + Build.BuildKphSignatureFile(false); return; } - else if (ProgramArgs.ContainsKey("-cleansdk")) + else if (ProgramArgs.ContainsKey("-sdk")) { + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; - if (!Build.BuildSolution("ProcessHacker.sln", true, true)) - return; - - PrintColorMessage("Copying SDK headers...", ConsoleColor.Cyan); + PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying version headers...", ConsoleColor.Cyan); + PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building sdk resource header...", ConsoleColor.Cyan); + PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - PrintColorMessage("Copying plugin linker files...", ConsoleColor.Cyan); - if (!Build.CopyLibFiles()) + PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); + if (!Build.CopyLibFiles(false)) return; Build.ShowBuildStats(false); return; } - else if (ProgramArgs.ContainsKey("-sdk")) + else if (ProgramArgs.ContainsKey("-cleansdk")) { + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Copying SDK headers...", ConsoleColor.Cyan); + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + return; + + PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying version headers...", ConsoleColor.Cyan); + PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building sdk resource header...", ConsoleColor.Cyan); + PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - PrintColorMessage("Copying plugin linker files...", ConsoleColor.Cyan); - if (!Build.CopyLibFiles()) + PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); + if (!Build.CopyLibFiles(false)) return; Build.ShowBuildStats(false); @@ -111,31 +111,44 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-bin")) { + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; Build.ShowBuildEnvironment(false); Build.BuildSecureFiles(); - if (!Build.BuildSolution("ProcessHacker.sln", true, true)) + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; - if (!Build.BuildKphSignatureFile()) + + PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + if (!Build.BuildKphSignatureFile(false)) return; + + PrintColorMessage("Copying text files...", ConsoleColor.Cyan); if (!Build.CopyTextFiles()) return; - if (!Build.CopyKProcessHacker()) + + PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); + if (!Build.CopyKProcessHacker(false)) return; + + PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; + + PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; + + PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles()) + if (!Build.CopyLibFiles(false)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) return; - if (!Build.CopyWow64Files()) + if (!Build.CopyWow64Files(false)) return; PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); @@ -143,26 +156,78 @@ public static void Main(string[] args) return; Build.ShowBuildStats(false); - return; } else if (ProgramArgs.ContainsKey("-exe")) { + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; + PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); if (!Build.BuildBinZip()) return; + PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); if (!Build.BuildSetupExe()) return; - Build.BuildSrcZip(); + PrintColorMessage("Building build-sdk.zip...", ConsoleColor.Cyan); Build.BuildSdkZip(); - return; + + PrintColorMessage("Building build-src.zip...", ConsoleColor.Cyan); + Build.BuildSrcZip(); } - else + else if (ProgramArgs.ContainsKey("-debug")) { + PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment(true); + Build.BuildSecureFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", true, true, true)) + return; + + PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + if (!Build.BuildKphSignatureFile(true)) + return; + + PrintColorMessage("Copying text files...", ConsoleColor.Cyan); + if (!Build.CopyTextFiles()) + return; + + PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); + if (!Build.CopyKProcessHacker(true)) + return; + + PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); + if (!Build.CopyPluginSdkHeaders()) + return; + + PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); + if (!Build.CopyVersionHeader()) + return; + + PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); + if (!Build.FixupResourceHeader()) + return; + + PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); + if (!Build.CopyLibFiles(true)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", true, true, true)) + return; + PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); + if (!Build.CopyWow64Files(true)) + return; + + Build.ShowBuildStats(true); + } + else // release + { if (!Build.InitializeBuildEnvironment(true)) return; @@ -170,11 +235,11 @@ public static void Main(string[] args) Build.ShowBuildEnvironment(true); Build.BuildSecureFiles(); - if (!Build.BuildSolution("ProcessHacker.sln", true, true)) + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); - if (!Build.BuildKphSignatureFile()) + if (!Build.BuildKphSignatureFile(false)) return; PrintColorMessage("Copying text files...", ConsoleColor.Cyan); @@ -182,40 +247,36 @@ public static void Main(string[] args) return; PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); - if (!Build.CopyKProcessHacker()) + if (!Build.CopyKProcessHacker(false)) return; - PrintColorMessage("Copying SDK headers...", ConsoleColor.Cyan); + PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying version headers...", ConsoleColor.Cyan); + PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building sdk resource header...", ConsoleColor.Cyan); + PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - PrintColorMessage("Copying plugin linker files...", ConsoleColor.Cyan); - if (!Build.CopyLibFiles()) + PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); + if (!Build.CopyLibFiles(false)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) return; PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); - if (!Build.CopyWow64Files()) + if (!Build.CopyWow64Files(false)) return; PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); if (!Build.BuildBinZip()) return; - //Build.BuildSrcZip(); - //Build.BuildSdkZip(); - //Build.BuildPdbZip(); - PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); if (!Build.BuildSetupExe()) return; @@ -228,8 +289,9 @@ public static void Main(string[] args) if (!Build.BuildUpdateSignature()) return; - Build.AppveyorUploadBuildFiles(); - Build.WebServiceUpdateConfig(); + if (Build.AppveyorUploadBuildFiles()) + Build.WebServiceUpdateConfig(); + Build.ShowBuildStats(true); } } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 29a6995bd64d0eb702065d554efad9fe67977c92..dd4aa7b45ed7243863358566d66f557b2cbd62bb 100644 GIT binary patch delta 14423 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 delta 13092 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 xtKPbBuN6~_crQ1HKYHL}O62 Date: Thu, 27 Apr 2017 23:39:21 +1000 Subject: [PATCH 025/839] CustomBuildTool: Add release builds, Fix build_release.cmd, Improve nightly builds --- appveyor.yml | 2 +- build/build_binaries.cmd | 6 - tools/CustomBuildTool/Source Files/Build.cs | 42 +++++- tools/CustomBuildTool/Source Files/Program.cs | 139 +++++++----------- .../bin/Release/CustomBuildTool.exe | Bin 145920 -> 146432 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 65024 bytes 6 files changed, 96 insertions(+), 93 deletions(-) delete mode 100644 build/build_binaries.cmd diff --git a/appveyor.yml b/appveyor.yml index 4ad9645b488a..1eb83fda0bb2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,7 @@ test: off # Run custom build script. build_script: -- cmd: tools\CustomBuildTool\bin\Release\CustomBuildTool.exe +- cmd: tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -appveyor # Setup build notifications. notifications: diff --git a/build/build_binaries.cmd b/build/build_binaries.cmd deleted file mode 100644 index 2d17641e43e2..000000000000 --- a/build/build_binaries.cmd +++ /dev/null @@ -1,6 +0,0 @@ -@echo off - -start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-bin" -start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-exe" - -pause diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 00a9b2533dbb..d88bfd38394f 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -181,6 +181,8 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) public static void CleanupBuildEnvironment() { + Program.PrintColorMessage("Cleaning up build environment...", ConsoleColor.Cyan); + try { @@ -193,6 +195,8 @@ public static void CleanupBuildEnvironment() public static void ShowBuildEnvironment(bool ShowBuildInfo) { + Program.PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); + string currentGitTag = Win32.ExecCommand(GitExePath, "describe --abbrev=0 --tags --always"); string latestGitRevision = Win32.ExecCommand(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); @@ -257,6 +261,8 @@ public static void ShowBuildStats(bool block) public static bool CopyTextFiles() { + Program.PrintColorMessage("Copying text files...", ConsoleColor.Cyan); + try { File.Copy("README.md", "bin\\README.txt", true); @@ -275,6 +281,8 @@ public static bool CopyTextFiles() public static bool CopyKProcessHacker(bool DebugBuild) { + Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); + try { if (DebugBuild) @@ -299,6 +307,8 @@ public static bool CopyKProcessHacker(bool DebugBuild) public static bool CopyLibFiles(bool DebugBuild) { + Program.PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); + try { if (DebugBuild) @@ -323,6 +333,8 @@ public static bool CopyLibFiles(bool DebugBuild) public static bool CopyWow64Files(bool DebugBuild) { + Program.PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); + try { if (DebugBuild) @@ -361,6 +373,8 @@ public static bool CopyWow64Files(bool DebugBuild) public static bool CopyPluginSdkHeaders() { + Program.PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); + try { foreach (string folder in sdk_directories) @@ -400,6 +414,8 @@ public static bool CopyPluginSdkHeaders() public static bool CopyVersionHeader() { + Program.PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); + try { HeaderGen gen = new HeaderGen(); @@ -422,6 +438,8 @@ public static bool CopyVersionHeader() public static bool FixupResourceHeader() { + Program.PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); + try { string phappContent = File.ReadAllText("sdk\\include\\phappresource.h"); @@ -446,6 +464,8 @@ public static bool BuildKphSignatureFile(bool DebugBuild) { string output; + Program.PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + if (!File.Exists(CustomSignToolPath)) { Program.PrintColorMessage("[SKIPPED] CustomSignTool not found.", ConsoleColor.Yellow); @@ -572,11 +592,13 @@ public static bool BuildSecureFiles() public static bool BuildSetupExe() { + Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); + + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", false, false, false)) + return false; + try { - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", false, false, false)) - return false; - if (File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) File.Delete(BuildOutputFolder + "\\processhacker-build-setup.exe"); @@ -596,6 +618,8 @@ public static bool BuildSetupExe() public static bool BuildSdkZip() { + Program.PrintColorMessage("Building build-sdk.zip...", ConsoleColor.Cyan); + try { if (File.Exists(BuildOutputFolder + "\\processhacker-build-sdk.zip")) @@ -614,6 +638,8 @@ public static bool BuildSdkZip() public static bool BuildBinZip() { + Program.PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); + try { if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) @@ -643,6 +669,8 @@ public static bool BuildBinZip() public static bool BuildSrcZip() { + Program.PrintColorMessage("Building build-src.zip...", ConsoleColor.Cyan); + if (!File.Exists(GitExePath)) { Program.PrintColorMessage("[SKIPPED] Git not installed.", ConsoleColor.Yellow); @@ -702,6 +730,8 @@ public static bool BuildPdbZip() public static bool BuildChecksumsFile() { + Program.PrintColorMessage("Building build-checksums.txt...", ConsoleColor.Cyan); + try { if (File.Exists(BuildOutputFolder + "\\processhacker-build-checksums.txt")) @@ -735,6 +765,8 @@ public static bool BuildChecksumsFile() public static bool BuildUpdateSignature() { + Program.PrintColorMessage("Building release signature...", ConsoleColor.Cyan); + if (!File.Exists(CustomSignToolPath)) { Program.PrintColorMessage("[SKIPPED] CustomSignTool not found.", ConsoleColor.Yellow); @@ -901,6 +933,8 @@ public static bool AppveyorUploadBuildFiles() public static bool UpdateHeaderFileVersion() { + Program.PrintColorMessage("Updating Process Hacker build revision...", ConsoleColor.Cyan); + try { if (File.Exists("ProcessHacker\\include\\phapprev.h")) @@ -926,6 +960,8 @@ public static bool UpdateHeaderFileVersion() public static bool BuildPublicHeaderFiles() { + Program.PrintColorMessage("Building public SDK headers...", ConsoleColor.Cyan); + try { HeaderGen gen = new HeaderGen(); diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 49825dc49032..18ec017a966b 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -28,7 +28,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Cleanup build...", ConsoleColor.Cyan); + Build.CleanupBuildEnvironment(); return; } @@ -37,7 +37,6 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Updating header version...", ConsoleColor.Cyan); Build.ShowBuildEnvironment(false); Build.UpdateHeaderFileVersion(); return; @@ -47,7 +46,6 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Building public headers...", ConsoleColor.Cyan); Build.BuildPublicHeaderFiles(); return; } @@ -58,23 +56,18 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-sdk")) { - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); if (!Build.CopyLibFiles(false)) return; @@ -83,26 +76,21 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-cleansdk")) { - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; - - PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); + if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); if (!Build.CopyLibFiles(false)) return; @@ -111,7 +99,6 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-bin")) { - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(false)) return; @@ -120,66 +107,41 @@ public static void Main(string[] args) if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; - - PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + if (!Build.BuildKphSignatureFile(false)) return; - PrintColorMessage("Copying text files...", ConsoleColor.Cyan); if (!Build.CopyTextFiles()) return; - PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); if (!Build.CopyKProcessHacker(false)) return; - - PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); + if (!Build.CopyPluginSdkHeaders()) return; - PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; + if (!Build.CopyLibFiles(false)) return; + if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) return; + if (!Build.CopyWow64Files(false)) return; - - PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); + if (!Build.BuildBinZip()) return; Build.ShowBuildStats(false); } - else if (ProgramArgs.ContainsKey("-exe")) - { - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); - if (!Build.InitializeBuildEnvironment(false)) - return; - - PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); - if (!Build.BuildBinZip()) - return; - - PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); - if (!Build.BuildSetupExe()) - return; - - PrintColorMessage("Building build-sdk.zip...", ConsoleColor.Cyan); - Build.BuildSdkZip(); - - PrintColorMessage("Building build-src.zip...", ConsoleColor.Cyan); - Build.BuildSrcZip(); - } else if (ProgramArgs.ContainsKey("-debug")) { - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); if (!Build.InitializeBuildEnvironment(true)) return; @@ -189,110 +151,121 @@ public static void Main(string[] args) if (!Build.BuildSolution("ProcessHacker.sln", true, true, true)) return; - PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); if (!Build.BuildKphSignatureFile(true)) return; - - PrintColorMessage("Copying text files...", ConsoleColor.Cyan); if (!Build.CopyTextFiles()) return; - - PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); if (!Build.CopyKProcessHacker(true)) return; - PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - - PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - - PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - - PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); if (!Build.CopyLibFiles(true)) return; if (!Build.BuildSolution("plugins\\Plugins.sln", true, true, true)) return; - PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); if (!Build.CopyWow64Files(true)) return; Build.ShowBuildStats(true); } - else // release + else if (ProgramArgs.ContainsKey("-release")) { - if (!Build.InitializeBuildEnvironment(true)) + if (!Build.InitializeBuildEnvironment(false)) return; - PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); - Build.ShowBuildEnvironment(true); + Build.ShowBuildEnvironment(false); Build.BuildSecureFiles(); if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; - PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); if (!Build.BuildKphSignatureFile(false)) return; - - PrintColorMessage("Copying text files...", ConsoleColor.Cyan); if (!Build.CopyTextFiles()) return; - - PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); if (!Build.CopyKProcessHacker(false)) return; - PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); if (!Build.CopyPluginSdkHeaders()) return; - - PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); if (!Build.CopyVersionHeader()) return; - - PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); if (!Build.FixupResourceHeader()) return; - - PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); if (!Build.CopyLibFiles(false)) return; if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) return; - PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); if (!Build.CopyWow64Files(false)) return; - PrintColorMessage("Building build-bin.zip...", ConsoleColor.Cyan); if (!Build.BuildBinZip()) return; + if (!Build.BuildSetupExe()) + return; + + Build.BuildSdkZip(); - PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); + Build.BuildSrcZip(); + } + else if (ProgramArgs.ContainsKey("-appveyor")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.ShowBuildEnvironment(false); + Build.BuildSecureFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + return; + + if (!Build.BuildKphSignatureFile(false)) + return; + if (!Build.CopyTextFiles()) + return; + if (!Build.CopyKProcessHacker(false)) + return; + + if (!Build.CopyPluginSdkHeaders()) + return; + if (!Build.CopyVersionHeader()) + return; + if (!Build.FixupResourceHeader()) + return; + if (!Build.CopyLibFiles(false)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) + return; + + if (!Build.CopyWow64Files(false)) + return; + + if (!Build.BuildBinZip()) + return; if (!Build.BuildSetupExe()) return; - PrintColorMessage("Building build-checksums.txt...", ConsoleColor.Cyan); if (!Build.BuildChecksumsFile()) return; - - PrintColorMessage("Building release signature...", ConsoleColor.Cyan); if (!Build.BuildUpdateSignature()) return; if (Build.AppveyorUploadBuildFiles()) Build.WebServiceUpdateConfig(); - - Build.ShowBuildStats(true); + } + else + { + Console.WriteLine("Invalid arguments.\n"); } } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index dd4aa7b45ed7243863358566d66f557b2cbd62bb..88f5b94a10f31fb1fa25c3bcd41f229babb8d33b 100644 GIT binary patch delta 15584 zcmch833wD$*7m9D?&>Yw>Fy-Gq&rDNNNBQ=up?_&6a;}7Q9&6%Kn4v2Z$LqoPQ*bO z#Su$U7G-q=1r_4P2nsqlI*Ov=h%zGR_{})Z#yF#>^XdPdTh&V#zWJa3dH&~Xo__Cn z&pG$pbCugC<%*d!h^E zsvJ>gq3-irHMmu}?agISN+Nor!g5w8idbiZWVAlSqK~weDJS*ot#zHZSj|*&4C9A3 zlY0yv(DWCqA$6#xzi%y5FV*z#tzq?AO%L0K=vUe*hAh%#$^lALqLn*Gpd+09JyIQk zx+($0En0LL$;F1*JC`bJW`*)oEk*owbOjj84yZzXWyaBug>sR<{LBo zAoJsCelqM5XZ6Rmc+x||##1;SnMG?cCaK0;j!a`}RcFx$T&xF}?Hy-6*qP#$al>P2 zS|g;#3u!J}MEhDIj(kgUjKvVt4{G+L;zP6Kp^Gj%PbTE*n8}6U;ue6J$wB7-N^@i~ z(B08vDf5iSzh$ETLi3zAha4fQSCP5&XCJRVNOOZWkY>2 z)M6#T%=p*LHBXvf3^6J0$tXU7C@Q=uhst^4W{^~aQZ^z#l%2XVa-R}LO^p5Hb@^W(KEY+^!lnBY_yPvt4&pj9NfY{ z)J?UpJePN+Vh)`v=Ci?TTq~gp@f>K%bgY9(?f((d6};3^4&H*Sy2%RB1fH_($hK7o zEohpmT!!2W&5L1KS}9Ad^z99MbDy+Rn#$zGa<0fbc$rJ?!_@^M)-A->K5w?E{sb!w z?@K~@gF8QY_B;-PS8^)nSU9Uc6=FGP%Jf1 z*~~RN_NTEh-JR4{W_D*8Eay1aM)mrfV3If3KVz1hnDXVAdTX_cIh`DZf0&1orR8YG zWiF#z(iWC77Pr`%wy>13lyFIOCeJ~Pnrb~w#dl`CAaY`r7pKD2|H!~$@++U$vC ze5KvVd>247_)+HhMKs?b`N@b!Tv?>|_0%O>(LuRqE`>F<&Sq%q;56csA^&=SBpHj@=7&kb#bTjrI67YDHx}wh`zv= zKZOs`F5bP{Q8nFHxvsxFZ?KG(gPpsidB?9RPHFYyzF0UjFWJVn5xd)?2iQccgCFxhiX^3CKf_o?$QtW^L(1Pt%E^7;4g}#bJm)NqDF_CS}a)~ z!+hg|38rZnj=A_^E2DgjcwViQRR$FNmEsOL zs5Il1x})3%o#B6=RNoTFaz=W%|{5*W^rQ2XqrVcA5U`ytsO6nf|A|t_267cQjuFgVU}hMzO;? zWqKmNi~5f;eO!JO7fbW|C6SIlVm{TJ(=9yT7@G9d<9Wf*q-SEQt7GNz@(!6_fOgYi zGPY5iw6x`?B4&&img^T4RH-w|^@Rl^xOl#xSIJt$wfpVs#V!bPK#a$dr$EIk<+?W* zR6i-#%Y&DXn~4GDOGyN~X69L32V!jOE<@&JFD0=HCi(a!MkZ3h<$0-yudoBb;t8n} zS)bul#LJ0qo)}u=aJU4OQp@RoI2@iV!r6|u3uE0D>1~$0%%&VEH=^>Rs1>UM3)pX~ z>MQmku$mapc^*k6R^krI2Uqbfx9T#Kq008R5|Q? zB+&nu>&Ef-g;40a|Jcw^Q0xD(q17E4(#!{79K6ozL&AyV9++GCVOhM$lH8=;Wr?rF z;?xs1-{Y`!#E=Ru!{Q2AMnsD&3&m<#M#WB9#>C6AEE50ZvK0?;){=Cr;gv?LMlO>G zHd`tp;wD)ZiaTT(6`N!k6HmypNF0`Bv3OsWB~I~$A-KeMhTs;tRUEq{N0b?YNAx!Y zuNZF#xnhPP_{2>RlJNG6)rKk{9yEkJvD*;x#UVo|5bqg6P<(0#A@Q{#goV3Wwi*#7 zhJd#Yh7c7O8A43VGK3=W6Bb(iJS1IK5606Dzrna)T0`P7S%$^SvW$rLWmzabmt|CZ zC(D>{*KmtPqLVC(#Q<4iNX8g~OH45Yx46L&a>Q+h;1TN#!7FyxP?Co$SG;JbeBy0G z@QV|M5DQR21SdwNJg58#8lfYJ|*=(@; z(rgykwp#tO=msTMZ;MS;eER8Fv*OnqiiU;uz^(Zjgs&>K`)y0cA@iWvQ>#B(R1!Lz zw#75H!?pUUqJv7HeM|APiaNJepBx|5?LGEvMHGiP0nzVTl7~QYYmz4=)AnAielY%o z=To@g!Go--__S7E+9~XTw_jZngbD+pZ|O8Z$=5&XG*v0k2X($e3F`NCZm@rbTH=e^ z_CI&_C`lVl!nP3NYKfb%&$ia1?s5ikhI84>J<1IxFpOHYQjx#|j8H(%O~@1xRTG&j^c?coR}aGOk5txp^3GF9;$B{RVxOqOn=)DM;g z-DA=f>h-J2BH_e;aQ043IrqOEcNqOlSy|F(p8JV20Y?ueG_#riAyP1bgD-oEIS3aD zhKz?DuIVsqVuv(BVW3lEwL`oHB6>X<_)okJ*dCTqC@OR zlS+r!nNK70EU55uwL=Kxo}Tg?f2Du3i~uFtdjt>V9X(fnvLdgrQ&X@0A`q^muUj5i27 zVDMYrl@<0X58lec0ds*Mm4%w&uo)EolEBLT&d`+}r{k_UJvQRSzL5wHnS|$jzbcg8 zNc2&7#eH3gS{{SsO6&r$&P zaoC%m<#5i#b0{!g`X`mwM6Tyt&+=KWnufXAEi}5bUEj~!A zRFT_<-v;~&c9*6G6L%pcwlvm@|uX$3v; zc1V>sO|AK`KDo9?c~f6hTdW+_@2@S&e;ZEu-&-n*EzR#h~L=Kt)ZeNu*$29bjr8h#;zdTK z7fl>J0_Ab=tM!lSx<=-L72|s)zSva=I|jd2r}y=u9`(s@Jh+eIqtS~wO+k$sJ&gO@ zHNjkZkvAzi?S4I=Q8I^dn#2n{ENc==B<4!}5%(pit4ka%H5EBGdNsPib1P{4sE!n%S!!rARM>Eop_$`sTcu5jdm5*N9ff&uzT!Js^izW05l2I-ARO)#m^ zlR50POeS%si+iv|_F${b^y3`XERp)NQh%#j6%NqfayA75v=OM$s|6h4QePc)jpllJ6icFsd>2&%H5%dD8rJA02SVnd8*(9%v0nr?u1i-HWzFP1nJ4V8uWW-K38}~V%H#t_)D0vUiSE;oPsG)*36du zzR=ITZ<6>cJIj-#JYV{M8(_~r2RP;$`&ViqJ(sjE3>4FA;e~-h+8$eohOhGR5Dh}_ zd0AR;`orYoUz?IoFKf$uBPCOKu{)XNQ4|^<-Qi~zq!>JX^gc0*Q5RV+su--C6s-1L z_GP}^6sJlsJTUcgGp%Ln^Ua!f)?%!ECcqXD#;!TX0tm?TdlM$MHB4VQ?g-* z$*h-T{m>w@K9cn+FxX&bNs61sq0)Gb8D^AZ6HVJB$@aO7iqmC(XGM)fXUP7#W$){0 zkz^Gywhf~tviB$R42HK77$-MkvaL;eT0;5D^u|8*$;Rjo%}?WEyMV8G+sIGfO+Y+z*Ga*xZZ-#XdmNb zE8{GQcScyA<9N&qf@{~ z=yTv2_YEa{K&ber@`jR z=11TNhxXT!FTk$9WD0h_G$wHCWY#@zB5eKoGFua-;9C5BN)8_`X=V7^0u3x*vNjT~ z?n(}gFl;oy>%;rJs|T=W8$BMHV)xSiG;6ZwQq@4VCEF-3yxbn3rGuERr0pIJ+t!4k zlF26U!>?p*crL!fo=49~ww7|ec$}tiJ$tStk9Uo|faaU*U2P(^JN~rN+W1|L`|UWD zn{2&(qa8s`BMT!~QZ=45g85YkwV3QY-j50B|yWTc1lH}Ok(p}`(8OE_QjALgQ$IdX0Eib!N zYz3E{{Jdgobbn@-SDmzdiviBC^2-YUm_-kx!Hn^~$X~N;Qyn)e8QW}kHcN#5k>%M# zpF1eppJyxM&$E?bJX;yYvz1|+^h1N9NuHLB?(@($S@ECnd~dHNzg)~K9XDb+Ybau} zTdY2?&L;c9-$31|!en3jePBIJ_B-^omIj#Y%UmDW2$QW&`WkTK8f((^I2P2=M3WtW ztp`mp*{@;iLD!n>5!iatJd^E(ttZ`NvU&_bFG`whe7+Ctc9Y$QM0?ZSCfnjq`asv2 zbPC1?CkK95 zZEMJGuX2o}4<%daxC+Z>Bz5nEGj#WRB{%$fmCMG$OzBMV&;Yd=)aL|>G zSEQ@UWWGGM1x(g}iX*AeWTldIHrdmXRhZ0zH8YZWn9S?0L!tu>hTXW2V3-#E$9THhWbZ3Kc1-9Hw1H-twi;z2uBwkr_6Ow-`=#_x z$&3xZu|vH^QpOu;Uqo#Vej-#z3o$Ou16pXXPybPUP@h+?C3``m(zrt0F6ad>9#iS8 zi#<<78Siv5z9{QH2p^SZ!)hV7)R*Iyl=A6rXL&xyrxmcO^q{n^aIj{BLxtvD>9YYE zmX)|dOOl0ZT%}f^g)Xs5RiT0k$8s6x1{lZ5%Dn?B8onmr_bGIjHG@NQ{k2k)#-X{! zZH=S3WM`i9a1!T3oLQj`-){$As7A8W^P zu-g3xs#7&Le(_O1_MAj2SG&&wS>|-Vr~axOM)iYSud+vu${y;EcR*|nE30S=cJ?*E z?n=GNa*jOjjaFaq)GPa>|311R8M#oIqz+7DVHj95cEozKetR*7OSIiCd;RL z3KuG`QIq{em{!RUPE`wUMw4?2e-3;q_Be2RA&?e zh1+gkbRlqe^k$_C9SkSGLovYm9b5-k6}6Xi9Vl@GD%7cCWrZ_JPv21WjPgM)59aSY zBh`Vj)vH*=$SeBx(A(mUE`A*ZRU;aGHy_DyF*s{^oEQ-Ev*>1{3g=bNV8~6qqAMI3y zn{k8-uQz~vALf4VlHJ%PJ$I^vX?J1+v;mt1C359&6wj45J^mi* z)?$V8jNWJcT)EZ033y}SHtXl=C$49$`_b373drw>UA>H3w z_bAowzgiVbOZcBwr^Oq)9Jtp4RFgfde;}tN6m;2MgR9S0r!LGZvN`d<>ax8JO;6hg zKs*{)KK2f(oKfHMTWMp6g*?XGZ$cvIF=BT>Y!m zZ!pwn6eaSsZI#-T{|n$8AQo)Wd%#v_;hFQRuZND>4xzCRZKr{sz+8booly>khpG*h zKRNycO~m`X?G3Z}Cf_7=jXJ3?S35>R8Dcp`eq|xn|L;5pY)!PwQ>x9eRJ*IRGs?1p z25Q0@8l*Y!x9OJy7tmF}MUqXv(d4+5jKA6QCOi#M2TLr;IGI-nk-)YSSfYlek6Vv>bM7mAF~rL5Zg% zl83Evi9;n$lh`V8v&4fEPe~-N^p`kP;xvh^z~pt>W=Rf8jQiMisKjX!TP1Fmcu?Xg ziR70-B@UH1O=7FW0rZeIh&F3OP}bwyi0?#vub^$(BzhS4+-+KSx`A%je>KLF)RYos zx$-mRUgde^Z;D-=ujW}}){)j3)|J*B*5lUitX*uQZR2bkZI9btvVChS(h6|siQq!O zQ-k$3oX=(j7)yMN4@rDe;xiH(^I89}pK*?tG2vjG>bL_qd$5}euZvZ0U&FkRbqi`xRd*5`kJwalJkANlzGZK$_L8lN{L#gR;m}MW7K`>X*I{v$95$UF;ZDPz`OW!C7WpC5pc$ z!(;GtAphffQ<2Inw4}JJWvjDUcCtJZHy1_kyS`XIy*`ojR6{XKCOexHf3*y0%v=lf zN?x@Y_C~P9NM4ZAx^IGXYeBR)4M*1n@~ngumkx1g79)>Y;GKbs(1!((SlJimu=fo0dv zbV|;xrJ9sEv+yopBEIeWH~w7lwagkdV)1A1jCtgm_sSnge%x1I@X$ByEt^(bhG=$+ zx5(?Y7X(_{RIk?=SBtzw7Ju6fxWhQC_!-sVwcAW1935W#eLi-?%MnExzXyzOEK2CILQH<;j_}RP_`C%9 j{yl$C!s=5;^x|DJS9l+P*88~f?JD)yp!P$*{Yv>i5xeR| delta 14797 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index f6216abb3bf3c1cdf615036245ef3bda716aa2b7..8020ab3344ec8af60c2fe10087ccab8d395c561e 100644 GIT binary patch delta 11474 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 delta 11269 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 Date: Thu, 27 Apr 2017 23:43:05 +1000 Subject: [PATCH 026/839] Update build binaries --- .../bin/Release/CustomBuildTool.exe | Bin 146432 -> 145408 bytes .../bin/Release/CustomBuildTool.pdb | Bin 65024 -> 62976 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 88f5b94a10f31fb1fa25c3bcd41f229babb8d33b..8f33b1499a7fd4f9a9e0be7dcd40113f12acb90f 100644 GIT binary patch delta 8323 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 8020ab3344ec8af60c2fe10087ccab8d395c561e..4af7deeb1bc7bfead1e9aaca9a28d36726076a59 100644 GIT binary patch delta 4078 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 delta 4376 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 Date: Fri, 28 Apr 2017 02:10:59 +1000 Subject: [PATCH 027/839] peview: Improve module import(s) enumeration --- phlib/include/mapimg.h | 2 +- phlib/mapimg.c | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index 306704dfb44c..608e652ce5a8 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -219,7 +219,7 @@ typedef struct _PH_MAPPED_IMAGE_IMPORT_DLL PIMAGE_IMPORT_DESCRIPTOR Descriptor; 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 diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 4366e633d1df..e093378073d2 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -949,11 +949,11 @@ NTSTATUS PhGetMappedImageImportDll( } else { - ImportDll->DelayDescriptor = &((PIMAGE_DELAYLOAD_DESCRIPTOR)Imports->DelayDescriptorTable)[Index]; + ImportDll->DelayDescriptor = &Imports->DelayDescriptorTable[Index]; ImportDll->Name = PhMappedImageRvaToVa( ImportDll->MappedImage, - ((PIMAGE_DELAYLOAD_DESCRIPTOR)ImportDll->DelayDescriptor)->DllNameRVA, + ImportDll->DelayDescriptor->DllNameRVA, NULL ); @@ -964,7 +964,7 @@ NTSTATUS PhGetMappedImageImportDll( ImportDll->LookupTable = PhMappedImageRvaToVa( ImportDll->MappedImage, - ((PIMAGE_DELAYLOAD_DESCRIPTOR)ImportDll->DelayDescriptor)->ImportNameTableRVA, + ImportDll->DelayDescriptor->ImportNameTableRVA, NULL ); } @@ -978,9 +978,9 @@ NTSTATUS PhGetMappedImageImportDll( if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - PULONG entry; + PIMAGE_THUNK_DATA32 entry; - entry = (PULONG)ImportDll->LookupTable; + entry = (PIMAGE_THUNK_DATA32)ImportDll->LookupTable; __try { @@ -989,10 +989,10 @@ NTSTATUS PhGetMappedImageImportDll( PhpMappedImageProbe( ImportDll->MappedImage, entry, - sizeof(ULONG) + sizeof(IMAGE_THUNK_DATA32) ); - if (*entry == 0) + if (entry->u1.AddressOfData == 0) break; entry++; @@ -1006,9 +1006,9 @@ NTSTATUS PhGetMappedImageImportDll( } else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - PULONG64 entry; + PIMAGE_THUNK_DATA64 entry; - entry = (PULONG64)ImportDll->LookupTable; + entry = (PIMAGE_THUNK_DATA64)ImportDll->LookupTable; __try { @@ -1017,10 +1017,10 @@ NTSTATUS PhGetMappedImageImportDll( PhpMappedImageProbe( ImportDll->MappedImage, entry, - sizeof(ULONG64) + sizeof(IMAGE_THUNK_DATA64) ); - if (*entry == 0) + if (entry->u1.AddressOfData == 0) break; entry++; @@ -1055,15 +1055,15 @@ NTSTATUS PhGetMappedImageImportEntry( if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - ULONG entry; + IMAGE_THUNK_DATA32 entry; - entry = ((PULONG)ImportDll->LookupTable)[Index]; + entry = ((PIMAGE_THUNK_DATA32)ImportDll->LookupTable)[Index]; // Is this entry using an ordinal? - if (entry & IMAGE_ORDINAL_FLAG32) + if (IMAGE_SNAP_BY_ORDINAL32(entry.u1.Ordinal)) { Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL32(entry); + Entry->Ordinal = (USHORT)IMAGE_ORDINAL32(entry.u1.Ordinal); return STATUS_SUCCESS; } @@ -1071,22 +1071,22 @@ NTSTATUS PhGetMappedImageImportEntry( { importByName = PhMappedImageRvaToVa( ImportDll->MappedImage, - entry, + entry.u1.AddressOfData, NULL ); } } else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - ULONG64 entry; + IMAGE_THUNK_DATA64 entry; - entry = ((PULONG64)ImportDll->LookupTable)[Index]; + entry = ((PIMAGE_THUNK_DATA64)ImportDll->LookupTable)[Index]; // Is this entry using an ordinal? - if (entry & IMAGE_ORDINAL_FLAG64) + if (IMAGE_SNAP_BY_ORDINAL64(entry.u1.Ordinal)) { Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL64(entry); + Entry->Ordinal = (USHORT)IMAGE_ORDINAL64(entry.u1.Ordinal); return STATUS_SUCCESS; } @@ -1094,7 +1094,7 @@ NTSTATUS PhGetMappedImageImportEntry( { importByName = PhMappedImageRvaToVa( ImportDll->MappedImage, - (ULONG)entry, + (ULONG)entry.u1.AddressOfData, NULL ); } From 35e5b6d69f8aea92898ece4a0c775f21b52a3d46 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Apr 2017 02:58:44 +1000 Subject: [PATCH 028/839] Sync minor changes --- .gitignore | 40 +++++----------------------------------- ProcessHacker.sln | 13 +------------ phlib/mapimg.c | 18 +++--------------- phnt/include/ntrtl.h | 11 +++++++++++ 4 files changed, 20 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index d8d3deaf0cc8..7f672b4c792c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,13 +2,9 @@ ## 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 @@ -28,11 +24,8 @@ *.tlh *.tmp *.vspscc -.builds -*.dotCover # Visual C++ cache files -ipch/ *.aps *.ncb *.opendb @@ -40,6 +33,9 @@ ipch/ *.sdf *.db *.dll + +# Visual C++ cache folders +ipch/ .vs/ # Visual Studio profiler @@ -49,60 +45,34 @@ 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/sdk/phapppub.h ProcessHacker/include/phapprev.h +ProcessHacker/sdk/phapppub.h plugins-extra/ /sdk/ diff --git a/ProcessHacker.sln b/ProcessHacker.sln index f9d474cc33d6..490ed238ad0c 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 15.0.26228.4 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{2758DC86-368B-430C-9D29-F1EF20032A71}" EndProject @@ -16,10 +16,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fixlib", "tools\fixlib\fixl 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 @@ -58,12 +54,6 @@ Global {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 @@ -75,7 +65,6 @@ Global 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 diff --git a/phlib/mapimg.c b/phlib/mapimg.c index e093378073d2..e8521af2f431 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -986,11 +986,7 @@ NTSTATUS PhGetMappedImageImportDll( { while (TRUE) { - PhpMappedImageProbe( - ImportDll->MappedImage, - entry, - sizeof(IMAGE_THUNK_DATA32) - ); + PhpMappedImageProbe(ImportDll->MappedImage, entry, sizeof(IMAGE_THUNK_DATA32)); if (entry->u1.AddressOfData == 0) break; @@ -1014,11 +1010,7 @@ NTSTATUS PhGetMappedImageImportDll( { while (TRUE) { - PhpMappedImageProbe( - ImportDll->MappedImage, - entry, - sizeof(IMAGE_THUNK_DATA64) - ); + PhpMappedImageProbe(ImportDll->MappedImage, entry, sizeof(IMAGE_THUNK_DATA64)); if (entry->u1.AddressOfData == 0) break; @@ -1109,11 +1101,7 @@ NTSTATUS PhGetMappedImageImportEntry( __try { - PhpMappedImageProbe( - ImportDll->MappedImage, - importByName, - sizeof(IMAGE_IMPORT_BY_NAME) - ); + PhpMappedImageProbe(ImportDll->MappedImage, importByName, sizeof(IMAGE_IMPORT_BY_NAME)); } __except (EXCEPTION_EXECUTE_HANDLER) { diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 42f1c47971f1..1d904d2ead41 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -5123,6 +5123,17 @@ RtlSidHashLookup( ); #endif +#if (PHNT_VERSION >= PHNT_REDSTONE2) +NTSYSAPI +NTSTATUS +NTAPI +RtlDeriveCapabilitySidsFromName( + _Inout_ PUNICODE_STRING UnicodeString, + _Out_ PSID CapabilityGroupSid, + _Out_ PSID CapabilitySid, + ); +#endif + // Security Descriptors NTSYSAPI From 3e7a790823181c8c9fb27fccbae5c25ab475fbd2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Apr 2017 04:14:14 +1000 Subject: [PATCH 029/839] peview: Fix section sizes, update tab layout --- tools/peview/peprp.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 1908df441a52..dcf1ceb21bd6 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -117,6 +117,17 @@ VOID PvPeProperties( ); 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)) @@ -140,17 +151,6 @@ VOID PvPeProperties( 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 && @@ -554,10 +554,9 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( 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); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PhaFormatSize(PvMappedImage.Sections[i].SizeOfRawData, -1)->Buffer); } } } From b102521d2b7fd4ea48460013e5e4ad769919b0e0 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Apr 2017 21:06:12 +1000 Subject: [PATCH 030/839] Fix pointer usage consistency --- ProcessHacker/phsvc/svcapi.c | 6 +++--- phlib/native.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index a375f4a6dee4..20fef6efb6a2 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -863,7 +863,7 @@ NTSTATUS PhSvcpUnpackBuffer( if (offset & (Alignment - 1)) return STATUS_DATATYPE_MISALIGNMENT; - *OffsetInBuffer = (PVOID)((ULONG_PTR)CapturedBuffer + offset); + *OffsetInBuffer = PTR_ADD_OFFSET(CapturedBuffer, offset); return STATUS_SUCCESS; } @@ -897,8 +897,8 @@ NTSTATUS PhSvcpUnpackStringZ( if (offset & 1) return STATUS_DATATYPE_MISALIGNMENT; - start = (PWCHAR)((ULONG_PTR)CapturedBuffer + offset); - end = (PWCHAR)((ULONG_PTR)CapturedBuffer + (PackedData->Length & -2)); + 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); diff --git a/phlib/native.c b/phlib/native.c index 169bcf5d01a1..6a70c8fe1400 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5406,7 +5406,7 @@ VOID PhpEnumGenericMappedFilesAndImages( do { - baseAddress = (PVOID)((ULONG_PTR)baseAddress + basicInfo.RegionSize); + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); allocationSize += basicInfo.RegionSize; if (!NT_SUCCESS(NtQueryVirtualMemory( @@ -5464,7 +5464,7 @@ VOID PhpEnumGenericMappedFilesAndImages( } else { - baseAddress = (PVOID)((ULONG_PTR)baseAddress + basicInfo.RegionSize); + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); if (!NT_SUCCESS(NtQueryVirtualMemory( ProcessHandle, From f73aea5e353b40cc1048fe64d8b26a6fdc2adfd9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Apr 2017 22:15:57 +1000 Subject: [PATCH 031/839] peview: Fix incorrect export function addresses (reported by @lucasg) --- phlib/mapimg.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/phlib/mapimg.c b/phlib/mapimg.c index e8521af2f431..ab022d66fa5b 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -739,11 +739,7 @@ NTSTATUS PhGetMappedImageExportFunction( } else { - Function->Function = PhMappedImageRvaToVa( - Exports->MappedImage, - rva, - NULL - ); + Function->Function = UlongToPtr(rva); Function->ForwardedName = NULL; } From 8e019d308a067997353a48ebb82df627680e5931 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 04:56:06 +1000 Subject: [PATCH 032/839] peview: Label unnamed exported variables vs exported functions --- tools/peview/peprp.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index dcf1ceb21bd6..dbdef62cac5e 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -852,13 +852,22 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( } else { - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); + if (exportFunction.Function) + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); + else + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed variable)", NULL); } PhPrintUInt32(number, exportEntry.Ordinal); PhSetListViewSubItem(lvHandle, lvItemIndex, 1, number); - if (!exportFunction.ForwardedName) + if (exportFunction.ForwardedName) + { + name = PhZeroExtendToUtf16(exportFunction.ForwardedName); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); + PhDereferenceObject(name); + } + else { if ((ULONG_PTR)exportFunction.Function >= (ULONG_PTR)PvMappedImage.ViewBase) PhPrintPointer(pointer, PTR_SUB_OFFSET(exportFunction.Function, PvMappedImage.ViewBase)); @@ -867,12 +876,6 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); } - else - { - name = PhZeroExtendToUtf16(exportFunction.ForwardedName); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); - PhDereferenceObject(name); - } } } } From c5376cd6b4410b22f0c55447550f03f2f45805b5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 06:07:36 +1000 Subject: [PATCH 033/839] peview: Fix typo, Remove legacy hack fixed by commit f73aea5 --- tools/peview/peprp.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index dbdef62cac5e..8ce3c08fe2ee 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -827,7 +827,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( 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"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 120, L"RVA"); PhSetExtendedListView(lvHandle); if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage))) @@ -869,11 +869,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( } else { - if ((ULONG_PTR)exportFunction.Function >= (ULONG_PTR)PvMappedImage.ViewBase) - PhPrintPointer(pointer, PTR_SUB_OFFSET(exportFunction.Function, PvMappedImage.ViewBase)); - else - PhPrintPointer(pointer, exportFunction.Function); - + PhPrintPointer(pointer, exportFunction.Function); PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); } } From bc346bfde1e2773ec4488357551023a1549f21fb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 06:18:14 +1000 Subject: [PATCH 034/839] peview: ignore entries with invalid RVA --- tools/peview/peprp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 8ce3c08fe2ee..8e444a87255d 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -844,6 +844,12 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( WCHAR number[PH_INT32_STR_LEN_1]; WCHAR pointer[PH_PTR_STR_LEN_1]; + // TODO: user32.dll and some other libraries have many (unnamed) exports with invalid RVA, + // they might be exported variables or caused by incorrect math, ignore these entries + // until more information is available. + if (!exportFunction.Function) + continue; + if (exportEntry.Name) { name = PhZeroExtendToUtf16(exportEntry.Name); @@ -852,10 +858,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( } else { - if (exportFunction.Function) - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); - else - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed variable)", NULL); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); } PhPrintUInt32(number, exportEntry.Ordinal); From 733d3f3b52339f160948339d14641c5f33d5d0c3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 06:31:46 +1000 Subject: [PATCH 035/839] Fix typo --- tools/peview/peprp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 8e444a87255d..6d26c7e76340 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -844,7 +844,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( WCHAR number[PH_INT32_STR_LEN_1]; WCHAR pointer[PH_PTR_STR_LEN_1]; - // TODO: user32.dll and some other libraries have many (unnamed) exports with invalid RVA, + // TODO: user32.dll and some other dlls have many (unnamed) exports with (invalid) 0x0 RVA, // they might be exported variables or caused by incorrect math, ignore these entries // until more information is available. if (!exportFunction.Function) From 327d7e7fc66ce0094ca1b912790aef3d72a8c2bc Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 21:07:18 +1000 Subject: [PATCH 036/839] Fix PhGetFileVersionInfoString returning invalid version strings --- phlib/util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phlib/util.c b/phlib/util.c index 5a923ee1daae..117339b56462 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1627,6 +1627,10 @@ PPH_STRING PhGetFileVersionInfoString( { PPH_STRING string; + // Check if the string has a valid length. + if (length <= sizeof(WCHAR)) + return NULL; + string = PhCreateStringEx((PWCHAR)buffer, length * sizeof(WCHAR)); // length may include the null terminator. PhTrimToNullTerminatorString(string); From 6e7e769fb5a29dc44f25396f062778980ae75243 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 21:07:59 +1000 Subject: [PATCH 037/839] peview: Fix null string check --- tools/peview/peprp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 6d26c7e76340..dd63d9509a28 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -316,7 +316,7 @@ FORCEINLINE PWSTR PvpGetStringOrNa( _In_ PPH_STRING String ) { - if (String) + if (!PhIsNullOrEmptyString(String)) return String->Buffer; else return L"N/A"; From aca8f685692d74f74842297bebebbd623f4505f0 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 29 Apr 2017 12:17:18 +0100 Subject: [PATCH 038/839] phlib: Fix a potential exception when calling NtQueryObject (#133) * Under some circumstances, NtQueryObject() will produce a NULL pointer exception if the last parameter is NULL. --- phlib/hndlinfo.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 749a6d91ccb9..ce0b3e27f28e 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -142,12 +142,13 @@ NTSTATUS PhpGetObjectBasicInformation( } else { + ULONG returnLength; status = NtQueryObject( Handle, ObjectBasicInformation, BasicInformation, sizeof(OBJECT_BASIC_INFORMATION), - NULL + &returnLength ); if (NT_SUCCESS(status)) @@ -1394,7 +1395,7 @@ NTSTATUS PhEnumObjectTypes( { NTSTATUS status; PVOID buffer; - ULONG bufferSize; + ULONG bufferSize, returnLength; bufferSize = 0x1000; buffer = PhAllocate(bufferSize); @@ -1404,7 +1405,7 @@ NTSTATUS PhEnumObjectTypes( ObjectTypesInformation, buffer, bufferSize, - NULL + &returnLength )) == STATUS_INFO_LENGTH_MISMATCH) { PhFree(buffer); From 19803e299771b9b18cfcd40d1e9a27ec9785cd99 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 21:33:22 +1000 Subject: [PATCH 039/839] Fix up typos per project guidelines --- phlib/hndlinfo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index ce0b3e27f28e..77df87c8d7c1 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -143,6 +143,7 @@ NTSTATUS PhpGetObjectBasicInformation( else { ULONG returnLength; + status = NtQueryObject( Handle, ObjectBasicInformation, @@ -1395,7 +1396,8 @@ NTSTATUS PhEnumObjectTypes( { NTSTATUS status; PVOID buffer; - ULONG bufferSize, returnLength; + ULONG bufferSize; + ULONG returnLength; bufferSize = 0x1000; buffer = PhAllocate(bufferSize); From 2eb391aaf2365e6b71ca3d4f214ae6ff83512544 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 29 Apr 2017 22:46:05 +1000 Subject: [PATCH 040/839] peview: Fix CFG index and rva not being consistent with other tabs --- tools/peview/peprp.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index dd63d9509a28..d9a237c942c1 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -1145,7 +1145,7 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( 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, 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 , 100, L"Flags"); PhSetExtendedListView(lvHandle); @@ -1173,23 +1173,18 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( 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; - - lvItemIndex = PhAddListViewItem( - lvHandle, - MAXINT, - PhaFormatString(L"%I64u", i)->Buffer, - NULL - ); - PhSetListViewSubItem( - lvHandle, - lvItemIndex, - 1, - PhaFormatString(L"0x%08x", cfgFunctionEntry.Rva)->Buffer - ); + + 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 (!(symbol = PhGetSymbolFromAddress( From 0da87cb201038afb166e7c2fb2fc9f777fe7bdf5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Apr 2017 00:38:36 +1000 Subject: [PATCH 041/839] UserNotes: Update default settings for 3.x builds --- plugins/UserNotes/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index e0f3f0278e08..88a55cfa438b 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -1331,7 +1331,7 @@ LOGICAL DllMain( PPH_PLUGIN_INFORMATION info; PH_SETTING_CREATE settings[] = { - { StringSettingType, SETTING_NAME_DATABASE_PATH, L"%APPDATA%\\Process Hacker 2\\usernotesdb.xml" }, + { StringSettingType, SETTING_NAME_DATABASE_PATH, L"%APPDATA%\\Process Hacker\\usernotesdb.xml" }, { StringSettingType, SETTING_NAME_CUSTOM_COLOR_LIST, L"" } }; From 9c438e01cb44759a29615399b1e8641635ea5840 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Apr 2017 09:09:27 +1000 Subject: [PATCH 042/839] Fix typo --- ProcessHacker/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index fbec1593805a..f0f7526594fc 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -2807,7 +2807,7 @@ BOOLEAN PhUiUnloadModule( L"Unable to unload ", Module->Name->Buffer, L". Make sure Process Hacker is running with " - L"administrative privileges. Error" + L"administrative privileges." )->Buffer, status, 0 From 0bf49bdcb6fc04417bc3185bb974f6f66084302a Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 4 May 2017 03:44:50 +0100 Subject: [PATCH 043/839] Fix search box font size on high DPI displays (#135) --- ProcessHacker/searchbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 4cc1c8e132af..53e2b47dbb25 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -83,7 +83,7 @@ VOID PhpSearchInitializeFont( if (Context->WindowFont) DeleteObject(Context->WindowFont); - Context->WindowFont = PhCreateCommonFont(PH_SCALE_DPI(10), FW_MEDIUM, Context->WindowHandle); + Context->WindowFont = PhCreateCommonFont(10, FW_MEDIUM, Context->WindowHandle); } VOID PhpSearchInitializeTheme( From e2245b4e574821e1904b9c98aa75d56854225caf Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 16:17:31 +1000 Subject: [PATCH 044/839] Fix Toolbar and Plugin Manager High-DPI scaling issues --- ProcessHacker/include/phapp.h | 2 +- ProcessHacker/searchbox.c | 2 +- plugins/ExtraPlugins/wndtree.c | 32 +++++++++++++++++--------------- plugins/ToolStatus/customizesb.c | 6 +++--- plugins/ToolStatus/customizetb.c | 4 ++-- plugins/ToolStatus/main.c | 4 ++-- plugins/ToolStatus/toolbar.c | 10 +++++++--- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index f4ff73920fa5..5a9914f8a514 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -87,7 +87,7 @@ extern PH_STARTUP_PARAMETERS PhStartupParameters; extern PH_PROVIDER_THREAD PhPrimaryProviderThread; extern PH_PROVIDER_THREAD PhSecondaryProviderThread; -#define PH_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) +#define PH_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) // phapppub // begin_phapppub PHAPPAPI diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 53e2b47dbb25..8581d2d15cfd 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -90,7 +90,7 @@ VOID PhpSearchInitializeTheme( _Inout_ PEDIT_CONTEXT Context ) { - Context->CXWidth = PhMultiplyDivide(20, PhGlobalDpi, 96); + 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)); diff --git a/plugins/ExtraPlugins/wndtree.c b/plugins/ExtraPlugins/wndtree.c index 54be25aaa2a8..0bdeced96e21 100644 --- a/plugins/ExtraPlugins/wndtree.c +++ b/plugins/ExtraPlugins/wndtree.c @@ -2,7 +2,7 @@ * Process Hacker Extra Plugins - * Plugin Manager * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2017 dmex * * This file is part of Process Hacker. * @@ -307,16 +307,20 @@ BOOLEAN NTAPI PluginsTreeNewCallback( PH_STRINGREF text; SIZE nameSize; SIZE textSize; - + SIZE imageSize; + + imageSize.cx = PH_SCALE_DPI(17); + imageSize.cy = PH_SCALE_DPI(17); + if (node->PluginOptions) { if (!node->Icon) { HBITMAP bitmapActive; - if (bitmapActive = PhLoadPngImageFromResource(PluginInstance->DllBase, 17, 17, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) + if (bitmapActive = PhLoadPngImageFromResource(PluginInstance->DllBase, imageSize.cx, imageSize.cy, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) { - node->Icon = CommonBitmapToIcon(bitmapActive, 17, 17); + node->Icon = CommonBitmapToIcon(bitmapActive, imageSize.cx, imageSize.cy); DeleteObject(bitmapActive); } } @@ -325,11 +329,11 @@ BOOLEAN NTAPI PluginsTreeNewCallback( { DrawIconEx( customDraw->Dc, - rect.left + 5, - rect.top + ((rect.bottom - rect.top) - 17) / 2, + rect.left + PH_SCALE_DPI(5), + rect.top + ((rect.bottom - rect.top) - imageSize.cy) / 2, node->Icon, - 17, - 17, + imageSize.cx, + imageSize.cy, 0, NULL, DI_NORMAL @@ -337,12 +341,10 @@ BOOLEAN NTAPI PluginsTreeNewCallback( } } - rect.left += 19; - rect.left += 8; - - rect.top += 5; - rect.right -= 5; - rect.bottom -= 8; + rect.left += PH_SCALE_DPI(27); + 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)); @@ -443,7 +445,7 @@ VOID InitializePluginsTree( Context->TitleFontHandle = PhCreateCommonFont(-14, FW_BOLD, NULL); TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, Context); - TreeNew_SetRowHeight(TreeNewHandle, 48); + TreeNew_SetRowHeight(TreeNewHandle, PH_SCALE_DPI(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); diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index 6b7d51ef4cdf..cd4dbd29034d 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -2,7 +2,7 @@ * Process Hacker ToolStatus - * Statusbar Customize Dialog * - * Copyright (C) 2015-2016 dmex + * Copyright (C) 2015-2017 dmex * * This file is part of Process Hacker. * @@ -321,8 +321,8 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( 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 + ListBox_SetItemHeight(context->AvailableListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight + ListBox_SetItemHeight(context->CurrentListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight CustomizeLoadStatusBarItems(context); diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index f1b906bd17c5..e376e26f749d 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -2,7 +2,7 @@ * Process Hacker ToolStatus - * Toolbar Customize Dialog * - * Copyright (C) 2015-2016 dmex + * Copyright (C) 2015-2017 dmex * * This file is part of Process Hacker. * @@ -519,7 +519,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( context->AddButtonHandle = GetDlgItem(hwndDlg, IDC_ADD); context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); - context->CXWidth = 16; + 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)); diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 8bd024e7203a..2c81fa57babb 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -2,8 +2,8 @@ * Process Hacker ToolStatus - * main program * - * Copyright (C) 2011-2016 dmex * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2011-2017 dmex * * This file is part of Process Hacker. * @@ -712,7 +712,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (SearchBoxDisplayMode == SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) { if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PhMultiplyDivide(180, PhGlobalDpi, 96), 22); + RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PH_SCALE_DPI(180), 22); if (!IsWindowVisible(SearchboxHandle)) ShowWindow(SearchboxHandle, SW_SHOW); diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index 67fb6176bab4..c7b752460c7b 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -2,7 +2,7 @@ * Process Hacker ToolStatus - * main toolbar * - * Copyright (C) 2011-2016 dmex + * Copyright (C) 2011-2017 dmex * * This file is part of Process Hacker. * @@ -109,6 +109,10 @@ VOID RebarLoadSettings( { if (ToolStatusConfig.ToolBarEnabled && !ToolBarImageList) { + // Enable scaling the Toolbar and images on high-DPI machines. + ToolBarImageSize.cx = PH_SCALE_DPI(16); + ToolBarImageSize.cy = PH_SCALE_DPI(16); + ToolBarImageList = ImageList_Create(ToolBarImageSize.cx, ToolBarImageSize.cy, ILC_COLOR32, 0, 0); } @@ -141,7 +145,7 @@ VOID RebarLoadSettings( NULL ); - // Set the toolbar info with no imagelist. + // Set the rebar info with no imagelist. SendMessage(RebarHandle, RB_SETBARINFO, 0, (LPARAM)&rebarInfo); // Set the toolbar struct size. SendMessage(ToolBarHandle, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); @@ -209,7 +213,7 @@ VOID RebarLoadSettings( // 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); + RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PH_SCALE_DPI(180), height); if (!IsWindowVisible(SearchboxHandle)) ShowWindow(SearchboxHandle, SW_SHOW); From 1ca180bb60c16ceee0aa3a987c87081834c4d8fa Mon Sep 17 00:00:00 2001 From: lucasg Date: Thu, 4 May 2017 08:37:44 +0200 Subject: [PATCH 045/839] Add cfg support for wow64 process (#134) * Locate and tag CFG bitmap memory page (both x86 and x64) for wow64 processes * Add CFG entries parsing for 32-bit executables in peview * Add unnamed union in PH_MAPPED_IMAGE in order to selectively read a IMAGE_NT_HEADER as 32 bit (could also have used a C-style cast) * Rename CFG page "RVA" column to "VA" since we read Virtual Adresses --- ProcessHacker/include/memprv.h | 3 +- ProcessHacker/memlist.c | 4 +- ProcessHacker/memprv.c | 81 ++++++++++++++++--- phlib/include/mapimg.h | 6 +- phlib/mapimg.c | 143 ++++++++++++++++++++++++++++++++- tools/peview/peprp.c | 7 +- 6 files changed, 221 insertions(+), 23 deletions(-) diff --git a/ProcessHacker/include/memprv.h b/ProcessHacker/include/memprv.h index b3d7baf86f8f..dcd62a55fbea 100644 --- a/ProcessHacker/include/memprv.h +++ b/ProcessHacker/include/memprv.h @@ -21,7 +21,8 @@ typedef enum _PH_MEMORY_REGION_TYPE Heap32Region, HeapSegmentRegion, HeapSegment32Region, - CfgBitmapRegion + CfgBitmapRegion, + CfgBitmap32Region } PH_MEMORY_REGION_TYPE; typedef struct _PH_MEMORY_ITEM diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index 4e5d9373c741..90f4ec268941 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -425,7 +425,9 @@ PPH_STRING PhpGetMemoryRegionUseText( 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"); + case CfgBitmap32Region: + return PhFormatString(L"CFG Bitmap%s", + type == CfgBitmap32Region ? L" 32-bit" : L""); default: return PhReferenceEmptyString(); } diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 14e9ca24bc42..ee97e34730b3 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -488,23 +488,81 @@ NTSTATUS PhpUpdateMemoryRegionTypes( } #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); + } + } + } + + if (isWow64) { LDR_INIT_BLOCK ldrInitBlock = { 0 }; PVOID ldrInitBlockBaseAddress = NULL; PPH_MEMORY_ITEM cfgBitmapMemoryItem; - PPH_STRING ntdllFileName; + PPH_STRING ntdllWow64FileName; - ntdllFileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\System32\\ntdll.dll"); + ntdllWow64FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\ntdll.dll"); status = PhGetProcedureAddressRemote( ProcessHandle, - ntdllFileName->Buffer, + ntdllWow64FileName->Buffer, "LdrSystemDllInitBlock", 0, &ldrInitBlockBaseAddress, NULL - ); + ); if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) { @@ -514,10 +572,10 @@ NTSTATUS PhpUpdateMemoryRegionTypes( &ldrInitBlock, sizeof(LDR_INIT_BLOCK), NULL - ); + ); } - PhDereferenceObject(ntdllFileName); + PhDereferenceObject(ntdllWow64FileName); if (NT_SUCCESS(status)) { @@ -537,17 +595,14 @@ NTSTATUS PhpUpdateMemoryRegionTypes( // 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; + memoryItem->RegionType = CfgBitmap32Region; listEntry = listEntry->Flink; memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); } } } + } #endif diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index 608e652ce5a8..ba179e8ca83d 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -10,7 +10,11 @@ typedef struct _PH_MAPPED_IMAGE PVOID ViewBase; SIZE_T Size; - PIMAGE_NT_HEADERS NtHeaders; + union { + PIMAGE_NT_HEADERS32 NtHeaders32; + PIMAGE_NT_HEADERS NtHeaders; + }; + ULONG NumberOfSections; PIMAGE_SECTION_HEADER Sections; USHORT Magic; diff --git a/phlib/mapimg.c b/phlib/mapimg.c index ab022d66fa5b..d3d73cb55879 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1213,16 +1213,17 @@ ULONG PhCheckSumMappedImage( return checkSum; } -NTSTATUS PhGetMappedImageCfg( +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 (config64->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardFlags)) @@ -1244,7 +1245,7 @@ NTSTATUS PhGetMappedImageCfg( CfgConfig->NumberOfGuardFunctionEntries = config64->GuardCFFunctionCount; CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(config64->GuardCFFunctionTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG) (ULONG_PTR) PTR_SUB_OFFSET(config64->GuardCFFunctionTable , MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1333,6 +1334,142 @@ NTSTATUS PhGetMappedImageCfg( 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 (config32->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, 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, + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(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 ( + config32->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardAddressTakenIatEntryTable) + + sizeof(config32->GuardAddressTakenIatEntryTable) + + sizeof(config32->GuardAddressTakenIatEntryCount) + ) + { + CfgConfig->NumberOfGuardAdressIatEntries = config32->GuardAddressTakenIatEntryCount; + CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( + MappedImage, + (ULONG)(config32->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 ( + config32->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardLongJumpTargetTable) + + sizeof(config32->GuardLongJumpTargetTable) + + sizeof(config32->GuardLongJumpTargetCount) + ) + { + CfgConfig->NumberOfGuardLongJumpEntries = config32->GuardLongJumpTargetCount; + CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( + MappedImage, + (ULONG)(config32->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 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, diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index d9a237c942c1..778ada75b70c 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -185,8 +185,7 @@ VOID PvPeProperties( } // CFG page - if ((PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && - (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF)) + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) { newPage = PvCreatePropPageContext( MAKEINTRESOURCE(IDD_PECFG), @@ -1144,8 +1143,8 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( PhSetListViewStyle(lvHandle, FALSE, 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, 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); From ead3508fc656a21a75c2bf0aae6552bbbe45df97 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 17:05:10 +1000 Subject: [PATCH 046/839] Fix variable scope name re-use issues, Add extra LDR_INIT_BLOCK error checking, Fix tabspace --- ProcessHacker/memprv.c | 37 ++++++++++++++++++------------------- phlib/mapimg.c | 25 ++++++++++++------------- tools/peview/peprp.c | 8 ++++---- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index ee97e34730b3..cf5979bd1fc8 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -517,7 +517,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( PhDereferenceObject(ntdllFileName); - if (NT_SUCCESS(status)) + if (NT_SUCCESS(status) && ldrInitBlock.Size) { PVOID cfgBitmapAddress = NULL; @@ -549,9 +549,9 @@ NTSTATUS PhpUpdateMemoryRegionTypes( if (isWow64) { - LDR_INIT_BLOCK ldrInitBlock = { 0 }; - PVOID ldrInitBlockBaseAddress = NULL; - PPH_MEMORY_ITEM cfgBitmapMemoryItem; + LDR_INIT_BLOCK ldrInitBlock32 = { 0 }; + PVOID ldrInitBlockBaseAddress32 = NULL; + PPH_MEMORY_ITEM cfgBitmapMemoryItem32; PPH_STRING ntdllWow64FileName; ntdllWow64FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\ntdll.dll"); @@ -560,40 +560,40 @@ NTSTATUS PhpUpdateMemoryRegionTypes( ntdllWow64FileName->Buffer, "LdrSystemDllInitBlock", 0, - &ldrInitBlockBaseAddress, + &ldrInitBlockBaseAddress32, NULL - ); + ); - if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) + if (NT_SUCCESS(status) && ldrInitBlockBaseAddress32) { status = NtReadVirtualMemory( ProcessHandle, - ldrInitBlockBaseAddress, - &ldrInitBlock, + ldrInitBlockBaseAddress32, + &ldrInitBlock32, sizeof(LDR_INIT_BLOCK), NULL - ); + ); } PhDereferenceObject(ntdllWow64FileName); - if (NT_SUCCESS(status)) + if (NT_SUCCESS(status) && ldrInitBlock32.Size) { 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 (ldrInitBlock32.Size == sizeof(LDR_INIT_BLOCK)) + cfgBitmapAddress = ldrInitBlock32.CfgBitmapAddress; // 15063 + else if (ldrInitBlock32.Size == 128) + cfgBitmapAddress = ldrInitBlock32.Unknown1[11]; // 14393 - if (cfgBitmapAddress && (cfgBitmapMemoryItem = PhLookupMemoryItemList(List, cfgBitmapAddress))) + if (cfgBitmapAddress && (cfgBitmapMemoryItem32 = PhLookupMemoryItemList(List, cfgBitmapAddress))) { - PLIST_ENTRY listEntry = &cfgBitmapMemoryItem->ListEntry; + PLIST_ENTRY listEntry = &cfgBitmapMemoryItem32->ListEntry; PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); // Tagging memory items - while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem) + while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem32) { memoryItem->RegionType = CfgBitmap32Region; @@ -602,7 +602,6 @@ NTSTATUS PhpUpdateMemoryRegionTypes( } } } - } #endif diff --git a/phlib/mapimg.c b/phlib/mapimg.c index d3d73cb55879..b949e3aefc39 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -3,6 +3,7 @@ * mapped image * * Copyright (C) 2010 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -1220,10 +1221,9 @@ NTSTATUS PhGetMappedImageCfg64( { 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)) @@ -1245,7 +1245,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardFunctionEntries = config64->GuardCFFunctionCount; CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( MappedImage, - (ULONG) (ULONG_PTR) PTR_SUB_OFFSET(config64->GuardCFFunctionTable , MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardCFFunctionTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1289,7 +1289,7 @@ NTSTATUS PhGetMappedImageCfg64( MappedImage, CfgConfig->GuardAdressIatTable, (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardAdressIatEntries) - ); + ); } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1337,7 +1337,7 @@ NTSTATUS PhGetMappedImageCfg64( NTSTATUS PhGetMappedImageCfg32( _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, _In_ PPH_MAPPED_IMAGE MappedImage -) + ) { NTSTATUS status; PIMAGE_LOAD_CONFIG_DIRECTORY32 config32; @@ -1345,7 +1345,6 @@ NTSTATUS PhGetMappedImageCfg32( if (!NT_SUCCESS(status = PhGetMappedImageLoadConfig32(MappedImage, &config32))) return status; - // Not every load configuration defines CFG characteristics if (config32->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardFlags)) return STATUS_INVALID_VIEW_SIZE; @@ -1368,7 +1367,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardCFFunctionTable , MappedImage->NtHeaders32->OptionalHeader.ImageBase), NULL - ); + ); if (CfgConfig->GuardFunctionTable && CfgConfig->NumberOfGuardFunctionEntries) { @@ -1378,7 +1377,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, CfgConfig->GuardFunctionTable, (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardFunctionEntries) - ); + ); } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1400,7 +1399,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, (ULONG)(config32->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL - ); + ); if (CfgConfig->GuardAdressIatTable && CfgConfig->NumberOfGuardAdressIatEntries) { @@ -1410,7 +1409,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, CfgConfig->GuardAdressIatTable, (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardAdressIatEntries) - ); + ); } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1433,7 +1432,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, (ULONG)(config32->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL - ); + ); if (CfgConfig->GuardLongJumpTable && CfgConfig->NumberOfGuardLongJumpEntries) { @@ -1443,7 +1442,7 @@ NTSTATUS PhGetMappedImageCfg32( MappedImage, CfgConfig->GuardLongJumpTable, (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardLongJumpEntries) - ); + ); } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -1458,7 +1457,7 @@ NTSTATUS PhGetMappedImageCfg32( NTSTATUS PhGetMappedImageCfg( _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, _In_ PPH_MAPPED_IMAGE MappedImage -) + ) { if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 778ada75b70c..5d7b9fc84053 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -1143,10 +1143,10 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( PhSetListViewStyle(lvHandle, FALSE, 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"); + 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); // Init symbol resolver From 9509162bdcf91f494ea7e530cdd8039fd8b31a7f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 21:15:34 +1000 Subject: [PATCH 047/839] Save token properties splitter position, Add workaround for splitter resize bug --- ProcessHacker/include/splitter.h | 1 + ProcessHacker/settings.c | 1 + ProcessHacker/splitter.c | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h index 50b9c1fb7bf4..f872c7a7483a 100644 --- a/ProcessHacker/include/splitter.h +++ b/ProcessHacker/include/splitter.h @@ -18,6 +18,7 @@ typedef struct _PH_HSPLITTER_CONTEXT LONG SplitterOffset; LONG SplitterPosition; + ULONG SplitterLayoutCount; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM Topitem; PPH_LAYOUT_ITEM Bottomitem; diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index addc547df23f..d57fafd97072 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -184,6 +184,7 @@ VOID PhSettingsInitialization( PhpAddStringSetting(L"ThreadTreeListSort", L"1,2"); // 1, DescendingSortOrder PhpAddStringSetting(L"ThreadStackListViewColumns", L""); PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,380"); + PhpAddIntegerSetting(L"TokenSplitterPosition", L"250"); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms // Colors are specified with R in the lowest byte, then G, then B. diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 55c28b97785a..ddecd07f23aa 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -45,7 +45,7 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( PhInitializeLayoutManager(&context->LayoutManager, Parent); context->SplitterOffset = -4; - context->SplitterPosition = 250; + context->SplitterPosition = PhGetIntegerSetting(L"TokenSplitterPosition"); context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); @@ -56,6 +56,8 @@ VOID PhDeleteHSplitter( _Inout_ PPH_HSPLITTER_CONTEXT Context ) { + PhSetIntegerSetting(L"TokenSplitterPosition", Context->SplitterPosition); + PhDeleteLayoutManager(&Context->LayoutManager); } @@ -66,7 +68,18 @@ VOID PhHSplitterHandleWmSize( ) { // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. - // TODO: Check min_height and adjust second control if invisible. + + if (Context->SplitterLayoutCount >= 2) + { + // BUG: If the window is maximized and you move the splitter to the bottom, restoring the window causes + // the bottom control to get moved outside the visible area... Just move the splitter back up. + if ((Context->Bottomitem->Rect.bottom - Context->Bottomitem->Rect.top) <= 100) + Context->SplitterPosition = Context->Topitem->Rect.bottom - Context->Topitem->Rect.top - SPLITTER_PADDING; + } + else + { + Context->SplitterLayoutCount++; + } // Set the bottom margin of the top control. Context->Topitem->Margin.bottom = Height - Context->SplitterPosition - SPLITTER_PADDING; From f52b85838ba2ad91be77a8c26a8e0d0db317f925 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 21:16:00 +1000 Subject: [PATCH 048/839] WindowExplorer: Hide window tab for system processes --- plugins/WindowExplorer/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index c3cc7af784de..c31834ae619d 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -3,6 +3,7 @@ * main program * * Copyright (C) 2011 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -168,11 +169,11 @@ VOID NTAPI ProcessPropertiesInitializingCallback( ) { PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - BOOLEAN isGuiProcess = TRUE; - // enum threads, IsGuiThread, isGuiProcess=TRUE - - if (isGuiProcess) + if ( + propContext->ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && + propContext->ProcessItem->ProcessId != SYSTEM_PROCESS_ID + ) { WE_WINDOW_SELECTOR selector; From e843852dcb35eb45ff54a034a2871319ef5d005e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 21:27:50 +1000 Subject: [PATCH 049/839] Updater: Fix memory leak --- plugins/Updater/page3.c | 6 ++---- plugins/Updater/page5.c | 13 +++++++------ plugins/Updater/updater.c | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index a34e8c6c07cf..740dae4589f1 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -77,7 +77,7 @@ VOID ShowAvailableDialog( 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_CANCEL_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; @@ -94,9 +94,7 @@ VOID ShowAvailableDialog( Context->RevisionVersion, PhGetStringOrEmpty(Context->Size) )->Buffer; - - //if (!PhIsNullOrEmptyString(Context->BuildMessage)) - // config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); + config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index ca014212da1d..36b9860d61b0 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -136,7 +136,7 @@ VOID ShowLatestVersionDialog( 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; @@ -158,6 +158,7 @@ VOID ShowLatestVersionDialog( Context->CurrentRevisionVersion, PhaFormatDateTime(&systemTime)->Buffer )->Buffer; + config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } @@ -170,9 +171,12 @@ 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.cxWidth = 200; + config.pfCallback = FinalTaskDialogCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = L"Process Hacker - Updater"; config.pszMainInstruction = L"You're running a pre-release build."; @@ -182,10 +186,7 @@ VOID ShowNewerVersionDialog( Context->CurrentMinorVersion, Context->CurrentRevisionVersion )->Buffer; - - config.cxWidth = 200; - config.pfCallback = FinalTaskDialogCallbackProc; - config.lpCallbackData = (LONG_PTR)Context; + config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 51f75f11c329..de808e1da9bc 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -61,6 +61,7 @@ VOID FreeUpdateContext( PhClearReference(&Context->ReleaseNotesUrl); PhClearReference(&Context->SetupFilePath); PhClearReference(&Context->SetupFileDownloadUrl); + PhClearReference(&Context->BuildMessage); if (Context->IconLargeHandle) DestroyIcon(Context->IconLargeHandle); From e6a9472a4fb61d5078d1b0d741a93fc0eb23d591 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 4 May 2017 21:41:12 +1000 Subject: [PATCH 050/839] CustomSetupTool: Fix shutting down current instances --- .../CustomSetupTool/CustomSetupTool/appsup.c | 52 ++++++------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index fa09d200d337..5e5d174eb8b7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -476,50 +476,30 @@ BOOLEAN ShutdownProcessHacker(VOID) HANDLE processHandle; ULONG processID = 0; - windowHandle = FindWindow(L"ProcessHacker", NULL); - if (!windowHandle) - return TRUE; - - GetWindowThreadProcessId(windowHandle, &processID); - - SendMessageTimeout( - windowHandle, - WM_QUIT, - 0, - 0, - SMTO_ABORTIFHUNG | SMTO_BLOCK, - 5000, - NULL - ); - - // Check the window handle again - windowHandle = FindWindow(L"ProcessHacker", NULL); - if (!windowHandle) - return TRUE; - - GetWindowThreadProcessId(windowHandle, &processID); - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - SYNCHRONIZE | PROCESS_TERMINATE, - ULongToHandle(processID) - ))) + while (windowHandle = FindWindow(L"ProcessHacker", NULL)) { - PostMessage(windowHandle, WM_QUIT, 0, 0); + GetWindowThreadProcessId(windowHandle, &processID); - if (WaitForSingleObject(processHandle, 10 * 1000) != WAIT_OBJECT_0) + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + SYNCHRONIZE | PROCESS_TERMINATE, + ULongToHandle(processID) + ))) { - if (!NT_SUCCESS(NtTerminateProcess(processHandle, 1))) + PostMessage(windowHandle, WM_QUIT, 0, 0); + + // Wait for process exit. + if (WaitForSingleObject(processHandle, 10 * 1000) != WAIT_OBJECT_0) { - NtClose(processHandle); - return FALSE; + // Timed out, kill the process. + NtTerminateProcess(processHandle, 1); } - } - NtClose(processHandle); + NtClose(processHandle); + } } - return FALSE; + return TRUE; } BOOLEAN CreateDirectoryPath(_In_ PWSTR DirPath) From 7dbbac839a47236f423d18e87861f74131e3a637 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 5 May 2017 15:42:03 +1000 Subject: [PATCH 051/839] Update build tools --- ProcessHacker/ProcessHacker.vcxproj | 48 +----- ProcessHacker/include/phapprev.h | 2 +- build/phapppub_options.txt | 6 - tools/CustomBuildTool/Source Files/Build.cs | 2 - .../CustomBuildTool/Source Files/HeaderGen.cs | 159 +++++++++--------- tools/CustomBuildTool/Source Files/Program.cs | 23 ++- .../bin/Release/CustomBuildTool.exe | Bin 145408 -> 146432 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 62976 bytes 8 files changed, 103 insertions(+), 137 deletions(-) delete mode 100644 build/phapppub_options.txt diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 8f2a2bbc1f1f..abad4684afab 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -104,16 +104,8 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - + + @@ -138,16 +130,8 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - + + @@ -179,16 +163,8 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - + + @@ -219,16 +195,8 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - ..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -updaterev - Generating revision number... - - - - - - - + + diff --git a/ProcessHacker/include/phapprev.h b/ProcessHacker/include/phapprev.h index 7fb3bd0b5cb6..cbb6d2096029 100644 --- a/ProcessHacker/include/phapprev.h +++ b/ProcessHacker/include/phapprev.h @@ -1,6 +1,6 @@ #ifndef PHAPPREV_H #define PHAPPREV_H -#define PHAPP_VERSION_REVISION 0 +#define PHAPP_VERSION_REVISION 0x0D06F00D #endif // PHAPPREV_H 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/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index d88bfd38394f..835b79b8565e 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -419,7 +419,6 @@ public static bool CopyVersionHeader() try { HeaderGen gen = new HeaderGen(); - gen.LoadConfig("build\\phapppub_options.txt"); gen.Execute(); File.Copy("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", true); @@ -965,7 +964,6 @@ public static bool BuildPublicHeaderFiles() try { HeaderGen gen = new HeaderGen(); - gen.LoadConfig("build\\phapppub_options.txt"); gen.Execute(); } catch (Exception ex) diff --git a/tools/CustomBuildTool/Source Files/HeaderGen.cs b/tools/CustomBuildTool/Source Files/HeaderGen.cs index 971675106e28..e4df545defbd 100644 --- a/tools/CustomBuildTool/Source Files/HeaderGen.cs +++ b/tools/CustomBuildTool/Source Files/HeaderGen.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace CustomBuildTool { @@ -16,48 +14,54 @@ public class HeaderFile public class HeaderGen { - private string _baseDirectory; - private string[] _modes; - private string[] _files; - private string _outputFile; - private string _header = ""; - private string _footer = ""; - - private string UnEscape(string text) - { - return text.Replace("\\r", "\r").Replace("\\n", "\n").Replace("\\\\", "\\"); - } - - public void LoadConfig(string fileName) + private string BaseDirectory; + private string[] Modes; + private string[] Files; + private string OutputFile; + private string Header; + private string Footer; + + public HeaderGen() { - string[] lines = File.ReadAllLines(fileName); - - foreach (string line in lines) + this.OutputFile = "..\\sdk\\phapppub.h"; + this.BaseDirectory = "ProcessHacker\\include"; + this.Modes = new[] { "phapppub" }; + this.Files = new[] { - string[] split = line.Split(new char[] { '=' }, 2); - - switch (split[0]) - { - case "base": - _baseDirectory = split[1]; - break; - case "modes": - _modes = split[1].ToLowerInvariant().Split(';'); - 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; - } - } + "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" + }; + 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) @@ -104,15 +108,15 @@ private List ProcessHeaderLines(IEnumerable lines) } else { - bool blockMode = _modes.Any(modes.Contains); - bool lineMode = _modes.Any(mode => - { - int indexOfMarker = s.LastIndexOf("// " + mode); - if (indexOfMarker == -1) - return false; + 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 == '/'); - }); + return s.Substring(indexOfMarker).Trim().All(c => char.IsLetterOrDigit(c) || c == ' ' || c == '/'); + }); if (blockMode || lineMode) { @@ -136,34 +140,31 @@ public void Execute() { // Read in all header files. - var headerFiles = _files.Select(fileName => + var headerFiles = this.Files.Select(fileName => { - var fullFileName = _baseDirectory + "\\" + 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); + }) + .ToDictionary(h => h.Name); - foreach (var h in headerFiles.Values) + 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)) - { - HeaderFile d; + var partitions = h.Lines.Select(s => + { + var trimmed = s.Trim().ToLowerInvariant(); - if (headerFiles.TryGetValue(trimmed.Remove(trimmed.Length - 1).Remove(0, "#include <".Length), out d)) - return Tuple.Create(s, d); - else - return Tuple.Create(s, null); - } - return Tuple.Create(s, null); - }) - .ToLookup(p => p.Item2 != null); + 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(); @@ -174,20 +175,20 @@ public void Execute() // Generate the ordering. - var orderedHeaderFiles = OrderHeaderFiles(_files.Select(s => headerFiles[Path.GetFileName(s).ToLower()]).ToList()); + var orderedHeaderFiles = OrderHeaderFiles(this.Files.Select(s => headerFiles[Path.GetFileName(s).ToLower()]).ToList()); // Process each header file and remove irrelevant content. - foreach (var h in orderedHeaderFiles) + foreach (HeaderFile h in orderedHeaderFiles) h.Lines = ProcessHeaderLines(h.Lines); // Write out the result. - StreamWriter sw = new StreamWriter(_baseDirectory + "\\" + _outputFile); + StreamWriter sw = new StreamWriter(this.BaseDirectory + "\\" + this.OutputFile); // Header - sw.Write(_header); + sw.Write(this.Header); // Header files - foreach (var h in orderedHeaderFiles) + foreach (HeaderFile h in orderedHeaderFiles) { //Console.WriteLine("Header file: " + h.Name); sw.WriteLine(); @@ -196,13 +197,13 @@ public void Execute() sw.WriteLine("//"); sw.WriteLine(); - foreach (var line in h.Lines) + foreach (string line in h.Lines) sw.WriteLine(line); } // Footer - sw.Write(_footer); + sw.Write(this.Footer); sw.Close(); } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 18ec017a966b..43dd36a01569 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -32,15 +32,6 @@ public static void Main(string[] args) Build.CleanupBuildEnvironment(); return; } - else if (ProgramArgs.ContainsKey("-updaterev")) - { - if (!Build.InitializeBuildEnvironment(false)) - return; - - Build.ShowBuildEnvironment(false); - Build.UpdateHeaderFileVersion(); - return; - } else if (ProgramArgs.ContainsKey("-phapppub_gen")) { if (!Build.InitializeBuildEnvironment(false)) @@ -103,8 +94,11 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(false); + Build.BuildSecureFiles(); + Build.UpdateHeaderFileVersion(); + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; @@ -146,8 +140,11 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(true); + Build.BuildSecureFiles(); + Build.UpdateHeaderFileVersion(); + if (!Build.BuildSolution("ProcessHacker.sln", true, true, true)) return; @@ -181,8 +178,11 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(false); + Build.BuildSecureFiles(); + Build.UpdateHeaderFileVersion(); + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; @@ -213,6 +213,8 @@ public static void Main(string[] args) if (!Build.BuildSetupExe()) return; + Build.BuildPdbZip(); + Build.BuildSdkZip(); Build.BuildSrcZip(); @@ -223,8 +225,11 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(false); + Build.BuildSecureFiles(); + Build.UpdateHeaderFileVersion(); + if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) return; diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 8f33b1499a7fd4f9a9e0be7dcd40113f12acb90f..28649ca0c27566b3a0dfc7e3bdcec903df5a4071 100644 GIT binary patch delta 16187 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 D^X=}5 delta 15335 zcmbVz34B!L)%Usg&fJ-~lbOs+l9@>+Dy%XBLakqWpzWK&plfP1CQtI;&^&+oQL-ZgN8x_$WKDy<>8PzL}5=~+L5z%`c zwnfaH(56xb$uXZ_yGju&jt9Z z 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 Date: Fri, 5 May 2017 19:24:31 +1000 Subject: [PATCH 052/839] Tidy up some stuff --- plugins/ExtraPlugins/plugin.c | 18 +++++++++++------- .../HardwareDevices.vcxproj.filters | 8 ++++++-- plugins/HardwareDevices/prpsh.c | 19 +++++++++---------- plugins/ToolStatus/customizetb.c | 2 +- .../CustomSetupTool/uninstall.c | 8 +++----- tools/peview/prpsh.c | 2 +- 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/plugins/ExtraPlugins/plugin.c b/plugins/ExtraPlugins/plugin.c index 81bb2e63b9e9..2e9b5bbed56f 100644 --- a/plugins/ExtraPlugins/plugin.c +++ b/plugins/ExtraPlugins/plugin.c @@ -242,13 +242,17 @@ PPH_STRING PluginGetVersionInfo( 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 - ); + PH_FORMAT fileVersionFormat[7]; + + PhInitFormatU(&fileVersionFormat[0], info->dwFileVersionMS >> 16); + PhInitFormatC(&fileVersionFormat[1], '.'); + PhInitFormatU(&fileVersionFormat[2], info->dwFileVersionMS & 0xffff); + PhInitFormatC(&fileVersionFormat[3], '.'); + PhInitFormatU(&fileVersionFormat[4], info->dwFileVersionLS >> 16); + PhInitFormatC(&fileVersionFormat[5], '.'); + PhInitFormatU(&fileVersionFormat[6], info->dwFileVersionLS & 0xffff); + + version = PhFormat(fileVersionFormat, 7, 30); } } diff --git a/plugins/HardwareDevices/HardwareDevices.vcxproj.filters b/plugins/HardwareDevices/HardwareDevices.vcxproj.filters index 2a5f122356f7..7edc128bdafd 100644 --- a/plugins/HardwareDevices/HardwareDevices.vcxproj.filters +++ b/plugins/HardwareDevices/HardwareDevices.vcxproj.filters @@ -24,7 +24,9 @@ Header Files - + + Header Files + @@ -63,7 +65,9 @@ Source Files\Disk - + + Source Files + diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index 517f1aa9961c..2aa401d4d246 100644 --- a/plugins/HardwareDevices/prpsh.c +++ b/plugins/HardwareDevices/prpsh.c @@ -124,7 +124,7 @@ INT CALLBACK PvpPropSheetProc( { if (lParam) { - if (((DLGTEMPLATEEX *)lParam)->signature == 0xffff) + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) { ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; } @@ -227,26 +227,25 @@ LRESULT CALLBACK PvpPropSheetWndProc( } 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. @@ -256,7 +255,7 @@ BOOLEAN PhpInitializePropSheetLayoutStage1( PhLoadWindowPlacementFromSetting(SETTING_NAME_DISK_POSITION, SETTING_NAME_DISK_SIZE, hwnd); - propSheetContext->LayoutInitialized = TRUE; + PropSheetContext->LayoutInitialized = TRUE; return TRUE; } @@ -385,7 +384,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/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index e376e26f749d..cef8fdffbb33 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -840,7 +840,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( if (isFocused) { - FillRect(bufferDc, &bufferRect, context->BrushHot); // leak + FillRect(bufferDc, &bufferRect, context->BrushHot); //FrameRect(bufferDc, &bufferRect, GetStockBrush(BLACK_BRUSH)); if (!itemContext->IsVirtual) diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index da0493de2d9b..4de1817f7517 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -48,11 +48,9 @@ VOID ShowUninstallErrorDialog( ); NTSTATUS SetupUninstallBuild( - _In_ PVOID Context + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context ) { - PPH_SETUP_UNINSTALL_CONTEXT context = Context; - SetupFindInstallDirectory(); // Stop Process Hacker. @@ -76,11 +74,11 @@ NTSTATUS SetupUninstallBuild( // Remove the ARP uninstall entry. SetupDeleteUninstallKey(); - ShowUninstallCompleteDialog(context); + ShowUninstallCompleteDialog(Context); return STATUS_SUCCESS; CleanupExit: - ShowUninstallErrorDialog(context); + ShowUninstallErrorDialog(Context); return STATUS_SUCCESS; } diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index ab84021aa15b..e6ac16b05e8e 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -127,7 +127,7 @@ INT CALLBACK PvpPropSheetProc( { if (lParam) { - if (((DLGTEMPLATEEX *)lParam)->signature == 0xffff) + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) { ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; } From 32a65912c32514ffaec78faffa47012b1fec2057 Mon Sep 17 00:00:00 2001 From: lucasg Date: Fri, 5 May 2017 22:22:07 +0200 Subject: [PATCH 053/839] [cfg] Fix typos when parsing CFG infos in PE load config (#136) There was some uninspired copy-pastes when adding support for CFG parsing for wow64 PE. --- phlib/mapimg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phlib/mapimg.c b/phlib/mapimg.c index b949e3aefc39..9210e2e351c9 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1277,7 +1277,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardAdressIatEntries = config64->GuardAddressTakenIatEntryCount; CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(config64->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardAddressTakenIatEntryTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1310,7 +1310,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardLongJumpEntries = config64->GuardLongJumpTargetCount; CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(config64->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardLongJumpTargetTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1397,7 +1397,7 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->NumberOfGuardAdressIatEntries = config32->GuardAddressTakenIatEntryCount; CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(config32->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardAddressTakenIatEntryTable, MappedImage->NtHeaders32->OptionalHeader.ImageBase), NULL ); @@ -1430,7 +1430,7 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->NumberOfGuardLongJumpEntries = config32->GuardLongJumpTargetCount; CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(config32->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardLongJumpTargetTable, MappedImage->NtHeaders32->OptionalHeader.ImageBase), NULL ); From 233d227f0e9dfbfa721a664093ae5fcd6b295ca9 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 6 May 2017 06:39:30 +1000 Subject: [PATCH 054/839] NetworkTools: Update default geoip database directory --- plugins/NetworkTools/country.c | 4 +-- plugins/NetworkTools/main.c | 57 +--------------------------------- plugins/NetworkTools/options.c | 7 +---- plugins/NetworkTools/update.c | 21 ++----------- 4 files changed, 5 insertions(+), 84 deletions(-) diff --git a/plugins/NetworkTools/country.c b/plugins/NetworkTools/country.c index 147a7ac900ab..f4d0ff2e3078 100644 --- a/plugins/NetworkTools/country.c +++ b/plugins/NetworkTools/country.c @@ -32,10 +32,8 @@ VOID LoadGeoLiteDb( ) { PPH_STRING path; - PPH_STRING directory; - directory = PH_AUTO(PhGetApplicationDirectory()); - path = PhConcatStrings(2, PhGetString(directory), L"Plugins\\plugindata\\GeoLite2-Country.mmdb"); + path = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); if (MMDB_open(PhGetString(path), MMDB_MODE_MMAP, &GeoDb) == MMDB_SUCCESS) { diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 129ffafa72c9..9cd8d0992746 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -224,62 +224,7 @@ VOID NTAPI MenuItemCallback( } 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); - } - } - } - } + ShowGeoIPUpdateDialog(NULL); break; } } diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index e0483f80af03..142402753dd7 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -53,12 +53,7 @@ INT_PTR CALLBACK OptionsDlgProc( } break; case IDC_GEOIP: - { - if (PhGetOwnTokenAttributes().Elevated) - { - ShowGeoIPUpdateDialog(NULL); - } - } + ShowGeoIPUpdateDialog(NULL); break; } } diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index b4604b58a20c..36a113ec6b45 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -580,13 +580,11 @@ NTSTATUS GeoIPUpdateThread( } PPH_STRING path; - PPH_STRING directory; PPH_STRING fullSetupPath; PPH_BYTES mmdbGzPath; gzFile file; - directory = PH_AUTO(PhGetApplicationDirectory()); - path = PhConcatStrings(2, PhGetString(directory), L"Plugins\\plugindata\\GeoLite2-Country.mmdb"); + path = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); mmdbGzPath = PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath)); if (RtlDoesFileExists_U(PhGetString(path))) @@ -724,21 +722,6 @@ LRESULT CALLBACK TaskDialogSubclassProc( 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; } return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); @@ -852,7 +835,7 @@ VOID ShowGeoIPUpdateDialog( _In_opt_ HWND Parent ) { - HANDLE threadHandle = NULL; + HANDLE threadHandle; if (threadHandle = PhCreateThread(0, GeoIPUpdateDialogThread, Parent)) NtClose(threadHandle); From d498091d7f91fcf7382eac90fcc446f33fb43bdb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 6 May 2017 07:59:01 +1000 Subject: [PATCH 055/839] Add token properties page listview settings --- ProcessHacker/settings.c | 2 ++ ProcessHacker/tokprp.c | 18 +++++------------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index d57fafd97072..5412d4960e18 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -184,7 +184,9 @@ VOID PhSettingsInitialization( PhpAddStringSetting(L"ThreadTreeListSort", L"1,2"); // 1, DescendingSortOrder PhpAddStringSetting(L"ThreadStackListViewColumns", L""); PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,380"); + PhpAddStringSetting(L"TokenGroupsListViewColumns", L""); PhpAddIntegerSetting(L"TokenSplitterPosition", L"250"); + PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms // Colors are specified with R in the lowest byte, then G, then B. diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index a80fc3f4aac8..897ba39e005e 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -442,6 +442,8 @@ INT_PTR CALLBACK PhpTokenPageProc( ExtendedListView_SetItemColorFunction(groupsLv, PhpTokenGroupColorFunction); PhSetExtendedListView(privilegesLv); ExtendedListView_SetItemColorFunction(privilegesLv, PhpTokenPrivilegeColorFunction); + PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", groupsLv); + PhLoadListViewColumnsFromSetting(L"TokenPrivilegesListViewColumns", privilegesLv); SetDlgItemText(hwndDlg, IDC_USER, L"Unknown"); SetDlgItemText(hwndDlg, IDC_USERSID, L"Unknown"); @@ -589,6 +591,9 @@ INT_PTR CALLBACK PhpTokenPageProc( break; case WM_DESTROY: { + PhSaveListViewColumnsToSetting(L"TokenGroupsListViewColumns", tokenPageContext->GroupsListViewHandle); + PhSaveListViewColumnsToSetting(L"TokenPrivilegesListViewColumns", tokenPageContext->PrivilegesListViewHandle); + if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); if (tokenPageContext->HSplitterContext) PhDeleteHSplitter(tokenPageContext->HSplitterContext); @@ -890,19 +895,6 @@ INT_PTR CALLBACK PhpTokenPageProc( { case PSN_QUERYINITIALFOCUS: { - if (ListView_GetItemCount(tokenPageContext->GroupsListViewHandle) != 0) - { - ListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 0, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(tokenPageContext->GroupsListViewHandle, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - if (ListView_GetItemCount(tokenPageContext->PrivilegesListViewHandle) != 0) - { - ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 0, LVSCW_AUTOSIZE); - ListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 1, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(tokenPageContext->PrivilegesListViewHandle, 2, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_SESSIONID)); return TRUE; } From cefcd61fbcec1baaed3eda3ff71cd817526d9e62 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 02:54:30 +1000 Subject: [PATCH 056/839] CustomBuildTool: Fix pdb releases, Add cleanup script, Add appx package build script --- .gitignore | 9 +- build/build_appx_package.cmd | 7 + build/build_clean.cmd | 5 + build/build_debug.cmd | 4 +- build/build_release.cmd | 6 +- build/build_sdk.cmd | 4 +- build/build_sdk_rebuild.cmd | 4 +- plugins/OnlineChecks/onlnchk.h | 2 +- tools/CustomBuildTool/CustomBuildTool.csproj | 13 + .../Properties/Resources.Designer.cs | 83 +++++ .../CustomBuildTool/Properties/Resources.resx | 164 +++++++++ tools/CustomBuildTool/Source Files/Build.cs | 312 +++++++++++++++--- tools/CustomBuildTool/Source Files/Program.cs | 176 ++++++---- tools/CustomBuildTool/Source Files/Zip.cs | 31 +- .../bin/Release/CustomBuildTool.exe | Bin 146432 -> 155648 bytes .../bin/Release/CustomBuildTool.pdb | Bin 62976 -> 71168 bytes .../resources/ProcessHacker.ico | Bin 97449 -> 0 bytes .../CustomSetupTool/resource.rc | 2 +- 18 files changed, 688 insertions(+), 134 deletions(-) create mode 100644 build/build_appx_package.cmd create mode 100644 build/build_clean.cmd create mode 100644 tools/CustomBuildTool/Properties/Resources.Designer.cs create mode 100644 tools/CustomBuildTool/Properties/Resources.resx delete mode 100644 tools/CustomBuildTool/resources/ProcessHacker.ico diff --git a/.gitignore b/.gitignore index 7f672b4c792c..c8bba02b98ab 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,7 @@ *.key # Build results -*_i.c -*_p.c +*.appx *.ilk *.meta *.obj @@ -24,6 +23,8 @@ *.tlh *.tmp *.vspscc +*_i.c +*_p.c # Visual C++ cache files *.aps @@ -52,7 +53,6 @@ publish [Bb]in [Oo]bj *.Cache -ClientBin ~$* # Backup & report files @@ -70,7 +70,8 @@ Desktop.ini ## Project specific rules ########################## -build/installer/*.exe +build/*.exe +build/*.zip ProcessHacker/include/phapprev.h ProcessHacker/sdk/phapppub.h plugins-extra/ diff --git a/build/build_appx_package.cmd b/build/build_appx_package.cmd new file mode 100644 index 000000000000..54a1f86cb0c3 --- /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 \ No newline at end of file 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 7a06cbedd774..8a9027bb842f 100644 --- a/build/build_debug.cmd +++ b/build/build_debug.cmd @@ -1,5 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -start /B /W "" "..\tools\CustomBuildTool\bin\Release\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 c0a55c13da5d..000b928d35a4 100644 --- a/build/build_release.cmd +++ b/build/build_release.cmd @@ -1,5 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-release" +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-release" -pause \ No newline at end of file +pause diff --git a/build/build_sdk.cmd b/build/build_sdk.cmd index 0bed04835688..c3dc6044491f 100644 --- a/build/build_sdk.cmd +++ b/build/build_sdk.cmd @@ -1,3 +1,5 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-sdk" \ No newline at end of file +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-sdk" diff --git a/build/build_sdk_rebuild.cmd b/build/build_sdk_rebuild.cmd index 57ead1eee13e..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\..\" -start /B /W "" "..\tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleansdk" +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleansdk" pause diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 498933978ecd..b53854ca4e17 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -41,7 +41,7 @@ #define SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED (PLUGIN_NAME L".EnableVirusTotalScanning") #define SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS (PLUGIN_NAME L".VirusTotalHighlightDetection") -#ifdef VIRUSTOTAL_API +#ifdef PH_BUILD_API #include "virustotal.h" #else #define VIRUSTOTAL_URLPATH L"" diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index b981e8ec1f99..a5f7110ee6c5 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -40,6 +40,7 @@ 4 false false + true AnyCPU @@ -52,6 +53,7 @@ 4 false true + true CustomBuildTool.Program @@ -76,6 +78,11 @@ + + True + True + Resources.resx + @@ -108,6 +115,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"> + <Identity + Name="ProcessHacker" + Version="PH_APPX_VERSION" + ProcessorArchitecture="x86" + Publisher="CN=ProcessHacker, O=ProcessHacker, C=AU" /> + <Properties> + <DisplayName>Process Hacker</DisplayName> + <PublisherDisplayName>Process Hacker</PublisherDisplayName> + <Description>ProcessHacker</Description> + <Logo>Assets\ProcessHacker-48.png</Logo> + </Properties> + <Resources> + <Resource Language="en-US" /> + </Resources> + <Dependencies> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.15063.0" /> + </Dependencies> + <Capabilities> + <rescap:Capability Name="runFullTrust"/> + </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/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 835b79b8565e..3963a1f96c54 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -2,6 +2,7 @@ using System.IO; using System.Net.Http; using System.Security; +using System.Security.Cryptography.X509Certificates; using System.Text; namespace CustomBuildTool @@ -12,8 +13,9 @@ public static class Build private static DateTime TimeStart; private static bool BuildNightly = false; private static string BuildBranch = "master"; - private static string BuildOutputFolder = "ClientBin"; + private static string BuildOutputFolder = "build"; private static string BuildVersion; + private static string BuildLongVersion; private static string BuildRevision; private static string BuildMessage; private static long BuildSetupFileLength; @@ -23,7 +25,9 @@ public static class Build private static string BuildSetupSig; private static string GitExePath; private static string MSBuildExePath; - private static string CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; + private static string CertUtilExePath; + private static string MakeAppxExePath; + private static string CustomSignToolPath; #region Build Config private static readonly string[] sdk_directories = @@ -118,6 +122,9 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) TimeStart = DateTime.Now; GitExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles%\\Git\\cmd\\git.exe"); MSBuildExePath = Environment.ExpandEnvironmentVariables(VisualStudio.GetMsbuildFilePath()); + CertUtilExePath = Environment.ExpandEnvironmentVariables("%SystemRoot%\\system32\\Certutil.exe"); + MakeAppxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeAppx.exe"); + CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; BuildNightly = !string.Equals(Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%"), "%APPVEYOR_BUILD_API%", StringComparison.OrdinalIgnoreCase); try @@ -181,11 +188,31 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) public static void CleanupBuildEnvironment() { - Program.PrintColorMessage("Cleaning up build environment...", ConsoleColor.Cyan); + string[] cleanupFileArray = + { + BuildOutputFolder + "\\processhacker-build-setup.exe", + BuildOutputFolder + "\\processhacker-build-bin.zip", + BuildOutputFolder + "\\processhacker-build-src.zip", + BuildOutputFolder + "\\processhacker-build-sdk.zip", + BuildOutputFolder + "\\processhacker-build-pdb.zip", + BuildOutputFolder + "\\processhacker-build-package.appx", + BuildOutputFolder + "\\processhacker-build-checksums.txt", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-src.zip", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-sdk.zip", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-pdb.zip", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-package.appx", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" + }; try { - + for (int i = 0; i < cleanupFileArray.Length; i++) + { + if (File.Exists(cleanupFileArray[i])) + File.Delete(cleanupFileArray[i]); + } } catch (Exception ex) { @@ -205,7 +232,8 @@ public static void ShowBuildEnvironment(bool ShowBuildInfo) else BuildRevision = latestGitRevision.Trim(); - BuildVersion = "3.0." + BuildRevision; // TODO: Remove hard-coded major/minor version. + BuildVersion = "3.0." + BuildRevision; + BuildLongVersion = "3.0.0." + BuildRevision; BuildMessage = Win32.ExecCommand(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); if (ShowBuildInfo) @@ -305,21 +333,27 @@ public static bool CopyKProcessHacker(bool DebugBuild) return true; } - public static bool CopyLibFiles(bool DebugBuild) + public static bool CopyLibFiles(BuildFlags Flags) { Program.PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); try { - if (DebugBuild) + if (Flags.HasFlag(BuildFlags.BuildDebug)) { - File.Copy("bin\\Debug32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); - File.Copy("bin\\Debug64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + if (Flags.HasFlag(BuildFlags.Build32bit)) + File.Copy("bin\\Debug32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + + if (Flags.HasFlag(BuildFlags.Build64bit)) + File.Copy("bin\\Debug64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); } else { - File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); - File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + if (Flags.HasFlag(BuildFlags.Build32bit)) + File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + + if (Flags.HasFlag(BuildFlags.Build64bit)) + File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); } } catch (Exception ex) @@ -331,13 +365,13 @@ public static bool CopyLibFiles(bool DebugBuild) return true; } - public static bool CopyWow64Files(bool DebugBuild) + public static bool CopyWow64Files(BuildFlags Flags) { Program.PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); try { - if (DebugBuild) + if (Flags.HasFlag(BuildFlags.BuildDebug)) { if (!Directory.Exists("bin\\Debug64\\x86")) Directory.CreateDirectory("bin\\Debug64\\x86"); @@ -356,10 +390,14 @@ public static bool CopyWow64Files(bool DebugBuild) if (!Directory.Exists("bin\\Release64\\x86\\plugins")) Directory.CreateDirectory("bin\\Release64\\x86\\plugins"); - File.Copy("bin\\Release32\\ProcessHacker.exe", "bin\\Release64\\x86\\ProcessHacker.exe", true); - File.Copy("bin\\Release32\\ProcessHacker.pdb", "bin\\Release64\\x86\\ProcessHacker.pdb", true); - File.Copy("bin\\Release32\\plugins\\DotNetTools.dll", "bin\\Release64\\x86\\plugins\\DotNetTools.dll", true); - File.Copy("bin\\Release32\\plugins\\DotNetTools.pdb", "bin\\Release64\\x86\\plugins\\DotNetTools.pdb", true); + if (File.Exists("bin\\Release32\\ProcessHacker.exe")) + File.Copy("bin\\Release32\\ProcessHacker.exe", "bin\\Release64\\x86\\ProcessHacker.exe", true); + if (File.Exists("bin\\Release32\\ProcessHacker.pdb")) + File.Copy("bin\\Release32\\ProcessHacker.pdb", "bin\\Release64\\x86\\ProcessHacker.pdb", true); + if (File.Exists("bin\\Release32\\plugins\\DotNetTools.dll")) + File.Copy("bin\\Release32\\plugins\\DotNetTools.dll", "bin\\Release64\\x86\\plugins\\DotNetTools.dll", true); + if (File.Exists("bin\\Release32\\plugins\\DotNetTools.pdb")) + File.Copy("bin\\Release32\\plugins\\DotNetTools.pdb", "bin\\Release64\\x86\\plugins\\DotNetTools.pdb", true); } } catch (Exception ex) @@ -459,6 +497,177 @@ public static bool FixupResourceHeader() return true; } +#region Appx Package + public static void BuildAppxPackage() + { + Program.PrintColorMessage("Building processhacker-build-package.appx...", ConsoleColor.Cyan); + + try + { + StringBuilder sb = new StringBuilder(0x100); + int startIndex = "bin\\Release64\\".Length; + string[] filesToAdd; + + File.WriteAllText("build\\AppxManifest.xml", Properties.Resources.AppxManifest.Replace("PH_APPX_VERSION", BuildLongVersion)); + + sb.AppendLine("[Files]"); + sb.AppendLine("\"build\\AppxManifest.xml\" \"AppxManifest.xml\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-44.png\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-48.png\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-150.png\""); + + filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); + + for (int i = 0; i < filesToAdd.Length; i++) + { + string filePath = filesToAdd[i]; + + // 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; + } + + sb.AppendLine("\"" + filePath + "\" \"" + filePath.Substring(startIndex) + "\""); + } + + File.WriteAllText("build\\package.map", sb.ToString()); + + string error = Win32.ExecCommand( + MakeAppxExePath, + "pack /o /f build\\package.map /p " + BuildOutputFolder + "\\processhacker-build-package.appx" + ); + + if (File.Exists("build\\AppxManifest.xml")) + File.Delete("build\\AppxManifest.xml"); + if (File.Exists("build\\package.map")) + File.Delete("build\\package.map"); + + if (string.IsNullOrEmpty(error) || !error.EndsWith("Package creation succeeded.", StringComparison.OrdinalIgnoreCase)) + { + Program.PrintColorMessage("[ERROR] " + error, ConsoleColor.Red); + return; + } + + string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); + + error = Win32.ExecCommand( + signToolExePath, + "sign /v /fd SHA256 /a /f build\\appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" + ); + + if (string.IsNullOrEmpty(error) || error.Contains("Successfully signed")) + { + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.DarkGray); + return; + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + } + } + + public static bool BuildAppxSignature() + { + Program.PrintColorMessage("Building Appx Signature...", ConsoleColor.Cyan); + + var makeCertExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeCert.exe"); + var pvk2PfxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\Pvk2Pfx.exe"); + + try + { + if (File.Exists("build\\processhacker-appx.pvk")) + File.Delete("build\\processhacker-appx.pvk"); + if (File.Exists("build\\processhacker-appx.cer")) + File.Delete("build\\processhacker-appx.cer"); + if (File.Exists("build\\processhacker-appx.pfx")) + File.Delete("build\\processhacker-appx.pfx"); + + string output = Win32.ExecCommand(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 " + + "build\\processhacker-appx.pvk " + + "build\\processhacker-appx.cer " + ); + + if (string.IsNullOrEmpty(output) || !output.Equals("Succeeded", StringComparison.OrdinalIgnoreCase)) + { + Program.PrintColorMessage("[MakeCert] " + output, ConsoleColor.Red); + return false; + } + + output = Win32.ExecCommand(pvk2PfxExePath, + "/pvk build\\processhacker-appx.pvk " + + "/spc build\\processhacker-appx.cer " + + "/pfx build\\processhacker-appx.pfx " + ); + + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[Pvk2Pfx] " + output, ConsoleColor.Red); + return false; + } + + output = Win32.ExecCommand(CertUtilExePath, + "-addStore TrustedPeople build\\processhacker-appx.cer" + ); + + if (string.IsNullOrEmpty(output) || !output.Contains("command completed successfully")) + { + Program.PrintColorMessage("[Certutil] " + output, ConsoleColor.Red); + return false; + } + } + 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); + } + } + + if (File.Exists("build\\processhacker-appx.pvk")) + File.Delete("build\\processhacker-appx.pvk"); + if (File.Exists("build\\processhacker-appx.cer")) + File.Delete("build\\processhacker-appx.cer"); + if (File.Exists("build\\processhacker-appx.pfx")) + File.Delete("build\\processhacker-appx.pfx"); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } +#endregion + public static bool BuildKphSignatureFile(bool DebugBuild) { string output; @@ -591,9 +800,9 @@ public static bool BuildSecureFiles() public static bool BuildSetupExe() { - Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); + Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan, true, BuildFlags.BuildVerbose); - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", false, false, false)) + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildVerbose)) return false; try @@ -712,11 +921,7 @@ public static bool BuildPdbZip() if (File.Exists(BuildOutputFolder + "\\processhacker-build-pdb.zip")) File.Delete(BuildOutputFolder + "\\processhacker-build-pdb.zip"); - // *.pdb # Include only PDB files - // Debug32 # Ignore junk directories - // Debug64 - // Obj - //Zip.CreateCompressedFolder("sdk", BuildOutputFolder + "\\processhacker-build-pdb.zip"); + Zip.CreateCompressedPdbFromFolder(".\\", BuildOutputFolder + "\\processhacker-build-pdb.zip"); } catch (Exception ex) { @@ -930,10 +1135,9 @@ public static bool AppveyorUploadBuildFiles() return true; } + public static bool UpdateHeaderFileVersion() { - Program.PrintColorMessage("Updating Process Hacker build revision...", ConsoleColor.Cyan); - try { if (File.Exists("ProcessHacker\\include\\phapprev.h")) @@ -950,7 +1154,7 @@ public static bool UpdateHeaderFileVersion() } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex.ToString(), ConsoleColor.Yellow); + Program.PrintColorMessage("[phapprev] " + ex.ToString(), ConsoleColor.Yellow); return false; } @@ -975,45 +1179,59 @@ public static bool BuildPublicHeaderFiles() return true; } - public static bool BuildSolution(string Solution, bool Build64bit, bool BuildDebug, bool Verbose) + + public static bool BuildSolution(string Solution, BuildFlags Flags) { - if (Verbose) + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { - Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false); - Program.PrintColorMessage("x32", ConsoleColor.Green, false); - Program.PrintColorMessage(")...", ConsoleColor.Cyan); - } + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x32", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); - string error32 = Win32.ExecCommand( - MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=" + (BuildDebug ? "Debug" : "Release") + " /p:Platform=Win32 " + Solution - ); + string error32 = Win32.ExecCommand( + MSBuildExePath, + "/m /nologo /verbosity:quiet /p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " /p:Platform=Win32 " + Solution + ); - if (!string.IsNullOrEmpty(error32)) - { - Program.PrintColorMessage("[ERROR] " + error32, ConsoleColor.Red); - return false; + if (!string.IsNullOrEmpty(error32)) + { + Program.PrintColorMessage("[ERROR] " + error32, ConsoleColor.Red, true, Flags); + return false; + } } - if (Build64bit) + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) { - Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false); - Program.PrintColorMessage("x64", ConsoleColor.Green, false); - Program.PrintColorMessage(")...", ConsoleColor.Cyan); + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x64", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); string error64 = Win32.ExecCommand( MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=" + (BuildDebug ? "Debug" : "Release") + " /p:Platform=x64 " + Solution + "/m /nologo /verbosity:quiet /p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " /p:Platform=x64 /p:ExternalCompilerOptions=PH_BUILD_API " + Solution ); if (!string.IsNullOrEmpty(error64)) { - Program.PrintColorMessage("[ERROR] " + error64, ConsoleColor.Red); + Program.PrintColorMessage("[ERROR] " + error64, ConsoleColor.Red, true, Flags); return false; } } return true; } + + } + + + + [Flags] + public enum BuildFlags + { + None, + Build32bit = 0x1, + Build64bit = 0x2, + BuildDebug = 0x4, + BuildVerbose = 0x8 } } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 43dd36a01569..52e7258d2990 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -7,30 +7,20 @@ public static class Program { public static Dictionary ProgramArgs; - public static void PrintColorMessage(string Message, ConsoleColor Color, bool Newline = true) - { - Console.ForegroundColor = Color; - - if (Newline) - Console.WriteLine(Message); - else - Console.Write(Message); - - Console.ResetColor(); - } - public static void Main(string[] args) { ProgramArgs = ParseArgs(args); if (ProgramArgs.ContainsKey("-cleanup")) { - if (!Build.InitializeBuildEnvironment(false)) + if (!Build.InitializeBuildEnvironment(true)) return; - + Build.CleanupAppxSignature(); + Build.CleanupBuildEnvironment(); - return; + + Build.ShowBuildStats(true); } else if (ProgramArgs.ContainsKey("-phapppub_gen")) { @@ -38,12 +28,10 @@ public static void Main(string[] args) return; Build.BuildPublicHeaderFiles(); - return; } else if (ProgramArgs.ContainsKey("-sign")) { Build.BuildKphSignatureFile(false); - return; } else if (ProgramArgs.ContainsKey("-sdk")) { @@ -52,41 +40,36 @@ public static void Main(string[] args) if (!Build.CopyPluginSdkHeaders()) return; - if (!Build.CopyVersionHeader()) return; - if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(false)) + if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; Build.ShowBuildStats(false); - return; } else if (ProgramArgs.ContainsKey("-cleansdk")) { if (!Build.InitializeBuildEnvironment(false)) return; - if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - + if (!Build.CopyPluginSdkHeaders()) return; - if (!Build.CopyVersionHeader()) return; - if (!Build.FixupResourceHeader()) return; - - if (!Build.CopyLibFiles(false)) + if (!Build.CopyLibFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; Build.ShowBuildStats(false); - return; } else if (ProgramArgs.ContainsKey("-bin")) { @@ -94,41 +77,37 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(false); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); - if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - + if (!Build.BuildKphSignatureFile(false)) return; - if (!Build.CopyTextFiles()) return; - if (!Build.CopyKProcessHacker(false)) return; - if (!Build.CopyPluginSdkHeaders()) return; - if (!Build.CopyVersionHeader()) return; - if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(false)) + if (!Build.CopyLibFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyWow64Files(false)) + if (!Build.CopyWow64Files(BuildFlags.None)) return; - + if (!Build.BuildBinZip()) return; @@ -140,12 +119,11 @@ public static void Main(string[] args) return; Build.ShowBuildEnvironment(true); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); - - if (!Build.BuildSolution("ProcessHacker.sln", true, true, true)) + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; if (!Build.BuildKphSignatureFile(true)) @@ -154,36 +132,38 @@ public static void Main(string[] args) return; if (!Build.CopyKProcessHacker(true)) return; - if (!Build.CopyPluginSdkHeaders()) return; if (!Build.CopyVersionHeader()) return; if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(true)) + if (!Build.CopyLibFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, true, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; - if (!Build.CopyWow64Files(true)) + if (!Build.CopyWow64Files(BuildFlags.BuildDebug)) return; Build.ShowBuildStats(true); } else if (ProgramArgs.ContainsKey("-release")) { - if (!Build.InitializeBuildEnvironment(false)) + if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment(false); - + Build.ShowBuildEnvironment(true); Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); - if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.BuildKphSignatureFile(false)) @@ -192,45 +172,44 @@ public static void Main(string[] args) return; if (!Build.CopyKProcessHacker(false)) return; - if (!Build.CopyPluginSdkHeaders()) return; if (!Build.CopyVersionHeader()) return; if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(false)) + if (!Build.CopyLibFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyWow64Files(false)) + if (!Build.CopyWow64Files(BuildFlags.None)) return; if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) return; - Build.BuildPdbZip(); - Build.BuildSdkZip(); - Build.BuildSrcZip(); + + Build.ShowBuildStats(true); } else if (ProgramArgs.ContainsKey("-appveyor")) { - if (!Build.InitializeBuildEnvironment(false)) + if (!Build.InitializeBuildEnvironment(true)) return; Build.ShowBuildEnvironment(false); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); - if (!Build.BuildSolution("ProcessHacker.sln", true, false, true)) + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.BuildKphSignatureFile(false)) @@ -239,27 +218,27 @@ public static void Main(string[] args) return; if (!Build.CopyKProcessHacker(false)) return; - if (!Build.CopyPluginSdkHeaders()) return; if (!Build.CopyVersionHeader()) return; if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(false)) + if (!Build.CopyLibFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", true, false, true)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyWow64Files(false)) + if (!Build.CopyWow64Files(BuildFlags.None)) return; if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) return; - if (!Build.BuildChecksumsFile()) return; if (!Build.BuildUpdateSignature()) @@ -268,6 +247,50 @@ public static void Main(string[] args) if (Build.AppveyorUploadBuildFiles()) Build.WebServiceUpdateConfig(); } + else if (ProgramArgs.ContainsKey("-appxbuild")) + { + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment(true); + Build.BuildSecureFiles(); + Build.UpdateHeaderFileVersion(); + + if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.CopyTextFiles()) + return; + if (!Build.CopyPluginSdkHeaders()) + return; + if (!Build.CopyVersionHeader()) + return; + if (!Build.FixupResourceHeader()) + return; + if (!Build.CopyLibFiles(BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.CopyWow64Files(BuildFlags.None)) + return; + + Build.BuildAppxPackage(); + + Build.ShowBuildStats(true); + } + else if (ProgramArgs.ContainsKey("-appxmakecert")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.ShowBuildEnvironment(true); + + Build.BuildAppxSignature(); + + Build.ShowBuildStats(true); + } else { Console.WriteLine("Invalid arguments.\n"); @@ -305,5 +328,18 @@ private static Dictionary ParseArgs(string[] args) 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(); + } } } \ No newline at end of file diff --git a/tools/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs index 129c79a707b6..08cc138439fe 100644 --- a/tools/CustomBuildTool/Source Files/Zip.cs +++ b/tools/CustomBuildTool/Source Files/Zip.cs @@ -31,12 +31,6 @@ private static string[] GetEntryNames(string[] names, string sourceFolder, bool public static void CreateCompressedFolder(string sourceDirectoryName, string destinationArchiveFileName) { - if (string.IsNullOrEmpty(sourceDirectoryName)) - throw new ArgumentNullException("sourceDirectoryName"); - - if (string.IsNullOrEmpty(destinationArchiveFileName)) - throw new ArgumentNullException("destinationArchiveFileName"); - string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); @@ -65,5 +59,30 @@ public static void CreateCompressedFolder(string sourceDirectoryName, string des } } } + + public static void CreateCompressedPdbFromFolder(string sourceDirectoryName, string destinationArchiveFileName) + { + string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); + string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + + using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + { + 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/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 28649ca0c27566b3a0dfc7e3bdcec903df5a4071..dcbe0cfce5d8a9b4b677332a982064ef728fe15c 100644 GIT binary patch delta 29963 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 /// Looks up a localized string similar to <?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"> - /// <Identity - /// Name="ProcessHacker" - /// Version="PH_APPX_VERSION" - /// ProcessorArchitecture="x86" - /// Publisher="CN=ProcessHacker, O=ProcessHacker, C=AU" /> - /// <Properties> - /// <Displa [rest of string was truncated]";. + /// 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" + /// ProcessorArchi [rest of string was truncated]";. /// internal static string AppxManifest { get { diff --git a/tools/CustomBuildTool/Properties/Resources.resx b/tools/CustomBuildTool/Properties/Resources.resx index 65d0c238f84a..083dd1a65c62 100644 --- a/tools/CustomBuildTool/Properties/Resources.resx +++ b/tools/CustomBuildTool/Properties/Resources.resx @@ -120,23 +120,24 @@ <?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"> - <Identity - Name="ProcessHacker" - Version="PH_APPX_VERSION" - ProcessorArchitecture="x86" - Publisher="CN=ProcessHacker, O=ProcessHacker, C=AU" /> - <Properties> - <DisplayName>Process Hacker</DisplayName> - <PublisherDisplayName>Process Hacker</PublisherDisplayName> - <Description>ProcessHacker</Description> - <Logo>Assets\ProcessHacker-48.png</Logo> - </Properties> - <Resources> - <Resource Language="en-US" /> - </Resources> + 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.14316.0" MaxVersionTested="10.0.15063.0" /> </Dependencies> @@ -145,18 +146,18 @@ </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> + <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> diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 779095ae0fc3..12ea6f115927 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -196,19 +196,20 @@ public static void CleanupBuildEnvironment() BuildOutputFolder + "\\processhacker-build-src.zip", BuildOutputFolder + "\\processhacker-build-sdk.zip", BuildOutputFolder + "\\processhacker-build-pdb.zip", - BuildOutputFolder + "\\processhacker-build-package.appx", BuildOutputFolder + "\\processhacker-build-checksums.txt", + BuildOutputFolder + "\\processhacker-build-package.appxbundle", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-src.zip", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-sdk.zip", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-pdb.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-package.appx", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" }; try { + Directory.Delete("build\\output", true); + for (int i = 0; i < cleanupFileArray.Length; i++) { if (File.Exists(cleanupFileArray[i])) @@ -490,6 +491,67 @@ public static bool CopyVersionHeader() return true; } + public static bool CopyRedistFiles(BuildFlags Flags) + { + string dbghelp32RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x86\\dbghelp.dll"); + string dbghelp64RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); + string symsrv32RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x86\\symsrv.dll"); + string symsrv64RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x64\\symsrv.dll"); + + Program.PrintColorMessage("Copying redist files...", ConsoleColor.Cyan); + + try + { + if (Flags.HasFlag(BuildFlags.BuildDebug)) + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + if (File.Exists(dbghelp32RedistDll)) + File.Copy(dbghelp32RedistDll, "bin\\Debug32\\dbghelp.dll", true); + + if (File.Exists(symsrv32RedistDll)) + File.Copy(symsrv32RedistDll, "bin\\Debug32\\symsrv.dll", true); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + if (File.Exists(dbghelp64RedistDll)) + File.Copy(dbghelp64RedistDll, "bin\\Debug64\\dbghelp.dll", true); + + if (File.Exists(symsrv64RedistDll)) + File.Copy(symsrv64RedistDll, "bin\\Debug64\\symsrv.dll", true); + } + } + else + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + if (File.Exists(dbghelp32RedistDll)) + File.Copy(dbghelp32RedistDll, "bin\\Release32\\dbghelp.dll", true); + + if (File.Exists(symsrv32RedistDll)) + File.Copy(symsrv32RedistDll, "bin\\Release32\\symsrv.dll", true); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + if (File.Exists(dbghelp64RedistDll)) + File.Copy(dbghelp64RedistDll, "bin\\Release64\\dbghelp.dll", true); + + if (File.Exists(symsrv64RedistDll)) + File.Copy(symsrv64RedistDll, "bin\\Release64\\symsrv.dll", true); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + public static bool FixupResourceHeader() { Program.PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); @@ -1063,65 +1125,148 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) } #region Appx Package - public static void BuildAppxPackage() + public static void BuildAppxPackage(BuildFlags Flags) { - Program.PrintColorMessage("Building processhacker-build-package.appx...", ConsoleColor.Cyan); + string error; + string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); + + Program.PrintColorMessage("Building processhacker-build-package.appxbundle...", ConsoleColor.Cyan); try { - StringBuilder sb = new StringBuilder(0x100); - int startIndex = "bin\\Release64\\".Length; - string[] filesToAdd; + if (!Directory.Exists("build\\output")) + Directory.CreateDirectory("build\\output"); - File.WriteAllText("build\\AppxManifest.xml", Properties.Resources.AppxManifest.Replace("PH_APPX_VERSION", BuildLongVersion)); + if (File.Exists(BuildOutputFolder + "\\processhacker-build-package.appxbundle")) + File.Delete(BuildOutputFolder + "\\processhacker-build-package.appxbundle"); - sb.AppendLine("[Files]"); - sb.AppendLine("\"build\\AppxManifest.xml\" \"AppxManifest.xml\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-44.png\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-48.png\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-150.png\""); + Win32.ImageResizeFile(44, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-44.png"); + Win32.ImageResizeFile(50, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-50.png"); + Win32.ImageResizeFile(150, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-150.png"); - filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); + 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("build\\output\\AppxManifest32.xml", appxManifestString); + + // create the package mapping file + StringBuilder packageMap32 = new StringBuilder(0x100); + packageMap32.AppendLine("[Files]"); + packageMap32.AppendLine("\"build\\output\\AppxManifest32.xml\" \"AppxManifest.xml\""); + packageMap32.AppendLine("\"build\\output\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); + packageMap32.AppendLine("\"build\\output\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); + packageMap32.AppendLine("\"build\\output\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + + var filesToAdd = Directory.GetFiles("bin\\Release32", "*", SearchOption.AllDirectories); + for (int i = 0; i < filesToAdd.Length; i++) + { + string filePath = filesToAdd[i]; + + // 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("build\\output\\package32.map", packageMap32.ToString()); + + // create the package + error = Win32.ExecCommand( + MakeAppxExePath, + "pack /o /f build\\output\\package32.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + + // sign the package + error = Win32.ExecCommand( + signToolExePath, + "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + } - for (int i = 0; i < filesToAdd.Length; i++) + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) { - string filePath = filesToAdd[i]; - - // 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)) + // 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("build\\output\\AppxManifest64.xml", appxManifestString); + + StringBuilder packageMap64 = new StringBuilder(0x100); + packageMap64.AppendLine("[Files]"); + packageMap64.AppendLine("\"build\\output\\AppxManifest64.xml\" \"AppxManifest.xml\""); + packageMap64.AppendLine("\"build\\output\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); + packageMap64.AppendLine("\"build\\output\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); + packageMap64.AppendLine("\"build\\output\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + + var filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); + for (int i = 0; i < filesToAdd.Length; i++) { - continue; + string filePath = filesToAdd[i]; + + // 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) + "\""); } - - sb.AppendLine("\"" + filePath + "\" \"" + filePath.Substring(startIndex) + "\""); + File.WriteAllText("build\\output\\package64.map", packageMap64.ToString()); + + // create the package + error = Win32.ExecCommand( + MakeAppxExePath, + "pack /o /f build\\output\\package64.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + + // sign the package + error = Win32.ExecCommand( + signToolExePath, + "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } - File.WriteAllText("build\\package.map", sb.ToString()); - - string error = Win32.ExecCommand( - MakeAppxExePath, - "pack /o /f build\\package.map /p " + BuildOutputFolder + "\\processhacker-build-package.appx" - ); - - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); - - if (File.Exists("build\\AppxManifest.xml")) - File.Delete("build\\AppxManifest.xml"); - if (File.Exists("build\\package.map")) - File.Delete("build\\package.map"); - - string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); - - error = Win32.ExecCommand( - signToolExePath, - "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" - ); + { + // create the appx bundle map + StringBuilder bundleMap = new StringBuilder(0x100); + bundleMap.AppendLine("[Files]"); + bundleMap.AppendLine("\"" + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx\" \"processhacker-build-package-x32.appx\""); + bundleMap.AppendLine("\"" + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx\" \"processhacker-build-package-x64.appx\""); + File.WriteAllText("build\\output\\bundle.map", bundleMap.ToString()); + + // create the appx bundle package + error = Win32.ExecCommand( + MakeAppxExePath, + "bundle /f build\\output\\bundle.map /p " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + + // sign the appx bundle package + error = Win32.ExecCommand( + signToolExePath, + "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + } - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + Directory.Delete("build\\output", true); } catch (Exception ex) { diff --git a/tools/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs index f173c3cff2fa..865a24548d52 100644 --- a/tools/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -30,6 +30,21 @@ public static string ExecCommand(string FileName, string args) return output; } + 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 const int STD_OUTPUT_HANDLE = -11; public const int STD_INPUT_HANDLE = -10; public const int STD_ERROR_HANDLE = -12; diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 8245aef57288..b70f7ea3cc08 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -51,6 +51,11 @@ public static void Main(string[] args) if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; + Build.CopyRedistFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + ); + Build.ShowBuildStats(false); } else if (ProgramArgs.ContainsKey("-cleansdk")) @@ -154,6 +159,10 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.BuildDebug)) return; + Build.CopyRedistFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose); + Build.ShowBuildStats(true); } else if (ProgramArgs.ContainsKey("-release")) @@ -192,6 +201,8 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.None)) return; + Build.CopyRedistFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); + if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) @@ -259,7 +270,7 @@ public static void Main(string[] args) Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); - if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.CopyTextFiles()) @@ -270,16 +281,16 @@ public static void Main(string[] args) return; if (!Build.FixupResourceHeader()) return; - if (!Build.CopyLibFiles(BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.CopyWow64Files(BuildFlags.None)) return; - Build.BuildAppxPackage(); + Build.BuildAppxPackage(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); Build.ShowBuildStats(true); } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 6071333ab93cf8ba6e32052da998c7ef9a4dfe6d..f64000cd50f9ba6d558ea03818794a11b5969893 100644 GIT binary patch delta 23423 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 delta 20132 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 O@%I6nc9bZGmH!7ZG%YUx diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 36e68b0ff3c85f8f83f4682ae074f6078778e0ce..4e225c154ca5823753ffbbae3475884e2ccee083 100644 GIT binary patch delta 20967 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 eaiegfanBdbjy9tTTl6wZ&muURe|PJ5^Zx

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 AX8-^I diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 3a349180c3a3..3ffc04f2b300 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -281,7 +281,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS // RCDATA // -IDR_BIN_DATA RCDATA "..\\..\\..\\ClientBin\\processhacker-build-bin.zip" +IDR_BIN_DATA RCDATA "..\\..\\..\\build\\processhacker-build-bin.zip" IDR_LICENCE_DATA RCDATA "resources\\Licence.txt" From 53de62e53204b9a166a1b2e9a2a2054159739e17 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 03:13:02 +1000 Subject: [PATCH 057/839] Updater: Fix 3.x build detection --- plugins/Updater/updater.c | 18 ++++++++++++++++-- tools/CustomSetupTool/CustomSetupTool/setup.c | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index de808e1da9bc..5ca195aa507a 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -121,11 +121,12 @@ BOOLEAN UpdaterInstalledUsingSetup( VOID ) { - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Process_Hacker2_is1"); + 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 'Process_Hacker2_is1' registry key. + // Check uninstall entries for the 'ProcessHacker' registry key. if (NT_SUCCESS(PhOpenKey( &keyHandle, KEY_READ, @@ -138,6 +139,19 @@ BOOLEAN UpdaterInstalledUsingSetup( 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; } diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 9309fa223e2f..fd9cbe189fc9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -38,7 +38,7 @@ NTSTATUS SetupCreateUninstallKey( status = PhCreateKey( &keyHandle, - KEY_ALL_ACCESS, + KEY_ALL_ACCESS | KEY_WOW64_64KEY, PH_KEY_LOCAL_MACHINE, &UninstallKeyName, 0, @@ -106,7 +106,7 @@ NTSTATUS SetupDeleteUninstallKey( status = PhOpenKey( &keyHandle, - KEY_WRITE | DELETE, + KEY_WRITE | DELETE | KEY_WOW64_64KEY, PH_KEY_LOCAL_MACHINE, &UninstallKeyName, 0 From 4e2987d984efd299a67e54ed792ea4a96db6d646 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 04:32:50 +1000 Subject: [PATCH 058/839] Plugins: Remove deprecated winhttp flags --- plugins/ExtraPlugins/cloud.c | 9 +++---- plugins/ExtraPlugins/setup/updater.c | 20 ++++++---------- plugins/NetworkTools/update.c | 27 +++++++-------------- plugins/OnlineChecks/main.c | 2 +- plugins/OnlineChecks/upload.c | 10 +++----- plugins/OnlineChecks/virustotal.c | 24 ++++++------------- plugins/Updater/updater.c | 35 ++++++++-------------------- 7 files changed, 40 insertions(+), 87 deletions(-) diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index 68dc3343b6ac..57509cc969c0 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -51,19 +51,16 @@ NTSTATUS QueryPluginsCallbackThread( 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, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index a17321e1cc1c..77662132c054 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -312,7 +312,6 @@ NTSTATUS UpdateDownloadThread( 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; @@ -403,7 +402,7 @@ NTSTATUS UpdateDownloadThread( httpUrlComponents.dwUrlPathLength = (ULONG)-1; if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->FileDownloadUrl), + PhGetString(context->FileDownloadUrl), 0, 0, &httpUrlComponents @@ -417,19 +416,14 @@ NTSTATUS UpdateDownloadThread( // 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, + 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 ))) { @@ -450,7 +444,7 @@ NTSTATUS UpdateDownloadThread( if (!(httpConnectionHandle = WinHttpConnect( httpSessionHandle, - PhGetStringOrEmpty(downloadHostPath), + PhGetString(downloadHostPath), httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) @@ -461,7 +455,7 @@ NTSTATUS UpdateDownloadThread( if (!(httpRequestHandle = WinHttpOpenRequest( httpConnectionHandle, NULL, - PhGetStringOrEmpty(downloadUrlPath), + PhGetString(downloadUrlPath), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 36a113ec6b45..988029df021f 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -154,17 +154,14 @@ PPH_STRING QueryFwLinkUrl( 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(); - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - if (!(httpSessionHandle = WinHttpOpen( NULL, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { @@ -320,7 +317,6 @@ NTSTATUS GeoIPUpdateThread( 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; @@ -417,15 +413,11 @@ NTSTATUS GeoIPUpdateThread( if (PhIsNullOrEmptyString(downloadUrlPath)) goto CleanupExit; - // 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, + 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 ))) { @@ -435,13 +427,12 @@ NTSTATUS GeoIPUpdateThread( 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), + PhGetString(downloadHostPath), httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) @@ -452,7 +443,7 @@ NTSTATUS GeoIPUpdateThread( if (!(httpRequestHandle = WinHttpOpenRequest( httpConnectionHandle, NULL, - PhGetStringOrEmpty(downloadUrlPath), + PhGetString(downloadUrlPath), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 9167e0a386db..5da0db55f840 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -49,7 +49,7 @@ VOID ProcessesUpdatedCallback( _In_opt_ PVOID Context ) { -#ifdef VIRUSTOTAL_API +#ifdef PH_BUILD_API static ULONG ProcessesUpdatedCount = 0; PLIST_ENTRY listEntry; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 3c8ff16e74c7..60d99ffd515a 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -962,7 +962,6 @@ NTSTATUS UploadCheckThreadStart( 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); @@ -1014,13 +1013,11 @@ NTSTATUS UploadCheckThreadStart( 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, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { @@ -1030,7 +1027,6 @@ NTSTATUS UploadCheckThreadStart( if (WindowsVersion >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; - WinHttpSetOption(context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); } diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 430a63d840ee..b9f08f1e694e 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -286,20 +286,15 @@ PSTR VirusTotalSendHttpRequest( 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, + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { @@ -332,7 +327,7 @@ PSTR VirusTotalSendHttpRequest( if (!(requestHandle = WinHttpOpenRequest( connectHandle, L"POST", - PhGetStringOrEmpty(urlString), + PhGetString(urlString), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, @@ -432,20 +427,15 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( 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, + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 5ca195aa507a..55c88a814a7f 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -319,7 +319,6 @@ BOOLEAN QueryUpdateData( 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; @@ -327,15 +326,11 @@ BOOLEAN QueryUpdateData( 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_AUTOMATIC_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 ))) { @@ -648,7 +643,6 @@ NTSTATUS UpdateDownloadThread( 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; @@ -764,15 +758,12 @@ NTSTATUS UpdateDownloadThread( 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, + 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 ))) { @@ -783,18 +774,12 @@ NTSTATUS UpdateDownloadThread( if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); + WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); } if (!(httpConnectionHandle = WinHttpConnect( httpSessionHandle, - PhGetStringOrEmpty(downloadHostPath), + PhGetString(downloadHostPath), httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) @@ -806,7 +791,7 @@ NTSTATUS UpdateDownloadThread( if (!(httpRequestHandle = WinHttpOpenRequest( httpConnectionHandle, NULL, - PhGetStringOrEmpty(downloadUrlPath), + PhGetString(downloadUrlPath), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, From e11b772b0b0a967e7b65e2c81001ef98aedf828c Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 05:40:52 +1000 Subject: [PATCH 059/839] OnlineChecks: Fix typo --- plugins/OnlineChecks/db.c | 1 + plugins/OnlineChecks/db.h | 4 +- plugins/OnlineChecks/main.c | 2 - plugins/OnlineChecks/onlnchk.h | 13 ++---- plugins/OnlineChecks/upload.c | 20 ++++---- plugins/OnlineChecks/virustotal.c | 76 ++++++++++++++++++++++--------- 6 files changed, 73 insertions(+), 43 deletions(-) diff --git a/plugins/OnlineChecks/db.c b/plugins/OnlineChecks/db.c index 093578f518df..683b5264bc9e 100644 --- a/plugins/OnlineChecks/db.c +++ b/plugins/OnlineChecks/db.c @@ -24,6 +24,7 @@ PPH_HASHTABLE ProcessObjectDb; PH_QUEUED_LOCK ProcessObjectDbLock = PH_QUEUED_LOCK_INIT; +PH_STRINGREF ProcessObjectDbHash = PH_STRINGREF_INIT(L"386f3b6b3f6c35346c69346c6b343d69396b6b3468386b683d383d356b3e383e38356b343f69393b683d3b3a39386b3c6b3a3a3e696835696e686f6b38683e6e"); BOOLEAN NTAPI ProcessObjectDbEqualFunction( _In_ PVOID Entry1, diff --git a/plugins/OnlineChecks/db.h b/plugins/OnlineChecks/db.h index 194762118818..4a6568c3db54 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,8 @@ #ifndef DB_H #define DB_H +extern PH_STRINGREF ProcessObjectDbHash; + typedef struct _PROCESS_DB_OBJECT { PH_STRINGREF FileName; diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 5da0db55f840..35ef25384a98 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -49,7 +49,6 @@ VOID ProcessesUpdatedCallback( _In_opt_ PVOID Context ) { -#ifdef PH_BUILD_API static ULONG ProcessesUpdatedCount = 0; PLIST_ENTRY listEntry; @@ -132,7 +131,6 @@ VOID ProcessesUpdatedCallback( listEntry = listEntry->Flink; } -#endif } VOID NTAPI LoadCallback( diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index b53854ca4e17..130be7849571 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -41,13 +41,6 @@ #define SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED (PLUGIN_NAME L".EnableVirusTotalScanning") #define SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS (PLUGIN_NAME L".VirusTotalHighlightDetection") -#ifdef PH_BUILD_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) @@ -134,8 +127,6 @@ typedef struct _UPLOAD_CONTEXT PPH_STRING FileSize; PPH_STRING ErrorString; - PPH_STRING KeyString; - PPH_STRING FileName; PPH_STRING BaseFileName; PPH_STRING WindowFileName; @@ -252,6 +243,10 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResult( _In_ PPH_STRING FileName ); +PPH_BYTES VirusTotalGetCachedDbHash( + VOID + ); + VOID InitializeVirusTotalProcessMonitor( VOID ); diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 60d99ffd515a..803689827131 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -185,7 +185,6 @@ VOID UploadContextDeleteProcedure( } PhClearReference(&context->ErrorString); - PhClearReference(&context->KeyString); PhClearReference(&context->FileName); PhClearReference(&context->BaseFileName); PhClearReference(&context->WindowFileName); @@ -494,7 +493,7 @@ NTSTATUS UploadFileThreadStart( httpComponents.dwUrlPathLength = (ULONG)-1; if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->UploadUrl), + PhGetString(context->UploadUrl), 0, 0, &httpComponents @@ -523,7 +522,7 @@ NTSTATUS UploadFileThreadStart( if (!NT_SUCCESS(status = PhCreateFileWin32( &fileHandle, - PhGetStringOrEmpty(context->FileName), + PhGetString(context->FileName), FILE_GENERIC_READ, 0, FILE_SHARE_READ | FILE_SHARE_DELETE, @@ -549,7 +548,7 @@ NTSTATUS UploadFileThreadStart( if (!(requestHandle = WinHttpOpenRequest( connectHandle, L"POST", - PhGetStringOrEmpty(httpHostPath), + PhGetString(httpHostPath), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, @@ -1046,8 +1045,6 @@ NTSTATUS UploadCheckThreadStart( 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) @@ -1099,15 +1096,19 @@ NTSTATUS UploadCheckThreadStart( )); } - if (context->VtApiUpload && !PhIsNullOrEmptyString(context->KeyString)) + if (context->VtApiUpload) { + PPH_BYTES resource = VirusTotalGetCachedDbHash(); + PhMoveReference(&context->UploadUrl, PhFormatString( - L"%s%s?apikey=%s&resource=%s", + L"%s%s?apikey=%S&resource=%s", L"https://www.virustotal.com/", L"/vtapi/v2/file/scan", - PhGetString(context->KeyString), + resource->Buffer, PhGetString(hashString) )); + + PhClearReference(&resource); } if (!PhIsNullOrEmptyString(context->UploadUrl)) @@ -1323,7 +1324,6 @@ VOID UploadToOnlineService( context->Service = Service; context->FileName = FileName; context->BaseFileName = PhGetBaseName(context->FileName); - context->KeyString = PhCreateString(VIRUSTOTAL_APIKEY); if (dialogThread = PhCreateThread(0, ShowUpdateDialogThread, (PVOID)context)) { diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index b9f08f1e694e..642ce0ddd9f3 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -101,7 +101,9 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalAddCacheResult( return result; } -VOID VirusTotalRemoveCacheResult(_In_ PPROCESS_EXTENSION Extension) +VOID VirusTotalRemoveCacheResult( + _In_ PPROCESS_EXTENSION Extension + ) { PhAcquireQueuedLockExclusive(&ProcessListLock); @@ -177,6 +179,30 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResultFromHash( return NULL; } +PPH_BYTES VirusTotalGetCachedDbHash( + VOID + ) +{ + ULONG length; + PUCHAR buffer; + PPH_BYTES string; + + length = (ULONG)ProcessObjectDbHash.Length / sizeof(WCHAR) / 2; + + buffer = PhAllocate(length + 1); + memset(buffer, 0, length + 1); + + PhHexStringToBuffer(&ProcessObjectDbHash, 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 ) @@ -284,7 +310,6 @@ PSTR VirusTotalSendHttpRequest( PSTR subRequestBuffer = NULL; PPH_STRING phVersion = NULL; PPH_STRING userAgent = NULL; - PPH_STRING keyString = NULL; PPH_STRING urlString = NULL; phVersion = PhGetPhVersion(); @@ -292,7 +317,7 @@ PSTR VirusTotalSendHttpRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - WINHTTP_ACCESS_TYPE_NO_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -304,7 +329,6 @@ PSTR VirusTotalSendHttpRequest( if (WindowsVersion >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); } @@ -318,12 +342,19 @@ PSTR VirusTotalSendHttpRequest( goto CleanupExit; } - keyString = PhCreateString(VIRUSTOTAL_APIKEY); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + urlString = PhFormatString( - L"/partners/sysinternals/file-reports?apikey=%s", - keyString->Buffer + L"%s%s%s%s%S", + L"/partners", + L"/sysinternals", + L"/file-reports", + L"?apikey=", + resourceString->Buffer ); + PhClearReference(&resourceString); + if (!(requestHandle = WinHttpOpenRequest( connectHandle, L"POST", @@ -334,12 +365,10 @@ PSTR VirusTotalSendHttpRequest( WINHTTP_FLAG_SECURE ))) { - PhClearReference(&keyString); PhClearReference(&urlString); goto CleanupExit; } - PhClearReference(&keyString); PhClearReference(&urlString); if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) @@ -425,7 +454,6 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( PSTR subRequestBuffer = NULL; PPH_STRING phVersion = NULL; PPH_STRING userAgent = NULL; - PPH_STRING keyString = NULL; PPH_STRING urlString = NULL; phVersion = PhGetPhVersion(); @@ -433,7 +461,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - WINHTTP_ACCESS_TYPE_NO_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -458,31 +486,35 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( goto CleanupExit; } - keyString = PhCreateString(VIRUSTOTAL_APIKEY); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + urlString = PhFormatString( - L"/vtapi/v2/file/report?apikey=%s&resource=%s", - keyString->Buffer, - PhGetStringOrEmpty(FileHash) + L"%s%s%s%s%S%s%s", + L"/vtapi", + L"/v2", + L"/file", + L"/report", + L"?apikey=", + resourceString->Buffer, + L"&resource=", + PhGetString(FileHash) ); + PhClearReference(&resourceString); + if (!(requestHandle = WinHttpOpenRequest( connectHandle, L"POST", - PhGetStringOrEmpty(urlString), + PhGetString(urlString), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE ))) { - PhClearReference(&keyString); - PhClearReference(&urlString); goto CleanupExit; } - PhClearReference(&keyString); - PhClearReference(&urlString); - if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) goto CleanupExit; @@ -536,6 +568,8 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( CleanupExit: + PhClearReference(&urlString); + if (requestHandle) WinHttpCloseHandle(requestHandle); From 0f11d181d4fe0b1ee50a1511778a61533d57a1d2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 05:42:19 +1000 Subject: [PATCH 060/839] BuildTools: Fix building sdk release --- tools/CustomBuildTool/Source Files/Build.cs | 426 +++++++++--------- tools/CustomBuildTool/Source Files/Program.cs | 22 +- tools/CustomBuildTool/Source Files/Zip.cs | 24 + .../bin/Release/CustomBuildTool.exe | Bin 155648 -> 155648 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 71168 bytes 5 files changed, 245 insertions(+), 227 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 3963a1f96c54..386702e3c8b0 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -220,9 +220,10 @@ public static void CleanupBuildEnvironment() } } - public static void ShowBuildEnvironment(bool ShowBuildInfo) + public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) { - Program.PrintColorMessage("Setting up build environment...", ConsoleColor.Cyan); + Program.PrintColorMessage("Build: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(Platform, ConsoleColor.White, false); string currentGitTag = Win32.ExecCommand(GitExePath, "describe --abbrev=0 --tags --always"); string latestGitRevision = Win32.ExecCommand(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); @@ -497,176 +498,48 @@ public static bool FixupResourceHeader() return true; } -#region Appx Package - public static void BuildAppxPackage() - { - Program.PrintColorMessage("Building processhacker-build-package.appx...", ConsoleColor.Cyan); - - try - { - StringBuilder sb = new StringBuilder(0x100); - int startIndex = "bin\\Release64\\".Length; - string[] filesToAdd; - - File.WriteAllText("build\\AppxManifest.xml", Properties.Resources.AppxManifest.Replace("PH_APPX_VERSION", BuildLongVersion)); - - sb.AppendLine("[Files]"); - sb.AppendLine("\"build\\AppxManifest.xml\" \"AppxManifest.xml\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-44.png\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-48.png\""); - sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-150.png\""); - - filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); - - for (int i = 0; i < filesToAdd.Length; i++) - { - string filePath = filesToAdd[i]; - - // 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; - } - - sb.AppendLine("\"" + filePath + "\" \"" + filePath.Substring(startIndex) + "\""); - } - - File.WriteAllText("build\\package.map", sb.ToString()); - - string error = Win32.ExecCommand( - MakeAppxExePath, - "pack /o /f build\\package.map /p " + BuildOutputFolder + "\\processhacker-build-package.appx" - ); - - if (File.Exists("build\\AppxManifest.xml")) - File.Delete("build\\AppxManifest.xml"); - if (File.Exists("build\\package.map")) - File.Delete("build\\package.map"); - - if (string.IsNullOrEmpty(error) || !error.EndsWith("Package creation succeeded.", StringComparison.OrdinalIgnoreCase)) - { - Program.PrintColorMessage("[ERROR] " + error, ConsoleColor.Red); - return; - } - - string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); - - error = Win32.ExecCommand( - signToolExePath, - "sign /v /fd SHA256 /a /f build\\appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" - ); - - if (string.IsNullOrEmpty(error) || error.Contains("Successfully signed")) - { - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.DarkGray); - return; - } - } - catch (Exception ex) - { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); - } - } - - public static bool BuildAppxSignature() + public static bool UpdateHeaderFileVersion() { - Program.PrintColorMessage("Building Appx Signature...", ConsoleColor.Cyan); - - var makeCertExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeCert.exe"); - var pvk2PfxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\Pvk2Pfx.exe"); - try { - if (File.Exists("build\\processhacker-appx.pvk")) - File.Delete("build\\processhacker-appx.pvk"); - if (File.Exists("build\\processhacker-appx.cer")) - File.Delete("build\\processhacker-appx.cer"); - if (File.Exists("build\\processhacker-appx.pfx")) - File.Delete("build\\processhacker-appx.pfx"); - - string output = Win32.ExecCommand(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 " + - "build\\processhacker-appx.pvk " + - "build\\processhacker-appx.cer " - ); - - if (string.IsNullOrEmpty(output) || !output.Equals("Succeeded", StringComparison.OrdinalIgnoreCase)) - { - Program.PrintColorMessage("[MakeCert] " + output, ConsoleColor.Red); - return false; - } - - output = Win32.ExecCommand(pvk2PfxExePath, - "/pvk build\\processhacker-appx.pvk " + - "/spc build\\processhacker-appx.cer " + - "/pfx build\\processhacker-appx.pfx " - ); + if (File.Exists("ProcessHacker\\include\\phapprev.h")) + File.Delete("ProcessHacker\\include\\phapprev.h"); - if (!string.IsNullOrEmpty(output)) - { - Program.PrintColorMessage("[Pvk2Pfx] " + output, ConsoleColor.Red); - return false; - } + File.WriteAllText("ProcessHacker\\include\\phapprev.h", +@"#ifndef PHAPPREV_H +#define PHAPPREV_H - output = Win32.ExecCommand(CertUtilExePath, - "-addStore TrustedPeople build\\processhacker-appx.cer" - ); +#define PHAPP_VERSION_REVISION " + BuildRevision + @" - if (string.IsNullOrEmpty(output) || !output.Contains("command completed successfully")) - { - Program.PrintColorMessage("[Certutil] " + output, ConsoleColor.Red); - return false; - } +#endif // PHAPPREV_H +"); } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + Program.PrintColorMessage("[phapprev] " + ex.ToString(), ConsoleColor.Yellow); return false; } return true; } - public static bool CleanupAppxSignature() + public static bool BuildPublicHeaderFiles() { + Program.PrintColorMessage("Building public SDK headers...", ConsoleColor.Cyan); + 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); - } - } - - if (File.Exists("build\\processhacker-appx.pvk")) - File.Delete("build\\processhacker-appx.pvk"); - if (File.Exists("build\\processhacker-appx.cer")) - File.Delete("build\\processhacker-appx.cer"); - if (File.Exists("build\\processhacker-appx.pfx")) - File.Delete("build\\processhacker-appx.pfx"); + HeaderGen gen = new HeaderGen(); + gen.Execute(); } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + Program.PrintColorMessage("[ERROR] " + ex.ToString(), ConsoleColor.Red); return false; } return true; } -#endregion public static bool BuildKphSignatureFile(bool DebugBuild) { @@ -830,10 +703,9 @@ public static bool BuildSdkZip() try { - if (File.Exists(BuildOutputFolder + "\\processhacker-build-sdk.zip")) - File.Delete(BuildOutputFolder + "\\processhacker-build-sdk.zip"); + File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); - Zip.CreateCompressedFolder("sdk", BuildOutputFolder + "\\processhacker-build-sdk.zip"); + Zip.CreateCompressedSdkFromFolder("sdk\\", BuildOutputFolder + "\\processhacker-build-sdk.zip"); } catch (Exception ex) { @@ -889,27 +761,27 @@ public static bool BuildSrcZip() { if (File.Exists(BuildOutputFolder + "\\processhacker-build-src.zip")) File.Delete(BuildOutputFolder + "\\processhacker-build-src.zip"); + + string output = Win32.ExecCommand( + 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; + } } catch (Exception ex) { Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); return false; } - - string output = Win32.ExecCommand( - 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; - } return true; } @@ -997,7 +869,6 @@ public static bool BuildUpdateSignature() return true; } - public static void WebServiceUpdateConfig() { string buildPostString; @@ -1135,103 +1006,216 @@ public static bool AppveyorUploadBuildFiles() return true; } - - public static bool UpdateHeaderFileVersion() + public static bool BuildSolution(string Solution, BuildFlags Flags) { - try + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { - if (File.Exists("ProcessHacker\\include\\phapprev.h")) - File.Delete("ProcessHacker\\include\\phapprev.h"); - - File.WriteAllText("ProcessHacker\\include\\phapprev.h", -@"#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION " + BuildRevision + @" + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x32", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); -#endif // PHAPPREV_H -"); + string error32 = Win32.ExecCommand( + MSBuildExePath, + "/m /nologo /verbosity:quiet " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + + "/p:Platform=Win32" + (BuildNightly ? " /p:ExternalCompilerOptions=PH_BUILD_API" : string.Empty) + " " + Solution + ); + + if (!string.IsNullOrEmpty(error32)) + { + Program.PrintColorMessage("[ERROR] " + error32, ConsoleColor.Red, true, Flags); + return false; + } } - catch (Exception ex) + + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) { - Program.PrintColorMessage("[phapprev] " + ex.ToString(), ConsoleColor.Yellow); - return false; + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x64", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); + + string error64 = Win32.ExecCommand( + MSBuildExePath, + "/m /nologo /verbosity:quiet " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + + "/p:Platform=x64" + (BuildNightly ? " /p:ExternalCompilerOptions=PH_BUILD_API" : string.Empty) + " " + Solution + ); + + if (!string.IsNullOrEmpty(error64)) + { + Program.PrintColorMessage("[ERROR] " + error64, ConsoleColor.Red, true, Flags); + return false; + } } return true; } - public static bool BuildPublicHeaderFiles() +#region Appx Package + public static void BuildAppxPackage() { - Program.PrintColorMessage("Building public SDK headers...", ConsoleColor.Cyan); + Program.PrintColorMessage("Building processhacker-build-package.appx...", ConsoleColor.Cyan); try { - HeaderGen gen = new HeaderGen(); - gen.Execute(); + StringBuilder sb = new StringBuilder(0x100); + int startIndex = "bin\\Release64\\".Length; + string[] filesToAdd; + + File.WriteAllText("build\\AppxManifest.xml", Properties.Resources.AppxManifest.Replace("PH_APPX_VERSION", BuildLongVersion)); + + sb.AppendLine("[Files]"); + sb.AppendLine("\"build\\AppxManifest.xml\" \"AppxManifest.xml\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-44.png\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-48.png\""); + sb.AppendLine("\"ProcessHacker\\resources\\ProcessHacker.png\" \"Assets\\ProcessHacker-150.png\""); + + filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); + + for (int i = 0; i < filesToAdd.Length; i++) + { + string filePath = filesToAdd[i]; + + // 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; + } + + sb.AppendLine("\"" + filePath + "\" \"" + filePath.Substring(startIndex) + "\""); + } + + File.WriteAllText("build\\package.map", sb.ToString()); + + string error = Win32.ExecCommand( + MakeAppxExePath, + "pack /o /f build\\package.map /p " + BuildOutputFolder + "\\processhacker-build-package.appx" + ); + + Program.PrintColorMessage(error, ConsoleColor.Gray); + + if (File.Exists("build\\AppxManifest.xml")) + File.Delete("build\\AppxManifest.xml"); + if (File.Exists("build\\package.map")) + File.Delete("build\\package.map"); + + string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); + + error = Win32.ExecCommand( + signToolExePath, + "sign /v /fd SHA256 /a /f build\\appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" + ); + + if (!string.IsNullOrEmpty(error) && !error.Contains("Successfully signed")) + { + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.DarkGray); + return; + } } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex.ToString(), ConsoleColor.Red); - return false; + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); } - - return true; } - - public static bool BuildSolution(string Solution, BuildFlags Flags) + public static bool BuildAppxSignature() { - if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) + Program.PrintColorMessage("Building Appx Signature...", ConsoleColor.Cyan); + + var makeCertExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeCert.exe"); + var pvk2PfxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\Pvk2Pfx.exe"); + + try { - Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); - Program.PrintColorMessage("x32", ConsoleColor.Green, false, Flags); - Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); + if (File.Exists("build\\processhacker-appx.pvk")) + File.Delete("build\\processhacker-appx.pvk"); + if (File.Exists("build\\processhacker-appx.cer")) + File.Delete("build\\processhacker-appx.cer"); + if (File.Exists("build\\processhacker-appx.pfx")) + File.Delete("build\\processhacker-appx.pfx"); - string error32 = Win32.ExecCommand( - MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " /p:Platform=Win32 " + Solution + string output = Win32.ExecCommand(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 " + + "build\\processhacker-appx.pvk " + + "build\\processhacker-appx.cer " ); - if (!string.IsNullOrEmpty(error32)) + if (!string.IsNullOrEmpty(output) && !output.Equals("Succeeded", StringComparison.OrdinalIgnoreCase)) { - Program.PrintColorMessage("[ERROR] " + error32, ConsoleColor.Red, true, Flags); + Program.PrintColorMessage("[MakeCert] " + output, ConsoleColor.Red); return false; } - } - if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) - { - Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); - Program.PrintColorMessage("x64", ConsoleColor.Green, false, Flags); - Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); + output = Win32.ExecCommand(pvk2PfxExePath, + "/pvk build\\processhacker-appx.pvk " + + "/spc build\\processhacker-appx.cer " + + "/pfx build\\processhacker-appx.pfx " + ); - string error64 = Win32.ExecCommand( - MSBuildExePath, - "/m /nologo /verbosity:quiet /p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " /p:Platform=x64 /p:ExternalCompilerOptions=PH_BUILD_API " + Solution + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[Pvk2Pfx] " + output, ConsoleColor.Red); + return false; + } + + output = Win32.ExecCommand(CertUtilExePath, + "-addStore TrustedPeople build\\processhacker-appx.cer" ); - if (!string.IsNullOrEmpty(error64)) + if (!string.IsNullOrEmpty(output) && !output.Contains("command completed successfully")) { - Program.PrintColorMessage("[ERROR] " + error64, ConsoleColor.Red, true, Flags); + Program.PrintColorMessage("[Certutil] " + output, ConsoleColor.Red); return false; } } + 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); + } + } + if (File.Exists("build\\processhacker-appx.pvk")) + File.Delete("build\\processhacker-appx.pvk"); + if (File.Exists("build\\processhacker-appx.cer")) + File.Delete("build\\processhacker-appx.cer"); + if (File.Exists("build\\processhacker-appx.pfx")) + File.Delete("build\\processhacker-appx.pfx"); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } - [Flags] - public enum BuildFlags - { - None, - Build32bit = 0x1, - Build64bit = 0x2, - BuildDebug = 0x4, - BuildVerbose = 0x8 + return true; + } +#endregion } } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 52e7258d2990..b08dbfb0c40f 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -76,7 +76,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - Build.ShowBuildEnvironment(false); + Build.ShowBuildEnvironment("bin", false); Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); @@ -118,7 +118,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment(true); + Build.ShowBuildEnvironment("debug", true); Build.BuildSecureFiles(); if (!Build.BuildSolution("ProcessHacker.sln", @@ -158,7 +158,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment(true); + Build.ShowBuildEnvironment("release", true); Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); @@ -204,7 +204,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment(false); + Build.ShowBuildEnvironment("nightly", false); Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); @@ -252,7 +252,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment(true); + Build.ShowBuildEnvironment("appx", true); Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); @@ -285,7 +285,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - Build.ShowBuildEnvironment(true); + Build.ShowBuildEnvironment("appxcert", true); Build.BuildAppxSignature(); @@ -342,4 +342,14 @@ public static void PrintColorMessage(string Message, ConsoleColor Color, bool Ne Console.ResetColor(); } } + + [Flags] + public enum BuildFlags + { + None, + Build32bit = 0x1, + Build64bit = 0x2, + BuildDebug = 0x4, + BuildVerbose = 0x8 + } } \ No newline at end of file diff --git a/tools/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs index 08cc138439fe..cddb9f950b88 100644 --- a/tools/CustomBuildTool/Source Files/Zip.cs +++ b/tools/CustomBuildTool/Source Files/Zip.cs @@ -34,6 +34,9 @@ public static void CreateCompressedFolder(string sourceDirectoryName, string des 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)) { @@ -60,11 +63,32 @@ public static void CreateCompressedFolder(string sourceDirectoryName, string des } } + 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)) + { + 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)) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index dcbe0cfce5d8a9b4b677332a982064ef728fe15c..c7103426b509e6da18aced66ecf171e3f0db4c6c 100644 GIT binary patch delta 18057 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 UJ?+`2e6w2J*YNZ%Rryl+Kg1pD%K!iX delta 17645 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 Wcb@jVBmHBsJh=DMzTWcp(*FZr_?giF diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 93863aa4f8b31961925b03a96ed5e13fa7f51171..89c01e4031cdb2ac1332bc6d3c6bb6f584ba701e 100644 GIT binary patch delta 17240 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 delta 16757 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 NF8FOrs$>N_{vWS=aAyDj From 31187326a8311066344adb8b9c4e5da8105ec711 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 09:53:53 +1000 Subject: [PATCH 061/839] BuildTools: Add script for creating appx package certificates --- .gitignore | 3 +++ build/build_appx_makecert.cmd | 7 +++++ tools/CustomBuildTool/Source Files/Build.cs | 20 +++++++-------- .../Source Files/NativeMethods.cs | 6 +++++ tools/CustomBuildTool/Source Files/Program.cs | 24 +++++++++++++++++- .../bin/Release/CustomBuildTool.exe | Bin 155648 -> 156160 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 71168 bytes 7 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 build/build_appx_makecert.cmd diff --git a/.gitignore b/.gitignore index c8bba02b98ab..334d389b9df0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,13 +9,16 @@ # Build results *.appx +*.cer *.ilk *.meta *.obj *.pch *.pdb +*.pfx *.pgc *.pgd +*.pvk *.rsp *.sbr *.tlb diff --git a/build/build_appx_makecert.cmd b/build/build_appx_makecert.cmd new file mode 100644 index 000000000000..d65686b5a244 --- /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 \ No newline at end of file diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 386702e3c8b0..26fe6b66fbd7 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -222,8 +222,11 @@ public static void CleanupBuildEnvironment() public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) { - Program.PrintColorMessage("Build: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(Platform, ConsoleColor.White, false); + if (ShowBuildInfo) + { + Program.PrintColorMessage("Build: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(Platform, ConsoleColor.White, false); + } string currentGitTag = Win32.ExecCommand(GitExePath, "describe --abbrev=0 --tags --always"); string latestGitRevision = Win32.ExecCommand(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); @@ -301,7 +304,7 @@ public static bool CopyTextFiles() } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + Program.PrintColorMessage("[CopyTextFiles] " + ex, ConsoleColor.Red); return false; } @@ -462,7 +465,6 @@ public static bool CopyVersionHeader() File.Copy("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", true); File.Copy("ProcessHacker\\sdk\\phdk.h", "sdk\\include\\phdk.h", true); - File.Copy("ProcessHacker\\mxml\\mxml.h", "sdk\\include\\mxml.h", true); File.Copy("ProcessHacker\\resource.h", "sdk\\include\\phappresource.h", true); } catch (Exception ex) @@ -1096,7 +1098,7 @@ public static void BuildAppxPackage() "pack /o /f build\\package.map /p " + BuildOutputFolder + "\\processhacker-build-package.appx" ); - Program.PrintColorMessage(error, ConsoleColor.Gray); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); if (File.Exists("build\\AppxManifest.xml")) File.Delete("build\\AppxManifest.xml"); @@ -1107,14 +1109,10 @@ public static void BuildAppxPackage() error = Win32.ExecCommand( signToolExePath, - "sign /v /fd SHA256 /a /f build\\appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" + "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appx" ); - if (!string.IsNullOrEmpty(error) && !error.Contains("Successfully signed")) - { - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.DarkGray); - return; - } + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } catch (Exception ex) { diff --git a/tools/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs index 0115f4209f27..f173c3cff2fa 100644 --- a/tools/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -33,6 +33,8 @@ public static string ExecCommand(string FileName, string args) public const int STD_OUTPUT_HANDLE = -11; public const int STD_INPUT_HANDLE = -10; public const int STD_ERROR_HANDLE = -12; + public const int SW_HIDE = 0; + public const int SW_SHOW = 5; static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); @@ -42,6 +44,10 @@ public static string ExecCommand(string FileName, string args) public static extern bool GetConsoleMode(IntPtr ConsoleHandle, out ConsoleMode Mode); [DllImport("kernel32.dll")] public static extern bool SetConsoleMode(IntPtr ConsoleHandle, ConsoleMode Mode); + [DllImport("kernel32.dll")] + public static extern IntPtr GetConsoleWindow(); + [DllImport("user32.dll")] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); } [Flags] diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index b08dbfb0c40f..dbf2eeac7091 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Security.Principal; namespace CustomBuildTool { @@ -204,7 +205,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment("nightly", false); + Build.ShowBuildEnvironment("nightly", true); Build.BuildSecureFiles(); Build.UpdateHeaderFileVersion(); @@ -282,6 +283,27 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-appxmakecert")) { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + + if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) + { + Win32.ShowWindow(Win32.GetConsoleWindow(), Win32.SW_HIDE); + + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName, + Arguments = "-appxmakecert", + Verb = "runas" + }); + } + catch (Exception) { } + + return; + } + if (!Build.InitializeBuildEnvironment(false)) return; diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index c7103426b509e6da18aced66ecf171e3f0db4c6c..2e2e7c96ad00663d985f6ceeaca78ff8c94556f7 100644 GIT binary patch delta 17440 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 delta 16952 zcmbt+34B!5_5XQqX5MU>$t=laCbJ|E2tzhNKxESZf}*m?A|eV1s8nR&23nCOlZaBW zF3_jAE4Wa%R&5l;g+&o}QHm>+g4kBHR%p?xwetI(^Cp?WE}zf;-?8((=X=h%=kE92 z`zEw?o4a+3d(~B4|5)umn{uCh)RnwiZ=#)0T%(D0=i${4PN{D_Of*ioM(?k*EV)cp zvR0=eNT(hmN|d`fs#lW{s^9c)BePywH{cuCVn2Zm1hNqY)i(}WSmBMN(#sH4uPA28eArPA_m>A2wpgF>i4Y z*)1KI+U70@5wxcph&<`8fLyjA&MI@Jb6|8ls(*L)s5%7yySv9ils2V*SjmzWM^TAXrpe%hnoYkGopU(5W%)ia^lo$MXfaSk8=226?jC6OdPf;#x z#Jg87t8vz#Of3K)f0l-ir&t+yytkcHM`bQT-pom1sKMHO9T5YM+^P$6ZrZ$`3NloaU}DZe@}6 z$*K=U3;54XHaEL#vgO4ckXD-dxzj%#kCD6 zmeoQ)uDquTY%Hs<^R~t?Ej*T_T8V213^U$m*>YBw`CMQq(Kxs!rj80Ppz++_4 zR6|XJMcn|%)%SVkNAkQp0zMXyvoB`8zLW2bd1VPW3yMbAND(iAtgB!R(AequB`_To zb8*G?B2woPkhtj+FajPUJ&>nL!ccW(C~!C+Gl&i1W26T|{;y@bYP5hk$QM6j4zgMU z;87y?L{^d1dYD?!KX!Fe6i!26umYz%Y6&z(W+Iwwa%A5?pjznt6bQZdsDf- zD99#zg82_$P4>0IY%L*_Dz&N)sH1F=_~^N^8g)u6S7X>_LoTrC1NZa1=GD=W`;KEh z)ISPx4=C@nyU7!_Nv|>LI9gsPRdrSa36Gp8}qZozsVsBu%n3&!K0uPlrNcY{us!GwnsW_bsihE8fVu~+I}n)ne^xh8gQ`P@Xbu&Cl+yf;Gy@0sYH{1c+4 zj8_If4ERax;gxWZvnIKxS!$_=l$-Iv$_@)ld5c%|>9q8zG}{6d*%?rD_C}<}DWihz z?3QEot`7ZXZUFUI#Eq3lA(r z_4~J5=w^4RIX+bC+0+@*%!C@Ue+89?L7i8zT$Ucjy{&i4t69#eLlDI+J)FH7n2M{= z(?G5I++ypf&noUO4y-t@m5p;q-ZXYF-m(7mz<{S`sd;L+wA-2R?d-7@f58T^f-Tkt z)D}Me%q^f+;e)zE!a>WPPIHg&O2p17D>0fonaJkVIZeHLlY3)DVsaJi*rvPW%(@?Q zFzMq{>>X|T#eeGKpOw^jeu@lMh0SM5p3$=APf8m`W|5;6hf#aeNL0yVsAu7o83no8 zP+xNqUaqg4;&{XDFnOvA+{{l)kC$mu_*gM}NBWN(4!3+K{kL6|CvC?D(!#m&JFGdr zw4@t0*DZx=*`+YSJUu$Pp|OYgQRJ-brD#$2EKam`bp>qcQ@GZ1St%#g%!OiibE|Nk z_GIQUS69I`bFqlub3~jz?W)#+{L*S1Na2*X4C}OH*dm>jS;S(pg?ls8Bx)avOGL!u z)1LW>`A%8A_lY#Z%GU9O>5Fzb>G$ALptTQotink8G}N!@bb!yDIRlA`_ryxhjDhHO z8tJjDs_$uD67AMF4wj%jb0$lxjP>-jdQ zI*>D3I|9%D>b;4uOC5zC2c-P5+{~7rGrDgLE!5MKIrk|LB^YieHtRAl2C52A2b;{y zTV~l>iqYALYtemL_lTNNWBw%`>-7vgY8~n|Bz*69PtsV64%d38?9#CQQ?%u3T&;O= zJbsb2_2t&26+2R6i_VBPKF^QB|6aj~wf|2Q%>JN)=O8;SM*p#bY>n%8r}1R6wi2)P z_`pM*tL?NVQ&Fx0jy9xUmzAbQo8EZ&4+ zQdM=BqXy4mKGw;x%TObr?AQ<{7RV13$c^=RpLaTCYlEKO!235Yv!0?z*mNX|XD);$ zp1~d4Q~%?jp)SkQ3qe=p=~U2P zZS0k9^%s~k=Wv_CvXQ!FXi~Y7upXT*(eT0Zg+9cl_KDe?ie@9d z3kFY}0Vc(J)TR#6s~Y;`t17Zo^`Zi;$fCWwKr1q94Yh~Wyh@9#6G(kem+H}%7G~p_ z>D-*S8VqJ#Z1RUC-|wzMWu0mwY{%q|s;>S1jr-o|EtrvZbpnjX_;+`yyP>nXE~@r* zX09qIY;VQhc-U@rpuKPh*-$aL_-kab>RMN(PVH}&S0*R_&;7!OC|+U5Y>3xX_RY@e zPYYZ5@van0{A^BjOv4svFb&lTNu%mcNn`41Nz2tPN#p8}qzUCYp8b<*2&b)KgsSL_ zb+y>a)UA?6)vqOuWz{AT%GFLuEyJ!lC5waF5KY8MOH?iC^Rv84*DBbHF2Tqkg$6{RX+2@zFe31#X; zONgp7Eg_~Zu!M58*b?IEI!j2XhbkQ=LP+(qgs?ip5=zwhmQbn|TS7!#Z3$&+y(L7|21|&k?Uqok-nWFf`imtb zlxwh*l~g4x9M`=frLZ$|@fB*2B~+?2ETM~i{cJ^Lv;H8MMJo>s7gs1Q~e|@SEouESJNa-sQHp6Rg0u4^$SVS z+v|r=mh0i1Lv|jY?sY=tl5K(Wt^zbPQu29L!zgy%9+ze)MsXw z>Udo2M?3A6dHYfGtm=W6T`lE#))+dLDJv23P!L67d)S>hk9HjM9$yV>~)b`BfOY`~EYJ6CPXx#H9r^iKA> zQi8H2jRSHm8ixi&GCza5<-BA4?_z&E2v7;Oq;YnrO*^?JWu_D}VI#WvD^x$#7|5Q1y-G zxVr96kxyzgm(`V5ihK@Nr=nnl?i6q@M z!3X@ z>|i-F0dm+75%*Q2Wj5BEY+vU?_}*9#UDHdbQTNu_=DL2hMJu3C1BT1soA38a#ifWr z=N&zIw1gKrZpgCWaYeS9TK@@IYam`m**}2menPvn8q9QC`c3nqrq*+&6*pAO91C<^Xx6Gc&QgBvc2{2i`WeIL;jT;nQ5F$^Pb-2nn_R$yh{8cFXmR}46F^#4-l z$T88noG|Wx>VdPQuK6CQYy3gwdkdf3*%+&FGdg*|?JaEGwbk zZk*w33cW%Ew z-B5oLh6U!S8>%mxiP(n0%h-cCy%$MD{N=?(W-)k3%lw|5T{b5_r_F~3mGhm&yjh{DQznnb8AM%+~23L=42Cc*w8y#VHBdl)zd7Dr?j{#kZ zuNz+tUvvZi7EdvJG5(?}XPvoqa5C#IW*^j$cKB|I8Z_I_ctg=S(PFAY7K7d?nq6Yh z^*+Xhf~WaejtKS=w26Le(PhY6DL6nhDc`bUgGQD-5I1O&kMZ21$14nK^E3W2aalHG z&~VRXz~!ZOn?b)SVR>hWadG64l!xv}?k+Xx4Gf%z60Y5)etO>XX1SmKTE>{s-YhrB zsqL|a=%2L5www}CuJmTe27MtoyNn~B37^|&6}Gv%#3w|jYJo`Bphk@g8z|@~x+NN+ zo60^$`v!*WHb32yI0tRo;$xq!Qmq?3+{y-N5>RN z>`Noshh-e$C&BJ$cP!4>1@Vn6)rFXZn9vw|_lk1**uOjMr%^?{%8&sAVbIr+KLZDq zhD-ePw6`_rr~8Xqg9d$^TEf$}Xq^v&TnSB}t zKe@_Ek{*gDK0+rK0}WEe-1*Mb?otnx0u7oMTnPP27l%6%;LvMJxQEuec_0pl8BdQh zE-B$bxzYP(d5BtEfrvrA=!?j^PzK@|M2OID%F<}e>})BwZ+e8o%@dpy}A%MSs*|^PO#@7uyy?K&6YeBB+z=H??(&S(Xhq(i0x_= z!Jhqu?Lbgw{e_J}fy@R7JEhEGCkboF*@g+5k&7}~*lcIka-Amhb&ti)m-e2QvT8a< z+B-^Ge=<(h+``5r!39T-*SgS;dp?J1kE}pN7?JU9}06qG*=yloH z1UidMvY|_H;-Lw2Hr-6NE&+NUd)5S+Mz@NM2WtY&q;&=|?r7!H8oL%T#2cr)my3lok%Mi2+ zRf&9q$Xi6dSMYa&>rhx1YIN`n*d96p?C<>s1Mx=UUOJw9`uENV-A1}ALapQl-ldfS zw`kpgyS4rHM*EPNp^NrTf{mG0=qkUa0_Cj(a>2y_<#darq zYC28l$1OY`O8iK()4m*=T)x=lp!xjhg)LE*0sAh;_Qx(oiJ#yi2wRqRf;wpq-jB2E zxADa;7fr&EirHxqW`EAH0w18bP$YOi5&W+@yM~7JSON8+f-jm_8hFo4|yG~@bI$9rP&pU@PTM_Mn zr-qmAKZZp|u&s@Hm0yW`mv*u>t|Iy*$F41Z&{a$)jFc$!xIb%PA1XT4%BaysSAZr7 zTSFDabF?5mo@2@4&8`p?;ReQ4+izS%VHPp5)>N)_z33|KWVbnA#VqQuJ?Px&iqcxi zXElP)UO0UPq!C%NDA9JugQ*W(dG>Cp#-+j7K}qc(n6uc6kr0GF<+4$7X+>yM}%>nmc2K>k4G7r6+Q1 zl|2BqDaRU7wL03KV`qc|U^{Zm9h`%6)O$G=4F4SY^RwJ*VB8$p|L><}i~2-==wldwic(3?3{gvmC7 z-pw%{!V$FBVp-RIas6Y?_+$wi|B_=fzTENjGO$i^rfwcZfXH$#R*5 zz5yDYE|#D-x>)}!mk!M?@%a@rENhjhL{hKR=^~(w&aqn>zT|xqVEk>EaVGL+b-FFA zqu`grL6~m0=W%2p*jqH6I5I$$+*2H`JF2VE%Xkm;|BKR$Y_2tfr0#z%3j}O5-xCPf zb6r${zOi~jr`2e^jVz7T6F<~wgrg+X8K*Eo6e=*@Clxr8H0EMS@EOR|3ni*KyEW1ZQb1w+Y??tfU8kJ!m7afnE{) z+k!E@N3=h60U9(6IG7%wU8(FDG+KWWm(U6N&*D?)F)8LTI+UD2v-B<4nkMKw5*N`t z{pG|0TBNV4XcEn(kS$GXMT=;zfzLs&WxW%cZ50cEcjGBAL4O@L!qNIn&o61IKE>07 zf+v+cD)PoGZ6#m)IpBima}sDP44#UY5N>A0Z)v60zxYGQ&!o7DUm6_rb3@nW=`W>R z8duJ*t)umLpv=?n^TxE*`T{TBr08GxUD_g4s3(x+ThQVF-J*XVI8l2=dhQiE-7{Rv zK7hk&(f0ndRk!O)v{w*l8LWS|U7>xT*Wp-N zLYF0P*7nn7&O5aIQv5-^TX+I-F&e7-ec(Ct)*0n5qPpXPJAfOD-veG3-=j^)qT6?9 z2jTh^@DCx<57Kw#PCaOgMSc2V`bRRP$84V`=16yy!Ft4>gnoVM4z!!cG9lP4C&!{= z^g86aS*sJDZggGzV!a!UwqL9#fQ$5g;yFa{WXi^(ztqo=3={OJLDWb;;(tmXA{mCD zu!|vch)Kw>UGHL>Uhz6YbV)sjInUvG+PEsybJlMXgKcwi^4yBKw)xaQzS_2!V$t=s zpQ4m!Y*&c&nw-yP+0svKH_+HnSNrWb)uPmB+-j}DD0!*FXW4lOZKx_G0`$7FmJQpVD*CglAI_(WtyL}$w^q@tuB8J)aC$i7l zx9SU$OSDDWx2bKAA1QtXSQr1$&QAO6!))&+{%9X<^qD%Wgb5^L2W2wHqY#{I`7S&SyPh}fvsqMz%(T;m9~?k%JB_6FLkWe6Qx(eryn#LChu(yH_)JUwvUVN zabzE`tuKDau`So(k9yv9bg^|$ed*Y$HQ*P6t=drF1Ekq~z&)um=%8NW;VykYUTz%J zD1E0FmPFVkKJSaq9`V^HJ|C0YsnZ|G?{v{Y zXk2(#$`ws$dw`W$jceCU<9emF&x{^8hU_tV(hF4WYrqZvPwoa>v=0H}RP1TMZ7vF| z73@pBAP=VgK%Dk~r_*TQMA4rkIGbidb1_{FY@(YyBk1RdKN|4|`Q`z$V|^=;%o@^?+9py zc}GLD+cz8X#opOkJ3N<*e!0l^K)%s?kH{}T-kkNmAmjkZLGJ;PZTf5q7uj^qTm^Y( zQI*IeAYWKCLgd+yR~O9|c{$`~ik6FfkNylQdO(+|>$}lsZX08jjoZE4_8R@6Xu0jT zBAdYMJvL5W5Ii7gv$LjEaF~jbw7?ibWt?9)YXnBXi6-K=jDTqn3yaKE7D zkz83xh6&CRY!h53xK*&Vh+WqTZV}ukNIuqA3N{MP6l@h-E4W2)pCI|gU$9Ydra#N7 zRv~Kzw+QYNYz(k4#sy3zT@zn zKu`Fc6X{9kWO@S21FxdYC(r7V-LHMBRp~SIpXeX!YwYXoJM5q1kD?kKvmMts9&>DP zX!u))uEuy{zHz-#iep$g)=7fOa4@~W{whv*uZ0;;4l;fq=nk=bSY#u@^0%T{P|Wfm z7vmz=o4_O9H?v53dRTQNz-Wa#;%52P62@twA0+s>V3icS(z_GZuOsgPhkAbpoRB); z)@crop&HEv>U05qr_|^|Adctww}~2#c}_l>;>4(7p}P4;XMNuE92=V{@$s-oS4Ju%Y%r=rur{Zo-xQp`dxi z*>zdB;|<3WV}(K51X_#N#iUK7+wcONw8``!QhO^OmJ2`pGYihwou3WH4jYp~-DYFA zyi+XHZ7{4~t&Y)+F*e|Ee{ALrMjH35AL@38|5Y~=H#h6URs3hvyk=Td+hpE1t!HSp zcF1Ak`-Ikv2jLXduzl~egx;>7`zx*Z$Ft@ynLA?!UD7l=qvp?R-*KMT*8clhbM?@O z%z~!O{JAt~$wJ_Ta~IEOzkN=^WgeJ+a{FA+0 zs!;o%SA3;uBh9i^)$Q?>H@dXG=HXR|=q1Q=3B0EREvW&Mj=GzfRZ(Y7)&Ah>XiU4% z-1gv!X6hmQJxY&>m`StoAD7XD@|GaNeAJ~9Y#wT}0G2uEj8h&O8f;(n>|0upwq<9X z{@KFKqc%L#?eyT7+PBSxn}(&5Zl^tTcIaF^bS?%gIN45`-|r%u?)L}V>dhUSE2T`bPmaEdH`H}jID)rLPC0b|^*gR9)GW4XS&PrY9%=Svn?*iz4 zm($_*`%#3~=?E2f~ZEZ%S2I)#iB_gkEZ{G52-E{|oXrZ>zX5SZj zW=9)N-5+Xw&P5t9)OMeETKIsoG}L+vYU_78I;5oE<(Fzc;X&lmP}@c=OdK#wVGS!l z3$}jla_XVhFGH<|vC@+czmwaObl?{dVqGP4EC=Q@taU9{`XHQ^_3!cqGF-8 zSAuPCaLqwbHoLSzc1SR(yUWR8xDh9B%MNF|8!BBx-+aK0hb7qdJ0ICYZGV7`JN%%- z33aKI^tt$k+Bo&2cVnjG#YOGQUW{u-soh|H`}W;t+e?c)pXuzA`$bC&zxebWD~_U%f2SDz=h+O@B= F{{y4GK->TT diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 89c01e4031cdb2ac1332bc6d3c6bb6f584ba701e..f00ca518a704458cd1de236ee07322455c4e43a0 100644 GIT binary patch delta 11834 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 From 2fadc93976165d48b265b036965d74c8c00d648c Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 09:55:54 +1000 Subject: [PATCH 062/839] Fix typo --- tools/CustomBuildTool/Source Files/Build.cs | 1 + .../bin/Release/CustomBuildTool.exe | Bin 156160 -> 156672 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 71168 bytes 3 files changed, 1 insertion(+) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 26fe6b66fbd7..ee89924e1f81 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -465,6 +465,7 @@ public static bool CopyVersionHeader() File.Copy("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", true); File.Copy("ProcessHacker\\sdk\\phdk.h", "sdk\\include\\phdk.h", true); + File.Copy("ProcessHacker\\mxml\\mxml.h", "sdk\\include\\mxml.h", true); File.Copy("ProcessHacker\\resource.h", "sdk\\include\\phappresource.h", true); } catch (Exception ex) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 2e2e7c96ad00663d985f6ceeaca78ff8c94556f7..7b84b6df2e08ae2b6f57b291c5479b10b71f10ec 100644 GIT binary patch delta 6730 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 aRw|>}r=QF|tz7z^&R6^7vwG!I<-Y;ITgoK> delta 6610 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index f00ca518a704458cd1de236ee07322455c4e43a0..6e330471040d46fe1645c4748f46dedb70f5a334 100644 GIT binary patch delta 7588 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 F_kY(nfr9`5 delta 7434 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 MwCVvt_Eo?C0WBJGBme*a From a78459e5a5c3c1010546037f9dad27589964779f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 May 2017 20:03:54 +1000 Subject: [PATCH 063/839] BuildTools: Fix missing sdk files --- tools/CustomBuildTool/CustomBuildTool.sln | 2 +- tools/CustomBuildTool/Source Files/Build.cs | 19 +++++++++++++++++- .../bin/Release/CustomBuildTool.exe | Bin 156672 -> 158208 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 71168 bytes 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/tools/CustomBuildTool/CustomBuildTool.sln b/tools/CustomBuildTool/CustomBuildTool.sln index 811b822b2e06..0558a6f4e5a1 100644 --- a/tools/CustomBuildTool/CustomBuildTool.sln +++ b/tools/CustomBuildTool/CustomBuildTool.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.0 +VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomBuildTool", "CustomBuildTool.csproj", "{CD644DF2-A658-4CBC-9497-CA5DD13CFEC3}" EndProject diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index ee89924e1f81..26a1dcbd8aad 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -444,6 +444,23 @@ public static bool CopyPluginSdkHeaders() { File.Copy("phlib\\include\\" + file, "sdk\\include\\" + file, true); } + + // Copy readme + File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); + // Copy symbols + File.Copy("bin\\Release32\\ProcessHacker.pdb", "sdk\\dbg\\i386\\ProcessHacker.pdb", true); + File.Copy("bin\\Release64\\ProcessHacker.pdb", "sdk\\dbg\\amd64\\ProcessHacker.pdb", true); + File.Copy("KProcessHacker\\bin\\i386\\kprocesshacker.pdb", "sdk\\dbg\\i386\\kprocesshacker.pdb", true); + File.Copy("KProcessHacker\\bin\\amd64\\kprocesshacker.pdb", "sdk\\dbg\\amd64\\kprocesshacker.pdb", true); + // Copy libs + File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + // Copy sample plugin + File.Copy("plugins\\SamplePlugin\\main.c", "sdk\\samples\\SamplePlugin\\main.c", true); + File.Copy("plugins\\SamplePlugin\\SamplePlugin.sln", "sdk\\samples\\SamplePlugin\\SamplePlugin.sln", true); + File.Copy("plugins\\SamplePlugin\\SamplePlugin.vcxproj", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj", true); + File.Copy("plugins\\SamplePlugin\\SamplePlugin.vcxproj.filters", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj.filters", true); + File.Copy("plugins\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll", "sdk\\samples\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll", true); } catch (Exception ex) { @@ -708,7 +725,7 @@ public static bool BuildSdkZip() { File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); - Zip.CreateCompressedSdkFromFolder("sdk\\", BuildOutputFolder + "\\processhacker-build-sdk.zip"); + Zip.CreateCompressedSdkFromFolder("sdk", BuildOutputFolder + "\\processhacker-build-sdk.zip"); } catch (Exception ex) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 7b84b6df2e08ae2b6f57b291c5479b10b71f10ec..cc4447fd6e5f2b48ee48b907c54786ec1cd927ae 100644 GIT binary patch delta 6504 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 AKmY&$ delta 5907 zcmbuDdvqN2b;s|FrPb`NwA$55`<8a~SZTG|)myeCTk=Xs20<)K$S=9(5wR}Vg{-)4 z62O+$M%35=d@TPOfr$x^La-?2k)?LsobWga#cgHYnF zv#XW=XnRg|j^=(p_uk+AJ!a-NGrQ|O~ z&;n2X7<^7@;cd);JIRv=V8iAT826i5%B;0m@-t z7xZ=PAy0_LV~Uti=vvZ3;YkWj(j;>5n=VRPL4nY6I?R=s@Ie>5%d%X3-o=hvHno3B z@ysMAi>Qex8MR`$n<5r{`pz(JB~Hiw!p#N>1Ibq|ODXj%q>PfzZhkx+qtOa2MtdpC zE41*6csGqVv64?o1TrWFi49&D?+w+^*$A3Kb(9r#;2H86LKOs=jvZl{{$CWz&hDJ2 zvy8Jo7PbbGZ}}DvwV`xj8VS;wYP1UPreib>p~Zl0WqJ$p3N5@Mo*B0cWKfQ4m>RCk z9vDp{$Df%-zP|kFY1C8bvh08LGtCx^nFACl<*UWjWR#SCwX|d+TrEV*IFgBs1lVmh zzq&iX_S+oS|AhRRIXl9dSl@^hf4A5V7%Zm?lV2BkpeV#ih#9$dhI;rqRAsrj*4?cnMOvtrXAN-_sRC~$oXp`j&Y>k#m0wGt zgM4bdF!Hvt8E+xlhI-kXT7JNjdM@yC{mboC^;;X33Y6E(z*$WQ0B~rb}b=q-Pdz@Sg-4tbYbNL<1&qVr; zeXN@8b5@mq#2cq?>hx6=sY#D(tcJbq98-T(!SHP4VgaT~wIFc6}YT)r-Kxm1*I zD2ap~VRyT3%|A^E^7=a+Vqdxfs#42*?odfrEuHPqj}=Sp{T!(M?H9Z9oyjR{9k(eHYNWbdz%n-|A*cxN>?^M%Il?XnE-#nZC_6l zHQ^T&2yGx~MrVZNA%4yFN7!_!J-LM(>C5WdS^BLkJr-Ta`8=cL-Lb!(-#i;ddS>&7yX2(wXPua!*kE~+^b=5M;a%E5UcMFDb8Iv7D~C4`i4+r_N=q~LH!YMCT9iv>d4(3<6IouNxz~V~xSL;+`Eu8c)@s<@0^??H z;!pS@dGXp7W)65twvlsDq6<4(S!cjov8MI1NKnHQ*Pj-)@s;OA zBTl@5Kue4fgl?jPK}*ER*5p&{;!n*KvVckhQ zC4>imBdiy{7S@La%XraJtQ58k+l2KSaX<%~!FeYjQFQcMb4hKGcuC7G4N zgg=qOjPFR1kDo|UfS*fYL35{=WFeN4p#J?+M+c1Tf#*GOTBr&74^Eh*gip%fnctrT9&T`qd{p;GiVD0} zib_1RoQ+o1Bwr++@X>mzc#2Cmm(KLkDl4e=gope5{%%EwOY!@{mf@_hwA?>k0ZD$HnD8CxGULZmj6rj0FOxS{cDGISg3M&ps zVZ-aBD8lVh*zsN|it&&XC3sW{2mVY7C;p1WTI$|~7o^LLf0V+5_HNOu7i*>PVV4x8 zI4ngOZsB6WE7ooY=?U6TX&Ikcnfj+-P;sg;+K;x=<`SvQv*qrfT`0$aYK&QG{7y$E z7FdWmVyV8GJ5;qg#$K-Nstxm;1Jqu_Mv^wu4hNMG4{h;O*JI%rs|vqDjZ&Y4`<1+Q zO2zJ2swtu=YF~^kkA~ItF?Ms*??qnlc|IV`5u#TwLv?tP-lTGHGR7W{_NaSf?0j^{ zb2y#WK`(XS2+;%75LLo>YZV);zft{BjJ;4F_Pm|Rx`(oynXI!hHebJqXALz}c>X<; z_3WaoFJsKwzb$4mwHkcn-41-_qUF@%X;ZyB;JC>!`Eg=+)ItHtd|+vUQ##pre0|? ztLpxG_U5wA4OuS(af2=A;q2Fe5#bOo1oCyES2(iX2%3cB<%L&-fN;cn*}EN8Yz>P!II7-p&s7OfQ@0tWX>2D(RfH z&Qq>8ae`k)*BH=r1>3Ydpw9QQoy$j6%N6YO^3|TJt^lZ9Hv;oh0Q|>;$}86m^pVZE zC)K@Tsg~RrCV&|@?|4Gf;G#~y6m$!Hr}Eez?L2l?ryqMbwMS&^YwZ!Dwe^x7r9zY9KpLEFL{;g=?-f;2M z8Tu*%*yDQ5WgvQN5m{+f0d~Vbcv4hmJVQU%^5L9LHQo=jLTKz4Rlrqfp!Lt;zxhx* z1%9nr;Tu;0ZRa$aslvEogJ(r>26h*Isue*FGp`y^9~wyAxXQOA`P6_Yqdk%nhbSD? zX*S17bWEo+_BhnT%R2p=-AeR^PPf_O&;ak~w9{rK`aq|#qBu0dIh_(kR-)hNbdX|A z@Vlgrk5aG+zSL)M?zxsaGd{3j5(| zo%&6jt|g-W_h`c<95?B~zY`6|bh=;AHl4cZZ1)4|6sBvYAMVoWjQJ1^^d3ocasQC$ zmbHA?(_rvEX(XA0t|=FaDIcN7lfN^g#!} zwEfPb#x-zAr`_fh(PKJ2U_L=~M5i48(=fE?6b`|wA~pp#!zaoR_~|>2`jlW2q4{<$Kr(86y zhYuxH!5@@gYa8G*fpRb#7d6}nO3JqI9sPgC6#)=`OQzJH+A7 zfVTM-HvVK8>pEgdefHE|n;}mGQag?xEwKLJiK+%P;g!t(Z95k5|5zI(J4>tQRlwC6j-qd4LCB lbg3@&t4pb(vtKD}=xkuhGI!iEr+m3j^|c;O diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 6e330471040d46fe1645c4748f46dedb70f5a334..1e6ddc2eb2b1a931c70e25314f94a06444b9bffb 100644 GIT binary patch delta 8009 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 N_A8aW9rn7Q{{v^kVS)ev delta 7857 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 SZKSW=p^5ETJ1EfJ;q`yN*4%jj From 6e38952cf281b7bf8ec6382beefab36345318253 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 01:17:55 +1000 Subject: [PATCH 064/839] peview: Add settings support for all tabs, Move settings support from main into phlib --- .gitignore | 2 +- ProcessHacker/ProcessHacker.def | 36 + ProcessHacker/ProcessHacker.vcxproj | 15 +- ProcessHacker/ProcessHacker.vcxproj.filters | 57 +- ProcessHacker/actions.c | 3 +- ProcessHacker/appsup.c | 126 +- ProcessHacker/hndllist.c | 3 +- ProcessHacker/include/appsup.h | 40 - ProcessHacker/include/phsettings.h | 67 + ProcessHacker/include/settingsp.h | 46 - ProcessHacker/logwnd.c | 1 + ProcessHacker/mainwnd.c | 1 + ProcessHacker/memlists.c | 1 + ProcessHacker/modlist.c | 3 +- ProcessHacker/mwpgproc.c | 1 + ProcessHacker/netlist.c | 3 +- ProcessHacker/notifico.c | 5 +- ProcessHacker/options.c | 3 +- ProcessHacker/procprp.c | 3 +- ProcessHacker/proctree.c | 3 +- ProcessHacker/prpgenv.c | 1 + ProcessHacker/prpgperf.c | 2 + ProcessHacker/sessshad.c | 3 +- ProcessHacker/settings.c | 1067 +------------- ProcessHacker/srvctl.c | 1 + ProcessHacker/srvlist.c | 3 +- ProcessHacker/sysinfo.c | 3 +- ProcessHacker/syssccpu.c | 3 +- ProcessHacker/sysscio.c | 3 +- ProcessHacker/sysscmem.c | 3 +- ProcessHacker/thrdlist.c | 3 +- ProcessHacker/tokprp.c | 1 + {plugins/OnlineChecks => build}/virustotal.s | Bin phlib/guisup.c | 126 -- phlib/icotobmp.c | 4 +- phlib/include/guisup.h | 11 - {ProcessHacker => phlib}/include/settings.h | 151 +- {ProcessHacker => phlib}/mxml/COPYING | 0 {ProcessHacker => phlib}/mxml/config.h | 0 {ProcessHacker => phlib}/mxml/mxml-attr.c | 0 {ProcessHacker => phlib}/mxml/mxml-entity.c | 0 {ProcessHacker => phlib}/mxml/mxml-file.c | 0 {ProcessHacker => phlib}/mxml/mxml-get.c | 0 {ProcessHacker => phlib}/mxml/mxml-index.c | 0 {ProcessHacker => phlib}/mxml/mxml-node.c | 0 {ProcessHacker => phlib}/mxml/mxml-private.c | 0 {ProcessHacker => phlib}/mxml/mxml-private.h | 0 {ProcessHacker => phlib}/mxml/mxml-search.c | 0 {ProcessHacker => phlib}/mxml/mxml-set.c | 0 {ProcessHacker => phlib}/mxml/mxml-string.c | 0 {ProcessHacker => phlib}/mxml/mxml.h | 0 phlib/phlib.vcxproj | 14 + phlib/phlib.vcxproj.filters | 42 + phlib/settings.c | 1251 +++++++++++++++++ plugins/DotNetTools/dn.h | 1 + plugins/ExtendedNotifications/filelog.c | 1 + plugins/ExtendedNotifications/main.c | 1 + plugins/ExtendedServices/extsrv.h | 1 + plugins/ExtendedTools/exttools.h | 1 + plugins/ExtraPlugins/main.h | 1 + plugins/HardwareDevices/devices.h | 1 + plugins/NetworkTools/nettools.h | 1 + plugins/OnlineChecks/onlnchk.h | 1 + plugins/SbieSupport/main.c | 1 + plugins/ToolStatus/toolstatus.h | 1 + plugins/Updater/updater.h | 1 + plugins/UserNotes/usernotes.h | 1 + plugins/WindowExplorer/wndexp.h | 1 + tools/CustomBuildTool/Source Files/Build.cs | 15 +- .../CustomBuildTool/Source Files/HeaderGen.cs | 1 - tools/CustomBuildTool/Source Files/Program.cs | 48 +- .../bin/Release/CustomBuildTool.exe | Bin 158208 -> 157696 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 71168 bytes tools/peview/include/peview.h | 12 + tools/peview/include/prpsh.h | 1 + tools/peview/main.c | 6 +- tools/peview/peprp.c | 59 +- tools/peview/peview.vcxproj | 1 + tools/peview/peview.vcxproj.filters | 3 + tools/peview/prpsh.c | 63 +- tools/peview/settings.c | 141 ++ 81 files changed, 1911 insertions(+), 1564 deletions(-) create mode 100644 ProcessHacker/include/phsettings.h delete mode 100644 ProcessHacker/include/settingsp.h rename {plugins/OnlineChecks => build}/virustotal.s (100%) rename {ProcessHacker => phlib}/include/settings.h (54%) rename {ProcessHacker => phlib}/mxml/COPYING (100%) rename {ProcessHacker => phlib}/mxml/config.h (100%) rename {ProcessHacker => phlib}/mxml/mxml-attr.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-entity.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-file.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-get.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-index.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-node.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-private.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-private.h (100%) rename {ProcessHacker => phlib}/mxml/mxml-search.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-set.c (100%) rename {ProcessHacker => phlib}/mxml/mxml-string.c (100%) rename {ProcessHacker => phlib}/mxml/mxml.h (100%) create mode 100644 phlib/settings.c create mode 100644 tools/peview/settings.c diff --git a/.gitignore b/.gitignore index 334d389b9df0..91909cdebcd6 100644 --- a/.gitignore +++ b/.gitignore @@ -75,7 +75,7 @@ Desktop.ini build/*.exe build/*.zip -ProcessHacker/include/phapprev.h +build/*.h ProcessHacker/sdk/phapppub.h plugins-extra/ /sdk/ diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index fec35a00d451..97692aee76bf 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -523,6 +523,33 @@ EXPORTS PhInitializeMappedImage PhLoadMappedImage +; settings + PhAddSetting + PhAddSettings + PhClearIgnoredSettings + PhConvertIgnoredSettings + PhGetIntegerSetting + PhGetIntegerPairSetting + PhGetScalableIntegerPairSetting + PhGetStringSetting + PhLoadSettings + PhLoadListViewColumnSettings + PhLoadListViewColumnsFromSetting + PhLoadWindowPlacementFromSetting + PhResetSettings + PhSaveListViewColumnSettings + PhSaveListViewColumnsToSetting + PhSaveWindowPlacementToSetting + PhSettingsInitialization + PhSetIntegerSetting + PhSetIntegerPairSetting + PhSetScalableIntegerPairSetting + PhSetScalableIntegerPairSetting2 + PhSetStringSetting + PhSetStringSetting2 + PhSaveSettings + PhUpdateCachedSettings + ; secedit PhCreateSecurityPage PhEditSecurity @@ -576,3 +603,12 @@ EXPORTS PhQueueItemWorkQueue PhQueueItemWorkQueueEx PhWaitForWorkQueue + +; mxml + mxmlDelete + mxmlElementSetAttr + mxmlLoadFd + mxmlNewOpaque + mxmlNewElement + mxmlSaveFd + mxml_opaque_cb \ No newline at end of file diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index abad4684afab..f212a670d21b 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -317,15 +317,6 @@ - - - - - - - - - @@ -353,6 +344,7 @@ + @@ -361,11 +353,8 @@ - - - @@ -382,8 +371,6 @@ - - diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 0af73f22ef55..671c41cb8543 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -16,18 +16,12 @@ {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} @@ -165,9 +159,6 @@ Process Hacker - - Process Hacker - Process Hacker @@ -189,33 +180,6 @@ Process Hacker - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - Process Hacker @@ -414,6 +378,9 @@ Process Hacker + + Process Hacker + @@ -437,12 +404,6 @@ Headers - - Headers - - - Headers - Headers @@ -506,15 +467,6 @@ PCRE\Headers - - Mini-XML\Headers - - - Mini-XML\Headers - - - Mini-XML\Headers - Headers @@ -587,6 +539,9 @@ Headers + + Headers + diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index f0f7526594fc..c9113390bc08 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,7 @@ #include #include #include -#include +#include #include #include diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 1d09f88b4014..4e6a5d25f525 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -30,13 +30,13 @@ #include #include #include +#include #include #include #include -#include +#include -#include "mxml/mxml.h" #include "pcre/pcre2.h" typedef LONG (WINAPI *_GetPackageFullName)( @@ -901,20 +901,6 @@ PPH_STRING PhUnescapeStringForDelimiter( 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 @@ -1184,114 +1170,6 @@ VOID PhSetWindowOpacity( ); } -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 ) diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index 517f838e79ee..6777cefe7159 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -26,11 +26,12 @@ #include #include +#include #include #include #include -#include +#include BOOLEAN PhpHandleNodeHashtableEqualFunction( _In_ PVOID Entry1, diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index e631485c30b9..fe34c8977206 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -124,12 +124,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 @@ -221,40 +215,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 diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h new file mode 100644 index 000000000000..d722745c8815 --- /dev/null +++ b/ProcessHacker/include/phsettings.h @@ -0,0 +1,67 @@ +#ifndef PH_SETTINGS_H +#define PH_SETTINGS_H + +// 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 8b31daaaea08..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/logwnd.c b/ProcessHacker/logwnd.c index d35acede54ac..0a9b71a94b62 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -21,6 +21,7 @@ */ #include +#include #include diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 033a4850bd32..0154356736f9 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 732e15b07729..6f73a3f3d918 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -23,6 +23,7 @@ #include #include +#include #include #include diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 3cca62efabd0..93704252428f 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -25,11 +25,12 @@ #include #include +#include #include #include #include -#include +#include BOOLEAN PhpModuleNodeHashtableEqualFunction( _In_ PVOID Entry1, diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index e9d633637bc9..11307c937294 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index 6efb1357d811..bfc42aca3fb6 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -25,14 +25,15 @@ #include #include +#include #include #include #include #include #include +#include #include -#include BOOLEAN PhpNetworkNodeHashtableEqualFunction( _In_ PVOID Entry1, diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index ccaedd1c3915..729f81412e78 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -757,7 +758,7 @@ VOID PhNfpBeginBitmap2( { HDC screenHdc; - screenHdc = GetDC(NULL); + screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); Context->Hdc = CreateCompatibleDC(screenHdc); memset(&Context->Header, 0, sizeof(BITMAPINFOHEADER)); @@ -768,7 +769,7 @@ VOID PhNfpBeginBitmap2( Context->Header.biBitCount = 32; Context->Bitmap = CreateDIBSection(screenHdc, (BITMAPINFO *)&Context->Header, DIB_RGB_COLORS, &Context->Bits, NULL, 0); - ReleaseDC(NULL, screenHdc); + DeleteDC(screenHdc); Context->Initialized = TRUE; } diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 32782a527ce9..c93fcbef6a06 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -27,10 +27,11 @@ #include #include +#include #include #include -#include +#include #include #define WM_PH_CHILD_EXIT (WM_APP + 301) diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 17488331c501..03236a716ee7 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -27,10 +27,11 @@ #include #include +#include #include +#include #include -#include PPH_OBJECT_TYPE PhpProcessPropContextType; PPH_OBJECT_TYPE PhpProcessPropPageContextType; diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 9cdd693494ed..b27671f789fb 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -34,13 +34,14 @@ #include #include #include +#include #include #include #include #include +#include #include -#include typedef enum _PHP_AGGREGATE_TYPE { diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index 46f6d895bd5b..2f8f181b94a0 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -28,6 +28,7 @@ #include #include +#include #include diff --git a/ProcessHacker/prpgperf.c b/ProcessHacker/prpgperf.c index cfa46d60d7c3..9efb80e93072 100644 --- a/ProcessHacker/prpgperf.c +++ b/ProcessHacker/prpgperf.c @@ -25,10 +25,12 @@ #include #include +#include #include #include #include +#include static VOID NTAPI PerformanceUpdateHandler( _In_opt_ PVOID Parameter, diff --git a/ProcessHacker/sessshad.c b/ProcessHacker/sessshad.c index 58c03ced17f6..62bd01a8317a 100644 --- a/ProcessHacker/sessshad.c +++ b/ProcessHacker/sessshad.c @@ -21,12 +21,11 @@ */ #include +#include #include #include -#include - #define SIP(String, Integer) { (String), (PVOID)(Integer) } static PH_KEY_VALUE_PAIR VirtualKeyPairs[] = diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 5412d4960e18..91077674310b 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -1,8 +1,9 @@ /* * Process Hacker - - * program settings + * program settings cache * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -20,59 +21,15 @@ * 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) +#define PH_SETTINGS_PRIVATE +#include -VOID PhSettingsInitialization( +VOID PhAddDefaultSettings( 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"); @@ -189,8 +146,7 @@ VOID PhSettingsInitialization( PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms - // Colors are specified with R in the lowest byte, then G, then B. - // So: bbggrr. + // 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"); @@ -234,968 +190,61 @@ VOID PhSettingsInitialization( 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); -} + #define PH_UPDATE_SETTING(Name) (PhCs##Name = PhGetIntegerSetting(L#Name)) + + 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(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(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); +} \ No newline at end of file diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 72b9066b80ab..a53d1f769498 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -23,6 +23,7 @@ #include #include +#include #include #include diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 897399a6bb29..003e86f51e31 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -26,12 +26,13 @@ #include #include #include +#include #include #include #include #include -#include +#include #include BOOLEAN PhpServiceNodeHashtableEqualFunction( diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index e8e389dbce97..317667b112a2 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -46,6 +46,7 @@ */ #include +#include #include #include @@ -55,8 +56,8 @@ #include #include +#include #include -#include static HANDLE PhSipThread = NULL; HWND PhSipWindow = NULL; diff --git a/ProcessHacker/syssccpu.c b/ProcessHacker/syssccpu.c index 357159c9f330..b7b82704742f 100644 --- a/ProcessHacker/syssccpu.c +++ b/ProcessHacker/syssccpu.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -28,7 +29,7 @@ #include #include -#include +#include static PPH_SYSINFO_SECTION CpuSection; static HWND CpuDialog; diff --git a/ProcessHacker/sysscio.c b/ProcessHacker/sysscio.c index 2f72b2b37ffa..87626b41a007 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; diff --git a/ProcessHacker/sysscmem.c b/ProcessHacker/sysscmem.c index 9d26867685c1..f375ad8fb9d7 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; diff --git a/ProcessHacker/thrdlist.c b/ProcessHacker/thrdlist.c index cae5d04594aa..37ad2e9c7698 100644 --- a/ProcessHacker/thrdlist.c +++ b/ProcessHacker/thrdlist.c @@ -24,12 +24,13 @@ #include #include +#include #include #include +#include #include #include -#include #include BOOLEAN PhpThreadNodeHashtableEqualFunction( diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 897ba39e005e..88ec93705383 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include 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/guisup.c b/phlib/guisup.c index a9e1436ab891..3ae41b72c5a4 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -219,132 +219,6 @@ VOID PhSetListViewSubItem( 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, diff --git a/phlib/icotobmp.c b/phlib/icotobmp.c index 9d4ce2878c84..b70d72c5b1af 100644 --- a/phlib/icotobmp.c +++ b/phlib/icotobmp.c @@ -160,10 +160,10 @@ HBITMAP PhIconToBitmap( iconRectangle.right = Width; iconRectangle.bottom = Height; - screenHdc = GetDC(NULL); + screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); hdc = CreateCompatibleDC(screenHdc); bitmap = PhpCreateBitmap32(screenHdc, Width, Height, NULL); - ReleaseDC(NULL, screenHdc); + DeleteDC(screenHdc); oldBitmap = SelectObject(hdc, bitmap); paintParams.dwFlags = BPPF_ERASE; diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index b1fe736ed688..d825dd31fe48 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -251,17 +251,6 @@ VOID PhSetListViewSubItem( _In_ PWSTR Text ); -PHLIBAPI -BOOLEAN PhLoadListViewColumnSettings( - _In_ HWND ListViewHandle, - _In_ PPH_STRING Settings - ); - -PHLIBAPI -PPH_STRING PhSaveListViewColumnSettings( - _In_ HWND ListViewHandle - ); - PHLIBAPI INT PhAddTabControlTab( _In_ HWND TabControlHandle, diff --git a/ProcessHacker/include/settings.h b/phlib/include/settings.h similarity index 54% rename from ProcessHacker/include/settings.h rename to phlib/include/settings.h index bf0e871bb5c6..5fadd004d4b8 100644 --- a/ProcessHacker/include/settings.h +++ b/phlib/include/settings.h @@ -1,7 +1,27 @@ -#ifndef PH_SETTINGS_H -#define PH_SETTINGS_H +#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, @@ -25,17 +45,25 @@ typedef struct _PH_SETTING } u; } PH_SETTING, *PPH_SETTING; -VOID PhSettingsInitialization( +PHLIBAPI +VOID +PhSettingsInitialization( VOID ); +// Note: Program specific function. +VOID PhAddDefaultSettings( + VOID + ); + +// Note: Program specific function. VOID PhUpdateCachedSettings( VOID ); // begin_phapppub _May_raise_ -PHAPPAPI +PHLIBAPI ULONG NTAPI PhGetIntegerSetting( @@ -43,7 +71,7 @@ PhGetIntegerSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI PH_INTEGER_PAIR NTAPI PhGetIntegerPairSetting( @@ -51,7 +79,7 @@ PhGetIntegerPairSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI PH_SCALABLE_INTEGER_PAIR NTAPI PhGetScalableIntegerPairSetting( @@ -60,7 +88,7 @@ PhGetScalableIntegerPairSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI PPH_STRING NTAPI PhGetStringSetting( @@ -68,7 +96,7 @@ PhGetStringSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetIntegerSetting( @@ -77,7 +105,7 @@ PhSetIntegerSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetIntegerPairSetting( @@ -86,7 +114,7 @@ PhSetIntegerPairSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetScalableIntegerPairSetting( @@ -95,7 +123,7 @@ PhSetScalableIntegerPairSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetScalableIntegerPairSetting2( @@ -104,7 +132,7 @@ PhSetScalableIntegerPairSetting2( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetStringSetting( @@ -113,7 +141,7 @@ PhSetStringSetting( ); _May_raise_ -PHAPPAPI +PHLIBAPI VOID NTAPI PhSetStringSetting2( @@ -147,6 +175,12 @@ VOID PhResetSettings( // 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; @@ -154,76 +188,49 @@ typedef struct _PH_SETTING_CREATE PWSTR DefaultValue; } PH_SETTING_CREATE, *PPH_SETTING_CREATE; -PHAPPAPI +PHLIBAPI VOID NTAPI PhAddSettings( _In_ PPH_SETTING_CREATE Settings, _In_ ULONG NumberOfSettings ); -// end_phapppub -// Cached settings +VOID +NTAPI +PhLoadWindowPlacementFromSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ); -#undef EXT +VOID +NTAPI +PhSaveWindowPlacementToSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ); -#ifdef PH_SETTINGS_PRIVATE -#define EXT -#else -#define EXT extern -#endif +VOID +NTAPI +PhLoadListViewColumnsFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); -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; +VOID +NTAPI +PhSaveListViewColumnsToSetting( + _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/ProcessHacker/mxml/COPYING b/phlib/mxml/COPYING similarity index 100% rename from ProcessHacker/mxml/COPYING rename to phlib/mxml/COPYING diff --git a/ProcessHacker/mxml/config.h b/phlib/mxml/config.h similarity index 100% rename from ProcessHacker/mxml/config.h rename to phlib/mxml/config.h diff --git a/ProcessHacker/mxml/mxml-attr.c b/phlib/mxml/mxml-attr.c similarity index 100% rename from ProcessHacker/mxml/mxml-attr.c rename to phlib/mxml/mxml-attr.c diff --git a/ProcessHacker/mxml/mxml-entity.c b/phlib/mxml/mxml-entity.c similarity index 100% rename from ProcessHacker/mxml/mxml-entity.c rename to phlib/mxml/mxml-entity.c diff --git a/ProcessHacker/mxml/mxml-file.c b/phlib/mxml/mxml-file.c similarity index 100% rename from ProcessHacker/mxml/mxml-file.c rename to phlib/mxml/mxml-file.c diff --git a/ProcessHacker/mxml/mxml-get.c b/phlib/mxml/mxml-get.c similarity index 100% rename from ProcessHacker/mxml/mxml-get.c rename to phlib/mxml/mxml-get.c diff --git a/ProcessHacker/mxml/mxml-index.c b/phlib/mxml/mxml-index.c similarity index 100% rename from ProcessHacker/mxml/mxml-index.c rename to phlib/mxml/mxml-index.c diff --git a/ProcessHacker/mxml/mxml-node.c b/phlib/mxml/mxml-node.c similarity index 100% rename from ProcessHacker/mxml/mxml-node.c rename to phlib/mxml/mxml-node.c diff --git a/ProcessHacker/mxml/mxml-private.c b/phlib/mxml/mxml-private.c similarity index 100% rename from ProcessHacker/mxml/mxml-private.c rename to phlib/mxml/mxml-private.c diff --git a/ProcessHacker/mxml/mxml-private.h b/phlib/mxml/mxml-private.h similarity index 100% rename from ProcessHacker/mxml/mxml-private.h rename to phlib/mxml/mxml-private.h diff --git a/ProcessHacker/mxml/mxml-search.c b/phlib/mxml/mxml-search.c similarity index 100% rename from ProcessHacker/mxml/mxml-search.c rename to phlib/mxml/mxml-search.c diff --git a/ProcessHacker/mxml/mxml-set.c b/phlib/mxml/mxml-set.c similarity index 100% rename from ProcessHacker/mxml/mxml-set.c rename to phlib/mxml/mxml-set.c diff --git a/ProcessHacker/mxml/mxml-string.c b/phlib/mxml/mxml-string.c similarity index 100% rename from ProcessHacker/mxml/mxml-string.c rename to phlib/mxml/mxml-string.c diff --git a/ProcessHacker/mxml/mxml.h b/phlib/mxml/mxml.h similarity index 100% rename from ProcessHacker/mxml/mxml.h rename to phlib/mxml/mxml.h diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index aba1e17fe699..f9b2a7f82826 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -167,12 +167,22 @@ + + + + + + + + + + @@ -208,6 +218,7 @@ + @@ -235,6 +246,9 @@ + + + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index 176452ccb152..e9859bc8d171 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -140,6 +140,36 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -316,5 +346,17 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/phlib/settings.c b/phlib/settings.c new file mode 100644 index 000000000000..5687b891d26b --- /dev/null +++ b/phlib/settings.c @@ -0,0 +1,1251 @@ +/* + * Process Hacker - + * settings + * + * 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 . + */ + +/* + * 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 "mxml/mxml.h" + +BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpSettingsHashtableHashFunction( + _In_ PVOID Entry + ); + +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 + ); + +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 = CreateIC(L"DISPLAY", NULL, NULL, NULL)) + { + dpi = GetDeviceCaps(hdc, LOGPIXELSY); + DeleteDC(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); +} + +PPH_STRING PhpGetOpaqueXmlNodeText( + _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 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 = 0; + + settingValue = PhpGetOpaqueXmlNodeText(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 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)); + + PhpSettingFromString(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 (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); +} + +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); +} \ No newline at end of file diff --git a/plugins/DotNetTools/dn.h b/plugins/DotNetTools/dn.h index 72f7e24132f3..95b2a6323cac 100644 --- a/plugins/DotNetTools/dn.h +++ b/plugins/DotNetTools/dn.h @@ -26,6 +26,7 @@ #define CINTERFACE #define COBJMACROS #include +#include #include #include "resource.h" diff --git a/plugins/ExtendedNotifications/filelog.c b/plugins/ExtendedNotifications/filelog.c index 372dc978958b..e1cc06587719 100644 --- a/plugins/ExtendedNotifications/filelog.c +++ b/plugins/ExtendedNotifications/filelog.c @@ -21,6 +21,7 @@ */ #include +#include #include "extnoti.h" VOID NTAPI LoggedCallback( diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index 293db5c4deab..33abdab44813 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -22,6 +22,7 @@ #include #include +#include #include #include "extnoti.h" #include "resource.h" diff --git a/plugins/ExtendedServices/extsrv.h b/plugins/ExtendedServices/extsrv.h index ff6ee0bd3648..2a15f62d44fe 100644 --- a/plugins/ExtendedServices/extsrv.h +++ b/plugins/ExtendedServices/extsrv.h @@ -2,6 +2,7 @@ #define ES_EXTSRV_H #include +#include #include #include "resource.h" diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index e76d42f99688..47ddf23fb115 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -2,6 +2,7 @@ #define EXTTOOLS_H #include +#include #include #include diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h index aaa69f001f96..8bb75a8fb1d5 100644 --- a/plugins/ExtraPlugins/main.h +++ b/plugins/ExtraPlugins/main.h @@ -27,6 +27,7 @@ #define COBJMACROS #include #include +#include #include #include #include diff --git a/plugins/HardwareDevices/devices.h b/plugins/HardwareDevices/devices.h index 466df57edf2a..e6b70f33d775 100644 --- a/plugins/HardwareDevices/devices.h +++ b/plugins/HardwareDevices/devices.h @@ -37,6 +37,7 @@ #define COBJMACROS #include #include +#include #include #include diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 47b8722f86a8..64ff271dc3c5 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 130be7849571..6e57a920a0f6 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -28,6 +28,7 @@ #define COBJMACROS #include #include +#include #include #include #include diff --git a/plugins/SbieSupport/main.c b/plugins/SbieSupport/main.c index a355a0acfd99..ff84ca975af1 100644 --- a/plugins/SbieSupport/main.c +++ b/plugins/SbieSupport/main.c @@ -21,6 +21,7 @@ */ #include +#include #include "resource.h" #include "sbiedll.h" diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 0bc63b8f4fd1..04751703fb12 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 41899d0c5cbf..a6835fa9268b 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/plugins/UserNotes/usernotes.h b/plugins/UserNotes/usernotes.h index 1d323c874c8a..afe98aa95911 100644 --- a/plugins/UserNotes/usernotes.h +++ b/plugins/UserNotes/usernotes.h @@ -26,6 +26,7 @@ #include #include +#include #include #include diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index 6be77e09186f..dba7a96bc9a2 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -3,6 +3,7 @@ #include #include +#include #include "wndtree.h" extern BOOLEAN IsHookClient; diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 26a1dcbd8aad..779095ae0fc3 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -108,6 +108,7 @@ public static class Build "queuedlock.h", "ref.h", "secedit.h", + "settings.h", "svcsup.h", "symprv.h", "templ.h", @@ -436,14 +437,10 @@ public static bool CopyPluginSdkHeaders() // Copy the plugin SDK headers foreach (string file in phnt_headers) - { File.Copy("phnt\\include\\" + file, "sdk\\include\\" + file, true); - } - foreach (string file in phlib_headers) - { File.Copy("phlib\\include\\" + file, "sdk\\include\\" + file, true); - } + File.Copy("phlib\\mxml\\mxml.h", "sdk\\include\\mxml.h", true); // Copy readme File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); @@ -482,7 +479,6 @@ public static bool CopyVersionHeader() File.Copy("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", true); File.Copy("ProcessHacker\\sdk\\phdk.h", "sdk\\include\\phdk.h", true); - File.Copy("ProcessHacker\\mxml\\mxml.h", "sdk\\include\\mxml.h", true); File.Copy("ProcessHacker\\resource.h", "sdk\\include\\phappresource.h", true); } catch (Exception ex) @@ -680,7 +676,7 @@ public static bool BuildSecureFiles() { Verify.Decrypt("build\\kph.s", "build\\kph.key", kphKey); Verify.Decrypt("build\\nightly.s", "build\\nightly.key", buildKey); - Verify.Decrypt("plugins\\OnlineChecks\\virustotal.s", "plugins\\OnlineChecks\\virustotal.h", vtBuildKey); + Verify.Decrypt("build\\virustotal.s", "build\\virustotal.h", vtBuildKey); } catch (Exception ex) { @@ -723,8 +719,6 @@ public static bool BuildSdkZip() try { - File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); - Zip.CreateCompressedSdkFromFolder("sdk", BuildOutputFolder + "\\processhacker-build-sdk.zip"); } catch (Exception ex) @@ -810,9 +804,6 @@ public static bool BuildPdbZip() { try { - if (File.Exists(BuildOutputFolder + "\\processhacker-build-pdb.zip")) - File.Delete(BuildOutputFolder + "\\processhacker-build-pdb.zip"); - Zip.CreateCompressedPdbFromFolder(".\\", BuildOutputFolder + "\\processhacker-build-pdb.zip"); } catch (Exception ex) diff --git a/tools/CustomBuildTool/Source Files/HeaderGen.cs b/tools/CustomBuildTool/Source Files/HeaderGen.cs index e4df545defbd..cd4c1dbe70a4 100644 --- a/tools/CustomBuildTool/Source Files/HeaderGen.cs +++ b/tools/CustomBuildTool/Source Files/HeaderGen.cs @@ -50,7 +50,6 @@ public HeaderGen() "extmgr.h", "mainwnd.h", "notifico.h", - "settings.h", "phplug.h", "actions.h", "procprp.h", diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index dbf2eeac7091..8245aef57288 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -14,11 +14,13 @@ public static void Main(string[] args) if (ProgramArgs.ContainsKey("-cleanup")) { + if (Restart("-cleanup")) + return; + if (!Build.InitializeBuildEnvironment(true)) return; Build.CleanupAppxSignature(); - Build.CleanupBuildEnvironment(); Build.ShowBuildStats(true); @@ -283,26 +285,8 @@ public static void Main(string[] args) } else if (ProgramArgs.ContainsKey("-appxmakecert")) { - WindowsIdentity identity = WindowsIdentity.GetCurrent(); - WindowsPrincipal principal = new WindowsPrincipal(identity); - - if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) - { - Win32.ShowWindow(Win32.GetConsoleWindow(), Win32.SW_HIDE); - - try - { - System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo - { - FileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName, - Arguments = "-appxmakecert", - Verb = "runas" - }); - } - catch (Exception) { } - + if (Restart("-appxmakecert")) return; - } if (!Build.InitializeBuildEnvironment(false)) return; @@ -363,6 +347,30 @@ public static void PrintColorMessage(string Message, ConsoleColor Color, bool Ne 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; + + Win32.ShowWindow(Win32.GetConsoleWindow(), Win32.SW_HIDE); + + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName, + Arguments = Arguments, + Verb = "runas" + }); + } + catch (Exception) { } + + return true; + } } [Flags] diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index cc4447fd6e5f2b48ee48b907c54786ec1cd927ae..6071333ab93cf8ba6e32052da998c7ef9a4dfe6d 100644 GIT binary patch delta 7604 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 GlfMJd&!Wx% delta 7718 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/j$Ho_mV3I2p>cp202 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 V`#)}|=G3w#>2tTw3$Xe){|_QkKkfhk delta 15004 zcmaLe349G#`@r!tH@T@qqKS~zl86Kii3kZvsfaB|2x8x2DY37$m3w2Sw31UzX{p$1 z2^FcWt*u3)TBTK0s?~*}wDndgO8>uSGUTk%<|zD@kgT})oK zKJS~tq&3F2pw?~`K5gHA`mM|`}o#$u)w$e)7K?o#&&}Nyd?E^a*dhEjiAZs4piE^XX4Q=}>_@y{rc{53@_^ z%X(3Bf7@5ydPRJHU8Y5VaZ`u3h~zi5g}=?!M?c%5o-t4_Z86oz))iZhD^uRLWOq9B zhXB34rMIoTuYRZH45Pbl+$z@StH-o@#yF}ATTL}C=sK-q`OR!Sz?h>CwT?Dc=s#M= z8;5kAlxX8e-8&`T=H;hfNr^H#=tC(EW4dn9rUt(w+V~rD_3SqBMn}D;jl<>_pqsRP zD#$P3zX#eaP^ra1N_|3cRu9lK+HNt{>X>%X#%7(_F4_29uWdKg<`tx?r9LI3rS`GK z1nDiQakk)KeKB>4k)SivqWR59^XGR%TCCAkpG%83PU?#7hxzQal@^v#l_{>@bx!-i zjwwnxIb&)nvVZkF*2HNTkJGUwI{1;{tpY7vXAr5!d1z zdDve}oJI6=Y^JdLdE{1KPofABTDfh$ld+Da^k`RI*n&==QXRa}pCa05o; zW=zCFY>C^jH5Ort5vw12d6aItSpp+9!Qy_kEm`_C%kPnvQb3o$w25ty4J2 znxADk3ON+#82k#y;#r)7U*kMHr$ak-b$mzU!omH3RB^?DJIRvz2_x|m4#F!arT#O@ zMRyHn;jbvA{TmkGb=;4?<6-;*KgL_g5vbe9zSJGOAf z7>91y1j}F-bjN<^iNldI;+%p{q8wg%d<8i(&P~WRo$sJOeuP2jrSx;1R>f3d(wi)V zU_}hYo>&d#*u!v)(O47iZuooQV#c zg)iZ3R5I9C*&ykZSCFJBBsV{Lcp4PLwXXtObx6$4iu8xu$zoDc(lEI5nvMs~c ztRZE39xLE?7=%CIQ}`n$;00`h7qJKaguU<*_QoqHMfNjJz+X_>fL}S4t9$s{yD1;l zSHG1}S)c9^lKwk$Wv{m|5N~5B-o<)&4;$m(_zZF-m}ks}oe5pA8@i$7<_VqBvs026 zktb0(l=|UqQ*-5N4IqxgK#a#=?0}U}wpJCTEeO%)dbUlfP9%A&iBcl9P)g(}lv);v zm9Y-WR-;f#CZN_O;OfvhRrbT(w*){)Q^w_gF~?q4o4bQH3H>Q9*r3|76;%s9Apeo>NzH8$J9ieij#0IPDa`dH5FIl^T?H} zrsId`#Iu-<=WrJOfpgH^rqo<4i!bPkea<=-6G{2zpq%SmOv9H^+W93YEp#4AleiS+ z{=5vOX?z`}p02>z_y*3!e3YhgH7>_B_y$T-uo5>AQ>+PUBNO{c*o4Qi06)XcC^z!A zkWPZyidV4+f5Er)hQ2Ar6#aYO#+kcFlgjcAN)>w-tBU)Gr2*TIDfqrk&Fqhd zi3j3`D7VTZC>QxhI1N8R2OiTo{SqBgO$ykJG_O+KW#BPDO){sH`i!L|7pGA6ej4R+ z`3f818BD;hF%8dQM?8;m8GVOdsw)?I7sgREGvydVnNDM@qMyn1aoC(0i zb-;K`$Bu@or#dkqz1hw<5;JfVc13zSsvAmU(-Y+e*-K9tIGAV1a|46B_ajX*-yh|S z4n#SlgHVcY2*%-1lzKM|rRYXtFC2|SaSX~qj(1TrrKy;#(+4HW1L=!{f*doL=}F>D zl>6;048++eJDr0OI2UW+Je2J`D=`G~ zv8F-$zlw=kB&@+CT#wRjZ$#;O6<{CSf)jBo(vMWza3Q{pn{WqiMycmJaVPO^+=cXy z)H_JGM7@ij;d{vQ!n$YY$RqgRV8?zJHM=o6K8&y8CoCW>=`qa5++eJLsNJf24>DcK%%%b~s};WrX4q7>av_%~icgB)K$>Ae1o z!6+LDwWKK3Z^TjwWrK1j{10(Myn!9?Pt3yGI1KOL2>c6Y7+fCrn2^dS8(L)fGTK~A z9=%*#_0SJm2&GOZ zptJ&wu^B#t(h4-i9+-sEP&dP|n2gi01gm#UNMd!mM+IglrG08 zSAXUFmWeSW+`zGT6US-yVJ|qmf=b$9sSGj%GfG8}VIoUP*-S<$n<*$|Gac)q0~64R z(x_x(2b_V@xXeT;tJ$tLAEiRb@?G#C%1!JLN(*`zC*g-Ew~JzY5s#oW86V*? zZjtIJ6LQo11m)n5q1-G_pxjqJ)7wY%b^o0_NhfEot~|1dv974sNT-oII>f%72ZoC} zd~~95QD=^xYFsQjH#*s{b60kot;5GwHD>D;V~5#aAzrArkBtso$eRR;z&99;KgsE>8Dj(lbi0daj<^T;}^m=`&1Hb=-oY^ongky7t6MZu1F4qjcKD z1p5LeYwAT46YVcB6|FyEY9Ui`+G|q0eGyZQb;_hfJLkczxt>qjOH8%Zhe%t@R7Y)} zTs5#`)PGB;f{S_AP;w9bRyUr!$%k|K&k;+#xUbWv1nUY@ybZfHrUvV#OnI79!G0`c{H9F**4U_PIcCze+w7=L(J|s$R)TI`ncS-FilC<25~DMwp#Dnp?77KBE=!1>%*&ZM4@+i8E$~+4G4z z>uF|uhtMb(6hYBgGo?bQ~$mcoQh_-5qSzDy+lJ%Hb!9LVYXNZdUC$)`U zPU@RxYV<#;on=|=J=@i*k!t!+YEMZuHWvBJe$ueh{<;)!Jtwf8(vn}2OxW!_M3So7zF*`2J}RTs_+ z3+!6^f%2p*Kc+%-ChJ|)|SP*8Epvb-`*zlA> zJ!@UPoT&ia{KX2kvoShtPBs1fi?17Jb>K_Efi$pAe>OD^X*1M#ly>Yno%+(ggxUs^ z40l~il;5drw=VrQb5?3*)=1Lidx|V_gHe{P8=>1wucs$14hfW5^-2DXaR%dZt_@kW ziVn^3)vxAOFoN~w++h83Y<=xL*V`6i)88!iYY@o;g}H|gnPbNdN*X(u49ceJl6aMI zGMgw}CP;<&5UNVLR!*=k$n~s~Id*V|K_drdjT~IE)le4#931idh(jqmg}>?~~Yvc}u8)G+C-9w@@c7mb5Zv zr-7Y2H8FKh?h&dEa`qJsllhJ=8dnoj8O*0aE?=kgpExbfBBXC+-a^Q<^AK@E`~(}} zF-+th{WUfwK8MfHqjdhrL{k#zU#cX$ip}sECZkk^=J*G;U<3ED74hGgf_6?v81OVGYZqOK6bsxL}^wI7bfOdN;(aS9H=sW^!J%|fb^b2d6SPucQ*b{GlLbsT}OBNu~n zIdcA-Yj6y%MY+5-;dqp8*96>;6H&Tflkf;m!IQ{E<~)PbxM-x}I{9}Qxq_V6<=JK? z6aOJ$7V_#)&Bi-82W>VUs?Y_w`#C+3+n)K#hzjH^PfY!B`Xcu`XAmwzUJA-5uo=}c zhd3OmS2JSq72^8%Dn4za{FgGpn>_QAS2EHwb2i82*bU#nez+1daTW67Os&STxCSTT zI!ba5t|#WbmfDCf;U?tGmMTErT$!K7Ud1iMD;-SG`El}!N^QmUSctrmGQXMerpf$f zR)}wtz6-bGZrs5Rd3&UG5r2U15TC&JhoY5AE_nFFOaX5a%M7xRZx)KIn1%zsAI`B&@>Ua5Y{>9)eFD<2Ix%a26qL zfs==2brX45R)69lyp12>9XyJ6@oT(?KO$|1^8(TqJ9&ashJ59@$%GAg##FAzQ=+0# zaq^U?p1|^027S>TgU|zmu`E_Yo&ua<=!HB5nqLNa3N*hA)<>QSoIL88UkG_*Grtfv zM~8eOp90@-lPvg$Zn1H`xBL0R=@D4UbFBPf0 zRD+@qmioI?PT_@QDq~yTceUqURSEqVBe<a(xcu|-B|kL5v*=B!zkvTtE-fkqsC zh~eC+Ft?TLAFZr%>NU?*bQZD&91fi$b- z%^i&)L3TuY$v2ek%e!)`_F56#Xdr2Bqz%F+a4>q|5cI|@l+4k{n=f>SV;J!Wow*{+ zhkB{XF!thVl_5aac_X+m`FSIEWr%$S{hWI`d}X9@Pp7YZ%WJ0ed+J!d5*;-(O(CydM!}ad$5ysuXafgVD1b zx8kChHNP76HLTlThp(%_wo=!{8Vhvxx=wVTzgQP$U(38t^}Th;KCeBnhkj(czfONM zxRx})o_fPAUmzKCwtks0Szlg1)t*cG5}man+O{NGSJ~9YYo5Ukh_cTxdj#;O) z*RDwNBU|Xa%iDILWC7LI@5tRxNq(0B`IW`bvv|#i7C+D8-tPoBwo;TIGyce^s9OPy^%>hS4q=o_b|vF3 z#?y?yF?#yY6J~76*o|>K<3h&ujQbhCX1u}ZU7l0M*qpH!<0Qt#j0KDb8PAtjHua~@ zf5+EjJk8cZ#`V61NeM>odll+Q->`J3N`)z`B1g**HDLUxu_ezCa!GKFDOt9#iuD*( zU%&odFCN>k@2RQ-_ITQSLv+NRG+WIO{oI}@MrYC^`K`Ry-x#kO?44??(}jB@`8~IH zl(Ao@?u+HwGkag6(Ngc(x5@ZeXYCK;chUZ-wp!J+$AKtYy=pq)KocWbPd*T9Y|)z! zMBD7u^*0A*7=3i+`;q)EdVj8aNq!Alg32Gn`Q_6D(@8J++irbTHnrE~F&)5gd;U z!HlJE9ckjs-1kAreP85CHoNqad)Z=;6<4$3S~!5^;*qA&+~Z&zLdCCx%7@i02~ zPTE#AoP?D)3h6saSAXtFoJ@Qcr{YC?9);y0}Mo8@tXjhoLW@8K8B*oX3(SW5MKEB+bZC!OaL zvy=Q69wZLn4sZy=@G#cGVvI*wzmXL;!y}}(v0}dCoAU?Q!b)~H#!8Uh|1lPP0gq!2 zp3oycT+P+sQylD*Lz+~!T;xLLFDx4uhehqDE}vw)%IH~vMuo9CV=qSeD~DGZw=xzp zUaC--G}g%dsJbm6Tt|EqA*6jY#a1O;fArA|?oX*l+e+H9qpuLed>mhno|CG?Sf8<7 z#loZoM((jP>2=-ctc+z_*+0OE zk@aRp5F4QNB5=HCN5hg)WX{3z0y^!EM;3R zwdPl}*2}kc>0=$U{1Jy_tx)?M_Ya&vPg9~VtPKBQ&E2k(j{B#aunzl^(sO8S`-^(Y8%?I`E&EEc_PN$N=*m{<8&*z}tgU%jM`Y-n)%=DB*Q8 zwYS1jtz+b?knGQ#T1*7HBFTC0{-FpI3Y-7S|}Tf1$|ucg2GysGVTIeq8zD$m@rGMr=0JY?D3 z%F=BsON*@7&VkuHyyc~*a#e=zd$M9#4{9uhEYDKfak8ROO)oj=Fg$d{FE;R}Z~MNe z$}8$`zX&l@?x`yJ?0t7#MC4KMIIAd|qm@mIHa*~U;ow?>=G}OQR7-=-nK^GGF zxY6Z8tP!sl@Y_)Dy%29S(l`07ucI!;v&^#>qgmz^e&hAwi?Ym}i}CvQ#j^V8pXwNy zdgxE_I_syhx`1eaK11{sQKd_Dc=g`qQk<>fvwFs*NS@!eTzZqFE|=qMjh@vnUXIs` zE|=BEFV`_*^!>~6Mm-&OC6)z-UWw8}u6XJ-SHktuD`oY`E8#40=gKoYswMm!*GFDV zyQ%-at@cy3D1YPAKbqzE}x?7l&r@V9vCDo zfiB|i(wxah(FgxyLFuxFn){RVpi7?u3^gbJ!Fj$R<|}S9>Ct(enb*-;_|bXmm}hlW z?OgwJ@^#%i0ebOYb{%-LGN(WHPLOA?{F$CPoGh#R+GK~mk3Th&KhFJs zgCy}0TumyiEitqVBT9o61Y=7BZ<^X;1WB^aHw5XA*`EC6v%FP*bdaR?X_+UNZZVM- zr&no^N6@V__?4gu0j&+yg7(vk{69L#JbmMCP2;q#{Fih`DYmk{>ZCl~Fr3sQ{*q3q z6z2PX@n@*!RK&f~LZ9=`)Wv%tj=4X59Lf^bZXT@zeX8?^uO*u`tAI7h^vA`^nAglo zun|8vPd?0+uJ`b~L&X8%e4n!xc5$fBA6W5F@r%slW@Qd856wGTyo`CY=jPz5jy|y7 z(c*mOQT+dN-iCi!&$0DiR^0V3EAIQ3^*;QU^^QHfp5vjTIQ1_po_l!3ht|9BFY8_X zm-TLh^H}$|BE9=BE4oBHem!~D^0-q`{?YZ=zd0D3&%l3KG4#X)ihN1PJ75^WM%}z~h7rj5x?hLl*9W$h+z;(h4@RC3MZOP3K@UZt4@ET} zilQHi;vR|`KNK~8C~EsqB!65|{7*iEqItcLm+xZu8%y%CT{xhjdCS56WnDgunKSIO zV>p&PA6J%Hn0H;4*`4R*$};=@nPET7WW|zY&gNw@(}>EOCM4&bkVV_n<5Njqwrr!b znPT)Tp?{vco%!UBxV3YH-O#Q%9V+E|xIXqjj_$1FVcf9W?pDYv x?`Kap2A+-o`Nd{{aDe8{_~0 diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index bcb62f34d4ce..9de0f19c7411 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -5,6 +5,8 @@ #include #include #include +#include + #include "resource.h" extern PPH_STRING PvFileName; @@ -36,4 +38,14 @@ VOID PvHandleListViewNotifyForCopy( _In_ HWND ListViewHandle ); +// settings + +VOID PeInitializeSettings( + VOID + ); + +VOID PeSaveSettings( + VOID + ); + #endif diff --git a/tools/peview/include/prpsh.h b/tools/peview/include/prpsh.h index 4f57f384f7d2..1290e7f63120 100644 --- a/tools/peview/include/prpsh.h +++ b/tools/peview/include/prpsh.h @@ -37,6 +37,7 @@ typedef struct _PV_PROPSHEETCONTEXT typedef struct _PV_PROPCONTEXT { PPH_STRING Title; + PPH_STRING StartPage; PROPSHEETHEADER PropSheetHeader; HPROPSHEETPAGE *PropSheetPages; } PV_PROPCONTEXT, *PPV_PROPCONTEXT; diff --git a/tools/peview/main.c b/tools/peview/main.c index d3e580a7ec82..5b223fde2635 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -46,7 +46,7 @@ static VOID PvpInitializeDpi( if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) { PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); + DeleteDC(hdc); } } @@ -68,6 +68,8 @@ INT WINAPI wWinMain( PhGuiSupportInitialization(); PvpInitializeDpi(); + PhSettingsInitialization(); + PeInitializeSettings(); PvPropInitialization(); PhApplicationName = L"PE Viewer"; @@ -124,5 +126,7 @@ INT WINAPI wWinMain( else PvLibProperties(); + PeSaveSettings(); + return 0; } diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 5d7b9fc84053..26d6bb7606ec 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -344,8 +344,6 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( PWSTR type; PH_STRING_BUILDER stringBuilder; - PhCenterWindow(GetParent(hwndDlg), NULL); - // File version information { @@ -755,6 +753,7 @@ INT_PTR CALLBACK PvpPeImportsDlgProc( PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 50, L"Hint"); PhSetExtendedListView(lvHandle); ExtendedListView_AddFallbackColumns(lvHandle, 3, fallbackColumns); + PhLoadListViewColumnsFromSetting(L"ImageImportsListViewColumns", lvHandle); if (NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage))) { @@ -771,6 +770,11 @@ INT_PTR CALLBACK PvpPeImportsDlgProc( EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageImportsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) @@ -824,10 +828,12 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( 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"RVA"); + 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"); PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageExportsListViewColumns", lvHandle); if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage))) { @@ -849,31 +855,34 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (!exportFunction.Function) continue; - if (exportEntry.Name) + PhPrintUInt64(number, i + 1); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + if (exportFunction.ForwardedName) { - name = PhZeroExtendToUtf16(exportEntry.Name); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); + name = PhZeroExtendToUtf16(exportFunction.ForwardedName); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, name->Buffer); PhDereferenceObject(name); } else { - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); + PhPrintPointer(pointer, exportFunction.Function); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); } - PhPrintUInt32(number, exportEntry.Ordinal); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, number); - - if (exportFunction.ForwardedName) + if (exportEntry.Name) { - name = PhZeroExtendToUtf16(exportFunction.ForwardedName); + name = PhZeroExtendToUtf16(exportEntry.Name); PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); PhDereferenceObject(name); } else { - PhPrintPointer(pointer, exportFunction.Function); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); } + + PhPrintUInt32(number, exportEntry.Ordinal); + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, number); } } } @@ -883,6 +892,11 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageExportsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) @@ -936,6 +950,8 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( 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_VALUE(Name, Value) \ { \ @@ -1012,6 +1028,11 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageLoadCfgListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) @@ -1148,6 +1169,7 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( 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); // Init symbol resolver if (PvpLoadDbgHelp(&symbolProvider)) @@ -1247,6 +1269,11 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageCfgListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 29c125003e18..e37234a5e523 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -196,6 +196,7 @@ + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index ff1f55b4ac2f..4abd9aaa1ebb 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index e6ac16b05e8e..717f1d1f4222 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -23,6 +23,7 @@ // NOTE: Copied from processhacker2\ProcessHacker\procprp.c #include +#include PPH_OBJECT_TYPE PvpPropContextType; PPH_OBJECT_TYPE PvpPropPageContextType; @@ -80,6 +81,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)); @@ -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,7 +115,9 @@ VOID NTAPI PvpPropContextDeleteProcedure( PPV_PROPCONTEXT propContext = (PPV_PROPCONTEXT)Object; PhFree(propContext->PropSheetPages); + PhDereferenceObject(propContext->Title); + PhDereferenceObject(propContext->StartPage); } INT CALLBACK PvpPropSheetProc( @@ -189,6 +196,30 @@ LRESULT CALLBACK PvpPropSheetWndProc( 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 = sizeof(text) / 2 - 1; + + if (TabCtrl_GetItem(tabControl, TabCtrl_GetCurSel(tabControl), &tabItem)) + { + PhSetStringSetting(L"MainWindowPage", text); + } + } + break; case WM_NCDESTROY: { RemoveWindowSubclass(hWnd, PvpPropSheetWndProc, uIdSubclass); @@ -262,6 +293,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 @@ -378,13 +435,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(propSheetContext, parent); + doLayoutStage2 = PhpInitializePropSheetLayoutStage1(propSheetContext, parent); if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) realParentItem = ParentItem; @@ -424,6 +482,9 @@ PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( item = PhAddLayoutItem(layoutManager, Handle, realParentItem, Anchor); } + if (doLayoutStage2) + PhpInitializePropSheetLayoutStage2(parent); + return item; } diff --git a/tools/peview/settings.c b/tools/peview/settings.c new file mode 100644 index 000000000000..4470b16fea46 --- /dev/null +++ b/tools/peview/settings.c @@ -0,0 +1,141 @@ +/* + * 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 + 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"ImageCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageExportsListViewColumns", L""); + PhpAddStringSetting(L"ImageImportsListViewColumns", L""); + PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); +} + +VOID PhUpdateCachedSettings( + VOID + ) +{ + NOTHING; +} + +VOID PeInitializeSettings( + VOID + ) +{ + 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 = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\peview.xml"); + } + + 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 (PhShowMessage( + NULL, + MB_ICONWARNING | MB_YESNO, + L"PE View'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, + 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); +} From 1aa9b357ee06454687a3e907895eb9da9d1102d2 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 01:32:43 +1000 Subject: [PATCH 065/839] phlib: Fix solution filters --- phlib/phlib.vcxproj.filters | 42 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index e9859bc8d171..aef40882c991 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -9,6 +9,12 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd + + {7d0c2da2-811d-4844-9704-0c87067c7be1} + + + {874b89c3-fb60-46f3-a62c-49ac88e3026d} + @@ -140,35 +146,35 @@ Source Files - + Source Files + + Mini-XML + - Source Files + Mini-XML - Source Files + Mini-XML - Source Files + Mini-XML - Source Files + Mini-XML - Source Files + Mini-XML - Source Files + Mini-XML - Source Files + Mini-XML - Source Files - - - Source Files + Mini-XML @@ -346,17 +352,17 @@ Header Files - + Header Files - - Header Files + + Mini-XML\Headers - Header Files + Mini-XML\Headers - - Header Files + + Mini-XML\Headers \ No newline at end of file From b7dba3db05ab83b35aa3160a0a1c2cd779aa5889 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 03:05:31 +1000 Subject: [PATCH 066/839] peview: Fix lib support (some lib files were not showing exported object data) --- phlib/maplib.c | 2 -- tools/peview/main.c | 6 +++--- tools/peview/peprp.c | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/phlib/maplib.c b/phlib/maplib.c index 41b80cdfbafc..4600b375909f 100644 --- a/phlib/maplib.c +++ b/phlib/maplib.c @@ -371,8 +371,6 @@ NTSTATUS PhGetMappedArchiveImportEntry( 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 diff --git a/tools/peview/main.c b/tools/peview/main.c index 5b223fde2635..893e7ee0cb48 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -121,10 +121,10 @@ INT WINAPI wWinMain( PhMoveReference(&PvFileName, targetFileName); } - if (!PhEndsWithString2(PvFileName, L".lib", TRUE)) - PvPeProperties(); - else + if (PhEndsWithString2(PvFileName, L".lib", TRUE)) PvLibProperties(); + else + PvPeProperties(); PeSaveSettings(); diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 26d6bb7606ec..38947aaf0420 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -695,10 +695,10 @@ VOID PvpProcessImports( PPH_STRING name; WCHAR number[PH_INT32_STR_LEN_1]; - if (!DelayImports) - name = PhZeroExtendToUtf16(importDll.Name); - else + if (DelayImports) name = PhFormatString(L"%S (Delay)", importDll.Name); + else + name = PhZeroExtendToUtf16(importDll.Name); lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, name->Buffer, NULL); PhDereferenceObject(name); From 6e91ac11bc2c6fec05b102b17637736cf9f66171 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 07:28:25 +1000 Subject: [PATCH 067/839] SbieSupport: Move to plugins-extras --- plugins/Plugins.sln | 12 +- plugins/SbieSupport/SbieSupport.rc | 146 ----- plugins/SbieSupport/SbieSupport.vcxproj | 65 -- .../SbieSupport/SbieSupport.vcxproj.filters | 35 -- plugins/SbieSupport/main.c | 556 ------------------ plugins/SbieSupport/resource.h | 18 - plugins/SbieSupport/sbiedll.h | 42 -- 7 files changed, 1 insertion(+), 873 deletions(-) delete mode 100644 plugins/SbieSupport/SbieSupport.rc delete mode 100644 plugins/SbieSupport/SbieSupport.vcxproj delete mode 100644 plugins/SbieSupport/SbieSupport.vcxproj.filters delete mode 100644 plugins/SbieSupport/main.c delete mode 100644 plugins/SbieSupport/resource.h delete mode 100644 plugins/SbieSupport/sbiedll.h diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index ae4f9f9a95bd..15c2c8271d2b 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 15.0.26228.4 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53C3AE07-D96F-4F5C-B407-4195084472CF}" ProjectSection(SolutionItems) = preProject @@ -10,8 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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}" @@ -46,14 +44,6 @@ Global 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 diff --git a/plugins/SbieSupport/SbieSupport.rc b/plugins/SbieSupport/SbieSupport.rc deleted file mode 100644 index 82c4ae0686ab..000000000000 --- a/plugins/SbieSupport/SbieSupport.rc +++ /dev/null @@ -1,146 +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) - -#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 - diff --git a/plugins/SbieSupport/SbieSupport.vcxproj b/plugins/SbieSupport/SbieSupport.vcxproj deleted file mode 100644 index 08a3a4d34cab..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/SbieSupport.vcxproj.filters b/plugins/SbieSupport/SbieSupport.vcxproj.filters deleted file mode 100644 index 1c000f9ebf8b..000000000000 --- a/plugins/SbieSupport/SbieSupport.vcxproj.filters +++ /dev/null @@ -1,35 +0,0 @@ - - - - - {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 - - - \ No newline at end of file diff --git a/plugins/SbieSupport/main.c b/plugins/SbieSupport/main.c deleted file mode 100644 index ff84ca975af1..000000000000 --- a/plugins/SbieSupport/main.c +++ /dev/null @@ -1,556 +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 -#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 b338721498ca..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 fab2af6674ac..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 From 1c19c94c46846455f82cf6c6619284a15454564b Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 08:16:03 +1000 Subject: [PATCH 068/839] peview: Add experimental symbols tab and pdb support, Fix lib properties window resize and settings --- tools/peview/include/pdb.h | 468 +++++ tools/peview/include/peview.h | 18 + tools/peview/libprp.c | 69 +- tools/peview/main.c | 4 +- tools/peview/pdb.c | 2609 +++++++++++++++++++++++++++ tools/peview/peprp.c | 266 +-- tools/peview/peview.rc | 21 + tools/peview/peview.vcxproj | 2 + tools/peview/peview.vcxproj.filters | 8 +- tools/peview/resource.h | 3 +- tools/peview/settings.c | 2 + 11 files changed, 3316 insertions(+), 154 deletions(-) create mode 100644 tools/peview/include/pdb.h create mode 100644 tools/peview/pdb.c diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h new file mode 100644 index 000000000000..7b49cc6f668f --- /dev/null +++ b/tools/peview/include/pdb.h @@ -0,0 +1,468 @@ +/* + * 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 . + */ + +#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; +} 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 of all type information structures + union // TypeInfoStructures + { + BaseTypeInfo sBaseTypeInfo; // If Tag == SymTagBaseType + TypedefInfo sTypedefInfo; // If Tag == SymTagTypedef + PointerTypeInfo sPointerTypeInfo; // If Tag == SymTagPointerType + UdtClassInfo sUdtClassInfo; // If Tag == SymTagUDT and UdtKind is "true" + UdtUnionInfo sUdtUnionInfo; // If Tag == SymTagUDT and UdtKind is "false" + BaseClassInfo sBaseClassInfo; // If Tag == SymTagBaseClass + EnumInfo sEnumInfo; // If Tag == SymTagEnum + ArrayTypeInfo sArrayTypeInfo; // If Tag == SymTagArrayType + FunctionTypeInfo sFunctionTypeInfo; // If Tag == SymTagFunctionType + FunctionArgTypeInfo sFunctionArgTypeInfo; // If Tag == SymTagFunctionArgType + DataInfo sDataInfo; // If Tag == SymTagData + }; +} TypeInfo; + +typedef struct _PDB_SYMBOL_CONTEXT +{ + HWND ListviewHandle; + ULONG64 BaseAddress; + PPH_LIST UdtList; +} PDB_SYMBOL_CONTEXT, *PPDB_SYMBOL_CONTEXT; + +BOOLEAN SymInfoDump_DumpBasicType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseTypeInfo* Info); +BOOLEAN SymInfoDump_DumpPointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, PointerTypeInfo* Info); +BOOLEAN SymInfoDump_DumpTypedef(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypedefInfo* Info); +BOOLEAN SymInfoDump_DumpEnum(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, EnumInfo* Info); +BOOLEAN SymInfoDump_DumpArrayType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ArrayTypeInfo* Info); +BOOLEAN SymInfoDump_DumpUDT(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); +BOOLEAN SymInfoDump_DumpUDTClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtClassInfo* Info); +BOOLEAN SymInfoDump_DumpUDTUnion(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtUnionInfo* Info); +BOOLEAN SymInfoDump_DumpFunctionType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionTypeInfo* Info); +BOOLEAN SymInfoDump_DumpFunctionArgType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionArgTypeInfo* Info); +BOOLEAN SymInfoDump_DumpBaseClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseClassInfo* Info); +BOOLEAN SymInfoDump_DumpData(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, DataInfo* Info); +BOOLEAN SymInfoDump_DumpType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); +BOOLEAN SymInfoDump_DumpSymbolType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info, ULONG* TypeIndex); + +BOOLEAN SymInfoDump_CheckTag( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ ULONG Tag + ); + +BOOLEAN SymInfoDump_SymbolSize(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* Size); +BOOLEAN SymInfoDump_ArrayElementTypeIndex(_Inout_ PPDB_SYMBOL_CONTEXT Context, ULONG ArrayIndex, ULONG* ElementTypeIndex); +BOOLEAN SymInfoDump_ArrayDims(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* pDims, ULONG* Dims, _In_ ULONG MaxDims); +BOOLEAN SymInfoDump_UdtVariables(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pVars, ULONG* Vars, _In_ ULONG MaxVars); +BOOLEAN SymInfoDump_UdtFunctions(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pFuncs, ULONG* Funcs, _In_ ULONG MaxFuncs); +BOOLEAN SymInfoDump_UdtBaseClasses(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pBases, ULONG* Bases, _In_ ULONG MaxBases); +BOOLEAN SymInfoDump_UdtUnionMembers(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pMembers, ULONG* Members, _In_ ULONG MaxMembers); +BOOLEAN SymInfoDump_FunctionArguments(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pArgs, ULONG* Args, _In_ ULONG MaxArgs); +BOOLEAN SymInfoDump_Enumerators(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pEnums, ULONG* Enums, _In_ ULONG MaxEnums); +BOOLEAN SymInfoDump_TypeDefType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex); +BOOLEAN SymInfoDump_PointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex, ULONG* NumPointers); + +BOOLEAN SymInfoDump_GetTypeNameHelper( + _In_ ULONG Index, + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _Out_ PWSTR *VarName, + _Out_ PWSTR *TypeName + ); +BOOLEAN SymInfoDump_GetTypeName( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ PWSTR pVarName, + _In_ PWSTR pTypeName, + _In_ ULONG MaxChars + ); +PWSTR SymInfoDump_TagStr(enum SymTagEnum Tag); +PWSTR SymInfoDump_BaseTypeStr(BasicType Type, ULONG64 Length); +PWSTR SymInfoDump_CallConvStr(CV_call_e CallConv); +PWSTR SymInfoDump_DataKindFromSymbolInfo(_In_ PSYMBOL_INFOW rSymbol); +PWSTR SymInfoDump_DataKindStr(DataKind dataKind); +VOID SymInfoDump_SymbolLocationStr(PSYMBOL_INFOW rSymbol, PWSTR pBuffer); +PWSTR SymInfoDump_RegisterStr(CV_HREG_e RegCode); +PWSTR SymInfoDump_UdtKindStr(UdtKind KindType); +PWSTR SymInfoDump_LocationTypeStr(LocationType LocType); + + +VOID PrintDataInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ); + +VOID PrintFunctionInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ); + +VOID PrintDefaultInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ); + +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 9de0f19c7411..639222135ceb 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -48,4 +48,22 @@ VOID PeSaveSettings( VOID ); +// symbols + +INT_PTR CALLBACK PvpSymbolsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PeDumpFileSymbols( + _In_ HWND ListViewHandle, + _In_ PWSTR FileName + ); + +VOID PvPdbProperties( + VOID + ); + #endif diff --git a/tools/peview/libprp.c b/tools/peview/libprp.c index b7ee42316c31..6f8b89a1b1ef 100644 --- a/tools/peview/libprp.c +++ b/tools/peview/libprp.c @@ -38,9 +38,7 @@ VOID PvLibProperties( ) { NTSTATUS status; - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[1]; + PPV_PROPCONTEXT propContext; status = PhLoadMappedArchive(PvFileName->Buffer, NULL, TRUE, &PvMappedArchive); @@ -50,24 +48,22 @@ VOID PvLibProperties( 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); + 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); } @@ -79,6 +75,12 @@ INT_PTR CALLBACK PvpLibExportsDlgProc( _In_ LPARAM lParam ) { + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: @@ -88,8 +90,6 @@ INT_PTR CALLBACK PvpLibExportsDlgProc( 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"); @@ -100,6 +100,7 @@ INT_PTR CALLBACK PvpLibExportsDlgProc( 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; @@ -170,6 +171,28 @@ INT_PTR CALLBACK PvpLibExportsDlgProc( 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)); diff --git a/tools/peview/main.c b/tools/peview/main.c index 893e7ee0cb48..4c845c4e9193 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -89,7 +89,7 @@ INT WINAPI wWinMain( { 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"Supported files (*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb)", L"*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb" }, { L"All files (*.*)", L"*.*" } }; PVOID fileDialog; @@ -123,6 +123,8 @@ INT WINAPI wWinMain( if (PhEndsWithString2(PvFileName, L".lib", TRUE)) PvLibProperties(); + //else if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) + // PvPdbProperties(); else PvPeProperties(); diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c new file mode 100644 index 000000000000..a68ed42d0144 --- /dev/null +++ b/tools/peview/pdb.c @@ -0,0 +1,2609 @@ +/* + * 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 + +typedef BOOL (WINAPI *_SymInitialize)( + _In_ HANDLE hProcess, + _In_opt_ PCSTR 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 *_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 + ); + +_SymInitialize SymInitialize_I = NULL; +_SymCleanup SymCleanup_I = NULL; +_SymEnumSymbolsW SymEnumSymbolsW_I = NULL; +_SymSetSearchPathW SymSetSearchPathW_I = NULL; +_SymGetOptions SymGetOptions_I = NULL; +_SymSetOptions SymSetOptions_I = NULL; +_SymLoadModuleExW SymLoadModuleExW_I = NULL; +_SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; +_SymGetTypeInfo SymGetTypeInfo_I = NULL; + +BOOLEAN SymInfoDump_DumpBasicType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ BaseTypeInfo *Info + ) +{ + ULONG baseType = btNoType; + ULONG64 length = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagBaseType)) + return FALSE; + + // Basic type ("basicType" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_BASETYPE, &baseType)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + return FALSE; + + Info->BaseType = (BasicType)baseType; + Info->Length = length; + return TRUE; +} + +BOOLEAN SymInfoDump_DumpPointerType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ PointerTypeInfo* Info + ) +{ + ULONG TypeIndex = 0; + ULONG64 Length = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagPointerType)) + return FALSE; + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + return FALSE; + + // Reference ("reference" in DIA) + // This property is not available via SymGetTypeInfo_I?? + // TODO: Figure out how DIA determines if the pointer is a pointer or a reference. + + Info->TypeIndex = TypeIndex; + Info->Length = Length; + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpTypedef( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ TypedefInfo* Info + ) +{ + ULONG typeIndex = 0; + PWSTR symbolName = NULL; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagTypedef)) + return FALSE; + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->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] = 0; + LocalFree(symbolName); + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpEnum( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ EnumInfo* Info + ) +{ + ULONG TypeIndex = 0; + ULONG Nested = 0; + PWSTR symbolName = NULL; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagEnum)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = 0; + LocalFree(symbolName); + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Enumerators + if (!SymInfoDump_Enumerators(Context, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) + return FALSE; + + Info->TypeIndex = TypeIndex; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpArrayType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ArrayTypeInfo* Info + ) +{ + ULONG elementTypeIndex = 0; + ULONG64 length = 0; + ULONG indexTypeIndex = 0; + + // Check if it is really SymTagArrayType + if (!SymInfoDump_CheckTag(Context, Index, SymTagArrayType)) + return FALSE; + + // Element type index + if (!SymInfoDump_ArrayElementTypeIndex(Context, Index, &elementTypeIndex)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + return FALSE; + + // Type index of the array index element + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_ARRAYINDEXTYPEID, &indexTypeIndex)) + return FALSE; + + Info->ElementTypeIndex = elementTypeIndex; + Info->Length = length; + Info->IndexTypeIndex = indexTypeIndex; + + if (length > 0) + { + // Dimensions + if (!SymInfoDump_ArrayDims( + Context, + Index, + Info->Dimensions, + &Info->NumDimensions, + ARRAYSIZE(Info->Dimensions) + ) || (Info->NumDimensions == 0)) + { + return FALSE; + } + } + else + { + Info->NumDimensions = 0; // No dimensions + } + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpUDT( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ TypeInfo* Info + ) +{ + ULONG UDTKind = 0; + BOOLEAN result = FALSE; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + // Determine UDT kind (class/structure or union?) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + switch (UDTKind) + { + case UdtStruct: + Info->UdtKind = TRUE; + result = SymInfoDump_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + break; + case UdtClass: + Info->UdtKind = TRUE; + result = SymInfoDump_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + break; + case UdtUnion: + Info->UdtKind = FALSE; + result = SymInfoDump_DumpUDTUnion(Context, Index, &Info->sUdtUnionInfo); + break; + } + + return result; +} + +BOOLEAN SymInfoDump_DumpUDTClass( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ UdtClassInfo* Info + ) +{ + ULONG UDTKind = 0; + PWSTR symbolName = NULL; + ULONG64 Length = 0; + ULONG Nested = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + // Check if it is really a class or structure UDT ? + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + if ((UDTKind != UdtStruct) && (UDTKind != UdtClass)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = 0; + LocalFree(symbolName); + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Member variables + if (!SymInfoDump_UdtVariables(Context, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) + return FALSE; + + // Member functions + if (!SymInfoDump_UdtFunctions(Context, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) + return FALSE; + + // Base classes + if (!SymInfoDump_UdtBaseClasses(Context, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) + return FALSE; + + Info->UDTKind = (UdtKind)UDTKind; + Info->Length = Length; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpUDTUnion( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ UdtUnionInfo *Info + ) +{ + ULONG UDTKind = 0; + PWSTR symbolName = 0; + ULONG64 Length = 0; + ULONG Nested = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + // Check if it is really a union UDT ? + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + if (UDTKind != UdtUnion) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = 0; + LocalFree(symbolName); + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Union members + if (!SymInfoDump_UdtUnionMembers(Context, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) + return FALSE; + + Info->UDTKind = (UdtKind)UDTKind; + Info->Length = Length; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpFunctionType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ FunctionTypeInfo *Info + ) +{ + ULONG RetTypeIndex = 0; + ULONG NumArgs = 0; + ULONG CallConv = 0; + ULONG ClassIndex = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionType)) + return FALSE; + + // Index of the return type symbol ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->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(), Context->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(), Context->BaseAddress, Index, TI_GET_CALLING_CONVENTION, &CallConv)) + return FALSE; + + // Parent class type index ("classParent" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->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(), Context->BaseAddress, Index, TI_GET_THISADJUST, &ThisAdjust)) + return FALSE; + + Info->ThisAdjust = ThisAdjust; + } + + // Test for GetChildren() + /* + ULONG cMaxChildren = 4; + ULONG Children[cMaxChildren]; + ULONG NumChildren = 0; + + if( !GetChildren( m_hProcess, BaseAddress, Index, Children, NumChildren, cMaxChildren ) ) + { + ULONG ErrCode = 0; + _ASSERTE( !_T("GetChildren() failed.") ); + } + else + { + ULONG ErrCode = 0; + } + */ + + // Dump function arguments + if (!SymInfoDump_FunctionArguments(Context, 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 SymInfoDump_DumpFunctionArgType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ FunctionArgTypeInfo *Info + ) +{ + ULONG typeIndex = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionArgType)) + return FALSE; + + // Index of the argument type ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + return FALSE; + + Info->TypeIndex = typeIndex; + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpBaseClass( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ BaseClassInfo *Info + ) +{ + ULONG typeIndex = 0; + ULONG virtualBase = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagBaseClass)) + return FALSE; + + // Base class UDT + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + Context->BaseAddress, + Index, + TI_GET_TYPEID, + &typeIndex + )) + { + return FALSE; + } + + // Is this base class virtual ? + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + Context->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(), + Context->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(), + Context->BaseAddress, + Index, + TI_GET_OFFSET, + &offset + )) + { + return FALSE; + } + + Info->Offset = offset; + Info->VirtualBasePointerOffset = 0; + } + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpData( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ DataInfo *Info + ) +{ + PWSTR symbolName = NULL; + ULONG TypeIndex = 0; + ULONG dataKind = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagData)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = 0; + LocalFree(symbolName); + + // Index of type symbol ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Data kind ("dataKind" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_DATAKIND, &dataKind)) + return FALSE; + + Info->TypeIndex = TypeIndex; + Info->dataKind = (DataKind)dataKind; + + // Location, depending on the data kind + switch (dataKind) + { + case DataIsGlobal: + case DataIsStaticLocal: + case DataIsFileStatic: + case DataIsStaticMember: + { + // 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) + + ULONG64 address = 0; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_ADDRESS, &address)) + return FALSE; + + Info->Address = address; + Info->Offset = 0; + } + break; + + case DataIsLocal: + case DataIsParam: + case DataIsObjectPtr: + case DataIsMember: + { + // Use Offset; Address is not defined + ULONG offset = 0; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_OFFSET, &offset)) + return FALSE; + + Info->Offset = offset; + Info->Address = 0; + } + break; + + default: + // Unknown location + Info->Address = 0; + Info->Offset = 0; + break; + } + + return TRUE; +} + +BOOLEAN SymInfoDump_DumpType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ TypeInfo *Info + ) +{ + ULONG tag = SymTagNull; + BOOLEAN result = FALSE; + + // Get the symbol's tag + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + Context->BaseAddress, + Index, + TI_GET_SYMTAG, + &tag + )) + { + return FALSE; + } + + Info->Tag = (enum SymTagEnum)tag; + + // Dump information about the symbol (depending on the tag). + switch (tag) + { + case SymTagBaseType: + result = SymInfoDump_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); + break; + case SymTagPointerType: + result = SymInfoDump_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); + break; + case SymTagTypedef: + result = SymInfoDump_DumpTypedef(Context, Index, &Info->sTypedefInfo); + break; + case SymTagEnum: + result = SymInfoDump_DumpEnum(Context, Index, &Info->sEnumInfo); + break; + case SymTagArrayType: + result = SymInfoDump_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); + break; + case SymTagUDT: + result = SymInfoDump_DumpUDT(Context, Index, Info); + break; + case SymTagFunctionType: + result = SymInfoDump_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); + break; + case SymTagFunctionArgType: + result = SymInfoDump_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); + break; + case SymTagBaseClass: + result = SymInfoDump_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); + break; + case SymTagData: + result = SymInfoDump_DumpData(Context, Index, &Info->sDataInfo); + break; + } + + return result; +} + +BOOLEAN SymInfoDump_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 SymInfoDump_DumpType(Context, *TypeIndex, Info); +} + +BOOLEAN SymInfoDump_CheckTag( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ ULONG Tag + ) +{ + ULONG symTag = SymTagNull; + + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + Context->BaseAddress, + Index, + TI_GET_SYMTAG, + &symTag + )) + { + return FALSE; + } + + return symTag == Tag; +} + +BOOLEAN SymInfoDump_SymbolSize( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG64* Size + ) +{ + ULONG index; + ULONG64 length = 0; + + // Does the symbol support "length" property ? + if (SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + { + *Size = length; + return TRUE; + } + else + { + // No, it does not - it can be SymTagTypedef + if (!SymInfoDump_CheckTag(Context, 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(), Context->BaseAddress, index, TI_GET_TYPEID, &tempIndex)) + return FALSE; + + index = tempIndex; + + } while (SymInfoDump_CheckTag(Context, index, SymTagTypedef)); + + // And get the length + if (SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_LENGTH, &length)) + { + *Size = length; + return TRUE; + } + } + } + + return FALSE; +} + +BOOLEAN SymInfoDump_ArrayElementTypeIndex( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG ArrayIndex, + _Inout_ ULONG* ElementTypeIndex + ) +{ + ULONG index; + ULONG elementIndex = 0; + + if (!SymInfoDump_CheckTag(Context, ArrayIndex, SymTagArrayType)) + return FALSE; + + // Get the array element type + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, ArrayIndex, TI_GET_TYPEID, &elementIndex)) + return FALSE; + + // If the array element type is SymTagArrayType, skip to its type + index = elementIndex; + + while (SymInfoDump_CheckTag(Context, elementIndex, SymTagArrayType)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->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 SymInfoDump_ArrayDims( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG64* pDims, + _Inout_ ULONG* Dims, + _In_ ULONG MaxDims + ) +{ + ULONG index; + ULONG dimCount; + + if (!SymInfoDump_CheckTag(Context, 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(), Context->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(), Context->BaseAddress, index, TI_GET_TYPEID, &typeIndex)) + { + // No type - we are done + break; + } + + // Size of its type + if (!SymInfoDump_SymbolSize(Context, typeIndex, &typeSize) || (typeSize == 0)) + return FALSE; + + // Size of the dimension + pDims[i] = length / typeSize; + dimCount++; + + // Test only +/* + ULONG ElemCount = 0; + if( !SymGetTypeInfo_I( m_hProcess, BaseAddress, CurIndex, TI_GET_COUNT, &ElemCount ) ) + { + // and continue ... + } + else if( ElemCount != pDims[i] ) + { + _ASSERTE( !_T("TI_GET_COUNT does not match.") ); + } + else + { + //_ASSERTE( !_T("TI_GET_COUNT works!") ); + } +*/ + // If the type symbol is not SymTagArrayType, we are done + if (!SymInfoDump_CheckTag(Context, typeIndex, SymTagArrayType)) + break; + + index = typeIndex; + } + + if (dimCount == 0) + return FALSE; + + *Dims = dimCount; + + return TRUE; +} + +BOOLEAN SymInfoDump_UdtVariables( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* pVars, + _Inout_ ULONG* Vars, + _In_ ULONG MaxVars + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + if (MaxVars <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + *Vars = 0; + return TRUE; // No children -> no member variables + } + + // Get the children + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + params->Count = childrenLength; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Vars = 0; + + // Enumerate children, looking for base classes, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + { + pVars[*Vars] = params->ChildId[i]; + + (*Vars)++; + + if (*Vars == MaxVars) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_UdtFunctions( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* pFuncs, + _Inout_ ULONG* Funcs, + _In_ ULONG MaxFuncs + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + if (MaxFuncs <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + *Funcs = 0; // No children -> no member variables + return TRUE; + } + + // Get the children + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + + params->Count = childrenLength; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Funcs = 0; + + // Enumerate children, looking for base classes, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagFunction)) + { + pFuncs[*Funcs] = params->ChildId[i]; + + (*Funcs)++; + + if (*Funcs == MaxFuncs) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_UdtBaseClasses( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* pBases, + _Inout_ ULONG* Bases, + _In_ ULONG MaxBases + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + if (MaxBases <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + *Bases = 0; // No children -> no member variables + return TRUE; + } + + // Get the children + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + + params->Count = childrenLength; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Bases = 0; + + // Enumerate children, looking for base classes, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagBaseClass)) + { + pBases[*Bases] = params->ChildId[i]; + + (*Bases)++; + + if (*Bases == MaxBases) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_UdtUnionMembers( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* pMembers, + _Inout_ ULONG* Members, + _In_ ULONG MaxMembers + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + return FALSE; + + if (MaxMembers <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + *Members = 0; + return TRUE; // No children -> no members + } + + // Get the children + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + + params->Count = childrenLength; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Members = 0; + + // Enumerate children, looking for enumerators, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + { + pMembers[*Members] = params->ChildId[i]; + + (*Members)++; + + if (*Members == MaxMembers) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_FunctionArguments( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* pArgs, + _Inout_ ULONG* Args, + _In_ ULONG MaxArgs + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionType)) + return FALSE; + + if (MaxArgs <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + *Args = 0; + return TRUE; // No children -> no member variables + } + + // Get the children + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + + params->Count = childrenLength; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Args = 0; + + // Enumerate children, looking for enumerators, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagFunctionArgType)) + { + pArgs[*Args] = params->ChildId[i]; + + (*Args)++; + + if (*Args == MaxArgs) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_Enumerators( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG *pEnums, + _Inout_ ULONG *Enums, + _In_ ULONG MaxEnums + ) +{ + ULONG childrenLength = 0; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagEnum)) + return FALSE; + + if (MaxEnums <= 0) + return FALSE; + + // Get the number of children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) + return FALSE; + + if (childrenLength == 0) + { + // No children -> no enumerators + *Enums = 0; + return TRUE; + } + + ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); + TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); + memset(params, 0, FindChildrenSize); + params->Count = childrenLength; + + // Get the children + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) + return FALSE; + + *Enums = 0; + + // Enumerate children, looking for enumerators, and copy their indexes. + for (ULONG i = 0; i < childrenLength; i++) + { + if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + { + pEnums[*Enums] = params->ChildId[i]; + + (*Enums)++; + + if (*Enums == MaxEnums) + break; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_TypeDefType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex + ) +{ + ULONG index; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagTypedef)) + return FALSE; + + // Skip to the type behind the type definition + index = Index; + + while (SymInfoDump_CheckTag(Context, index, SymTagTypedef)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + return FALSE; + + index = *UndTypeIndex; + } + + return TRUE; +} + +BOOLEAN SymInfoDump_PointerType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex, + _Inout_ ULONG *NumPointers + ) +{ + ULONG index; + + if (!SymInfoDump_CheckTag(Context, Index, SymTagPointerType)) + return FALSE; + + // Skip to the type pointer points to + *NumPointers = 0; + index = Index; + + while (SymInfoDump_CheckTag(Context, index, SymTagPointerType)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + return FALSE; + + (*NumPointers)++; + + index = *UndTypeIndex; + } + + return TRUE; +} + +BOOLEAN SymInfoDump_GetTypeNameHelper( + _In_ ULONG Index, + _Inout_ PPDB_SYMBOL_CONTEXT Obj, + _Out_ PWSTR *VarName, + _Out_ PWSTR *TypeName + ) +{ + TypeInfo Info; + + // Get the type information + if (!SymInfoDump_DumpType(Obj, Index, &Info)) + { + return FALSE; + } + else + { + // Is it a pointer ? + ULONG numPointers = 0; + + if (Info.Tag == SymTagPointerType) + { + // Yes, get the number of * to show + ULONG typeIndex = 0; + + if (!SymInfoDump_PointerType(Obj, Index, &typeIndex, &numPointers)) + return FALSE; + + // Get more information about the type the pointer points to + if (!SymInfoDump_DumpType(Obj, 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: + *TypeName = SymInfoDump_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length); + break; + + case SymTagTypedef: + *TypeName = _wcsdup(Info.sTypedefInfo.Name); + break; + + case SymTagUDT: + { + if (Info.UdtKind) + { + // A class/structure + *TypeName = _wcsdup(Info.sUdtClassInfo.Name); + + // Add the UDT and its base classes to the collection of UDT indexes + PhAddItemList(Obj->UdtList, UlongToPtr(Index)); + } + else + { + // A union + *TypeName = _wcsdup(Info.sUdtUnionInfo.Name); + } + } + break; + + case SymTagEnum: + *TypeName = _wcsdup(Info.sEnumInfo.Name); + break; + + case SymTagFunctionType: + { + if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.StaticFunction) + { + //*TypeName = L"static "; + } + + // Print return value + if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) + return FALSE; + + // Print calling convention + //TypeName += " "; + //TypeName += SymInfoDump_CallConvStr(Info.Info.sFunctionTypeInfo.CallConv); + //TypeName += " "; + + // If member function, save the class type index + if (Info.sFunctionTypeInfo.MemberFunction) + { + PhAddItemList(Obj->UdtList, UlongToPtr(Info.sFunctionTypeInfo.ClassIndex)); + + /* + // It is not needed to print the class name here, because + // it is contained in the function name + if( !SymInfoDump_GetTypeNameHelper( Info.Info.sFunctionTypeInfo.ClassIndex, Obj, VarName, TypeName ) ) + return false; + + TypeName += "::"); + */ + } + + // Print that it is a function + //TypeName += VarName; + // Print parameters + //TypeName += " ("; + for (ULONG i = 0; i < Info.sFunctionTypeInfo.NumArgs; i++) + { + if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionTypeInfo.Args[i], Obj, VarName, TypeName)) + return FALSE; + + //if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) + //TypeName += ", "; + } + //TypeName += ")"; + + // Print "this" adjustment value + if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.ThisAdjust != 0) + { + WCHAR buffer[MAX_PATH + 1] = L""; + + //TypeName += "ThisAdjust: "; + _snwprintf(buffer, ARRAYSIZE(buffer), L"this+%u", Info.sFunctionTypeInfo.ThisAdjust); + //TypeName += buffer; + + *TypeName = _wcsdup(buffer); + } + } + break; + + case SymTagFunctionArgType: + { + if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) + return FALSE; + } + break; + + case SymTagArrayType: + { + // Print element type name + if (!SymInfoDump_GetTypeNameHelper(Info.sArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) + return FALSE; + + //TypeName += " "; + //TypeName += VarName; + + // Print dimensions + for (ULONG i = 0; i < Info.sArrayTypeInfo.NumDimensions; i++) + { + //WCHAR buffer[MAX_PATH + 1] = L""; + //_snwprintf(buffer, cTempBufSize, "[%u]"), Info.Info.sArrayTypeInfo.Dimensions[i]); + //TypeName += buffer; + } + } + break; + default: + { + WCHAR buffer[MAX_PATH + 1] = L""; + + _snwprintf(buffer, ARRAYSIZE(buffer), L"Unknown(%lu)", Info.Tag); + + *TypeName = _wcsdup(buffer); + } + break; + } + + // If it is a pointer, display * characters + if (numPointers != 0) + { + //for (ULONG i = 0; i < NumPointers; i++) + // TypeName += "*"; + } + } + + return TRUE; +} + +BOOLEAN SymInfoDump_GetTypeName( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ PWSTR pVarName, + _In_ PWSTR pTypeName, + _In_ ULONG MaxChars + ) +{ + PWSTR VarName = NULL; + PWSTR TypeName = NULL; + + if (!pTypeName) + return FALSE; + + if (pVarName) + VarName = pVarName; + + // Obtain the type name + if (!SymInfoDump_GetTypeNameHelper(Index, Context, &VarName, &TypeName)) + return FALSE; + + if (!wcslen(TypeName)) + return FALSE; + + // Return the type name to the caller + _snwprintf(pTypeName, MaxChars, L"%s", TypeName); + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// Data-to-string conversion functions +PWSTR SymInfoDump_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 SymInfoDump_BaseTypeStr( + _In_ BasicType Type, + _In_ ULONG64 Length + ) +{ + switch (Type) + { + case btNoType: + return L"NoType"; + 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 SymInfoDump_CallConvStr( + _In_ CV_call_e CallConv + ) +{ + switch (CallConv) + { + case CV_CALL_NEAR_C: + return L"NEAR_C"; + 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"NEAR_FAST"; + case CV_CALL_FAR_FAST: + return L"FAR_FAST"; + case CV_CALL_SKIPPED: + return L"SKIPPED"; + case CV_CALL_NEAR_STD: + return L"NEAR_STD"; + case CV_CALL_FAR_STD: + return L"FAR_STD"; + case CV_CALL_NEAR_SYS: + return L"NEAR_SYS"; + 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 SymInfoDump_DataKindFromSymbolInfo( + _In_ PSYMBOL_INFOW SymbolInfo + ) +{ + ULONG dataKindType = 0; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) + return L"UNKNOWN"; + + return SymInfoDump_DataKindStr(dataKindType); +} + +PWSTR SymInfoDump_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 SymInfoDump_SymbolLocationStr( + _In_ PSYMBOL_INFOW SymbolInfo, + _In_ PWSTR Buffer + ) +{ + if (SymbolInfo->Flags & SYMFLAG_REGISTER) + { + PWSTR regString = SymInfoDump_RegisterStr((CV_HREG_e)SymbolInfo->Register); + + if (regString) + wcscpy(Buffer, regString); + else + _swprintf(Buffer, L"Reg%u", SymbolInfo->Register); + + return; + } + else if (SymbolInfo->Flags & SYMFLAG_REGREL) + { + WCHAR szReg[32]; + PWSTR regString = SymInfoDump_RegisterStr((CV_HREG_e)SymbolInfo->Register); + + if (regString) + wcscpy(szReg, regString); + else + _swprintf(szReg, L"Reg%u", SymbolInfo->Register); + + _swprintf(Buffer, L"%s%+lu", szReg, (long)SymbolInfo->Address); + + return; + } + else if (SymbolInfo->Flags & SYMFLAG_FRAMEREL) + { + wcscpy(Buffer, L"N/A"); + return; + } + else + { + //_swprintf(Buffer, L"%16I64x", SymbolInfo->Address); + PhPrintPointer(Buffer, (PVOID)SymbolInfo->Address); + } +} + +PWSTR SymInfoDump_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 SymInfoDump_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 SymInfoDump_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) // Try to enumerate other symbols + return TRUE; + + switch (SymbolInfo->Tag) + { + case SymTagFunction: + PrintFunctionInfo(Context, SymbolInfo); + break; + case SymTagData: + PrintDataInfo(Context, SymbolInfo); + break; + default: + PrintDefaultInfo(Context, SymbolInfo); + break; + } + + return TRUE; +} + +VOID PrintDataInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ) +{ + INT lvItemIndex; + PWSTR symDataKind; + WCHAR pointer[PH_PTR_STR_LEN_1] = L""; + + if (SymbolInfo->Tag != SymTagData) + return; + + // Type + symDataKind = SymInfoDump_DataKindFromSymbolInfo(SymbolInfo); + lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, symDataKind, NULL); + + // Name + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, SymbolInfo->Name); + + // Address + SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, pointer); + + // Size + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + + // Data + //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex +} + +VOID PrintFunctionInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ) +{ + INT lvItemIndex; + WCHAR pointer[PH_PTR_STR_LEN_1] = L""; + + if (SymbolInfo->Tag != SymTagFunction) + return; + + // Type + lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"FUNCTION", NULL); + + // Address + SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + + // Name + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); + + // Size + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + + // Data + //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + + // Enumerate function parameters and local variables... + //IMAGEHLP_STACK_FRAME sf; + //sf.InstructionOffset = SymbolInfo->Address; + //SymSetContext(NtCurrentProcess(), &sf, 0); + //SymEnumSymbolsW(NtCurrentProcess(), 0, NULL, EnumCallbackProc, pTypeInfo); +} + +VOID PrintDefaultInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PSYMBOL_INFOW SymbolInfo + ) +{ + INT lvItemIndex; + WCHAR pointer[PH_PTR_STR_LEN_1] = L""; + + if (SymbolInfo->Tag != SymTagPublicSymbol) + return; + + // Type + lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"SYMBOL", NULL); + + // Address + SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + + // Name + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); + + // Size + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + + // Data + //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex +} + +VOID PrintClassInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ UdtClassInfo* Info + ) +{ + // UDT kind + //OutputDebugString(PhaFormatString(L"%s", SymInfoDump_UdtKindStr(Info->UDTKind))->Buffer); + // Name + //OutputDebugString(PhaFormatString(L" %s \n", Info->Name)->Buffer); + // Size + //OutputDebugString(PhaFormatString(L"Size: %I64u", Info->Length)->Buffer); + + // Nested + //if (Info.Nested) + //OutputDebugString(L" Nested"); + // Number of member variables + //OutputDebugString(PhaFormatString(L" Variables: %d", Info->NumVariables)->Buffer); + // Number of member functions + //OutputDebugString(PhaFormatString(L" Functions: %d", Info->NumFunctions)->Buffer); + // Number of base classes + //OutputDebugString(PhaFormatString(L" Base classes: %d", Info->NumBaseClasses)->Buffer); + // Extended information about member variables + //OutputDebugString(L"\n"); + + for (ULONG i = 0; i < Info->NumVariables; i++) + { + TypeInfo VarInfo; + + if (!SymInfoDump_DumpType(Context, Info->Variables[i], &VarInfo)) + { + //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + // Continue with the next variable + } + else if (VarInfo.Tag != SymTagData) + { + //_ASSERTE(!_T("Unexpected symbol tag.")); + // Continue with the next variable + } + else + { + // Display information about the variable + //_tprintf(_T("%s"), Context->DataKindStr(VarInfo.Info.sDataInfo.dataKind)); + + // Name + //wprintf(L" %s \n", VarInfo.Info.sDataInfo.Name); + // Type name + //_tprintf(_T(" Type: ")); + + //#define cMaxTypeNameLen 1024 + //TCHAR szTypeName[cMaxTypeNameLen + 1] = _T(""); + + //if (SymInfoDump_GetTypeName(VarInfo.Info.sDataInfo.TypeIndex, W2T(VarInfo.Info.sDataInfo.Name), szTypeName, cMaxTypeNameLen)) + //_tprintf(szTypeName); + //else + //_tprintf(_T("n/a")); + //_tprintf(_T("\n")); + + // Location + switch (VarInfo.sDataInfo.dataKind) + { + case DataIsGlobal: + case DataIsStaticLocal: + case DataIsFileStatic: + case DataIsStaticMember: + { + // Use Address + //_tprintf(_T(" Address: %16I64x"), VarInfo.Info.sDataInfo.Address); + } + break; + case DataIsLocal: + case DataIsParam: + case DataIsObjectPtr: + case DataIsMember: + { + // Use Offset + //_tprintf(_T(" Offset: %8d"), (long)VarInfo.Info.sDataInfo.Offset); + } + break; + default: + break; // Add support for constants + } + + // Indices + //_tprintf(_T(" Index: %8u TypeIndex: %8u"), Index, VarInfo.Info.sDataInfo.TypeIndex); + //_tprintf(_T("\n")); + } + } + + // Extended information about member functions + // Implement + // Extended information about base classes + // Implement + + for (ULONG i = 0; i < Info->NumBaseClasses; i++) + { + TypeInfo baseInfo; + + if (!SymInfoDump_DumpType(Context, Info->BaseClasses[i], &baseInfo)) + { + //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + // Continue with the next base class + } + else if (baseInfo.Tag != SymTagBaseClass) + { + //_ASSERTE(!_T("Unexpected symbol tag.")); + // Continue with the next base class + } + else + { + // Obtain the name of the base class + TypeInfo BaseUdtInfo; + + if (!SymInfoDump_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &BaseUdtInfo)) + { + //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + // Continue with the next base class + } + else if (BaseUdtInfo.Tag != SymTagUDT) + { + //_ASSERTE(!_T("Unexpected symbol tag.")); + // Continue with the next base class + } + else + { + // Print the name of the base class + if (baseInfo.sBaseClassInfo.Virtual) + { + //wprintf(L"VIRTUAL_BASE_CLASS %s \n", BaseUdtInfo.Info.sUdtClassInfo.Name); + } + else + { + //wprintf(L"BASE_CLASS %s \n", BaseUdtInfo.Info.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 (SymInfoDump_DumpType(Context, index, &info)) + { + if (info.Tag == SymTagUDT) + { + if (info.UdtKind) + { + // Print information about the class + PrintClassInfo(Context, index, &info.sUdtClassInfo); + + // Print information about its base classes + for (ULONG i = 0; i < info.sUdtClassInfo.NumBaseClasses; i++) + { + TypeInfo baseInfo; + + if (!SymInfoDump_DumpType(Context, info.sUdtClassInfo.BaseClasses[i], &baseInfo)) + { + //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + // Continue with the next base class + } + else if (baseInfo.Tag != SymTagBaseClass) + { + //_ASSERTE(!_T("Unexpected symbol tag.")); + // Continue with the next base class + } + else + { + // Obtain information about the base class + TypeInfo baseUdtInfo; + + if (!SymInfoDump_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo)) + { + //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + // Continue with the next base class + } + else if (baseUdtInfo.Tag != SymTagUDT) + { + //_ASSERTE(!_T("Unexpected symbol tag.")); + // Continue with the next base class + } + else + { + // Print information about the base class + PrintClassInfo(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo.sUdtClassInfo); + } + } + } + } + else + { + // UDT kind + //printf("%s", Context->UdtKindStr(Info.Info.sUdtClassInfo.UDTKind)); + // + // Name + //printf("%s", Info.Info.sUdtClassInfo.Name); + // + // Size + //printf("Size: %I64u", Info.Info.sUdtClassInfo.Length); + // + // Nested + //if (Info.Info.sUdtClassInfo.Nested) + // printf("Nested"); + // + // Number of members + //printf(" Members: %d", Info.Info.sUdtClassInfo.NumVariables); + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +BOOLEAN GetFileParamsSize(_In_ PWSTR pFileName, _Out_ ULONG* FileLength) +{ + HANDLE hFile = CreateFile( + pFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if (hFile == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + *FileLength = GetFileSize(hFile, NULL); + NtClose(hFile); + + return (*FileLength != INVALID_FILE_SIZE); +} + +BOOLEAN GetFileParams(_In_ PWSTR pFileName, _Out_ ULONG64 *BaseAddress, _Out_ ULONG* FileLength) +{ + TCHAR szFileExt[_MAX_EXT] = { 0 }; + + _wsplitpath(pFileName, NULL, NULL, NULL, szFileExt); + + // Is it .PDB file ? + if (_wcsicmp(szFileExt, L".PDB") == 0) + { + // Yes, it is a .PDB file + *BaseAddress = 0x10000000; + + // Determine its size, and use a dummy base address. + // it can be any non-zero value, but if we load symbols + // from more than one file, memory regions specified + // for different files should not overlap + // (region is "base address + file size") + + if (!GetFileParamsSize(pFileName, FileLength)) + { + return FALSE; + } + } + else + { + *BaseAddress = 0; + *FileLength = 0; + } + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +VOID PdbLoadDbgHelpFromPath( + _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 ShowSymbolInfo( + _In_ ULONG64 BaseAddress + ) +{ + IMAGEHLP_MODULE64 info; + + memset(&info, 0, sizeof(IMAGEHLP_MODULE64)); + info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + + if (!SymGetModuleInfoW64_I(NtCurrentProcess(), BaseAddress, &info)) + return; + + switch (info.SymType) + { + case SymNone: + //OutputDebugString(L"No symbols available for the module.\r\n"); + break; + case SymExport: + //OutputDebugString(L"Loaded symbols: Exports\r\n"); + break; + case SymCoff: + //OutputDebugString(L"Loaded symbols: COFF\r\n"); + break; + case SymCv: + //OutputDebugString(L"Loaded symbols: CodeView\r\n"); + break; + case SymSym: + //OutputDebugString(L"Loaded symbols: SYM\r\n"); + break; + case SymVirtual: + //OutputDebugString(L"Loaded symbols: Virtual\r\n"); + break; + case SymPdb: + //OutputDebugString(L"Loaded symbols: PDB\r\n"); + break; + case SymDia: + //OutputDebugString(L"Loaded symbols: DIA\r\n"); + break; + case SymDeferred: + //OutputDebugString(L"Loaded symbols: Deferred\r\n"); // not actually loaded + break; + default: + //OutputDebugString(L"Loaded symbols: Unknown format.\r\n"); + break; + } + + // Image name + if (wcslen(info.ImageName) > 0) + { + //OutputDebugString(PhaFormatString(L"Image name: %s\n", info.ImageName)->Buffer); + } + + // Loaded image name + if (wcslen(info.LoadedImageName) > 0) + { + //OutputDebugString(PhaFormatString(L"Loaded image name: %s\n", info.LoadedImageName)->Buffer); + } + + // Loaded PDB name + if (wcslen(info.LoadedPdbName) > 0) + { + //OutputDebugString(PhaFormatString(L"PDB file name: %s\n", info.LoadedPdbName)->Buffer); + } + + // Is debug information unmatched ? + // (It can only happen if the debug information is contained in a separate file (.DBG or .PDB) + if (info.PdbUnmatched || info.DbgUnmatched) + { + OutputDebugString(L"Warning: Unmatched symbols. \n"); + } + + // Load address: BaseAddress + //OutputDebugString(PhaFormatString(L"Line numbers: %s\n", info.LineNumbers ? L"Available" : L"Not available")->Buffer); + //OutputDebugString(PhaFormatString(L"Global symbols: %s\n", info.GlobalSymbols ? L"Available" : L"Not available")->Buffer); + //OutputDebugString(PhaFormatString(L"Type information: %s\n", info.TypeInfo ? L"Available" : L"Not available")->Buffer); + //OutputDebugString(PhaFormatString(L"Source indexing: %s\n", info.SourceIndexed ? L"Yes" : L"No")->Buffer); + //OutputDebugString(PhaFormatString(L"Public symbols: %s\n", info.Publics ? L"Available" : L"Not available")->Buffer); +} + + +VOID PeDumpFileSymbols( + _In_ HWND ListViewHandle, + _In_ PWSTR FileName + ) +{ + HMODULE dbghelpHandle; + HMODULE symsrvHandle; + ULONG64 symbolBaseAddress = 0; + ULONG64 baseAddress = 0; + ULONG fileLength = 0; + + PdbLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); + + if (!(dbghelpHandle = GetModuleHandle(L"dbghelp.dll"))) + return; + + symsrvHandle = GetModuleHandle(L"symsrv.dll"); + SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); + SymCleanup_I = PhGetProcedureAddress(dbghelpHandle, "SymCleanup", 0); + SymEnumSymbolsW_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); + SymSetSearchPathW_I = PhGetProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0); + SymGetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymGetOptions", 0); + SymSetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymSetOptions", 0); + SymLoadModuleExW_I = PhGetProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); + SymGetModuleInfoW64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetModuleInfoW64", 0); + SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); + + SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME); // SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED + + if (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) + return; + + if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*http://msdl.microsoft.com/download/symbols")) + return; + + if (GetFileParams(FileName, &baseAddress, &fileLength)) + { + if ((symbolBaseAddress = SymLoadModuleExW_I( + NtCurrentProcess(), + NULL, + FileName, + NULL, + baseAddress, + fileLength, + NULL, + 0 + ))) + { + PDB_SYMBOL_CONTEXT context; + + context.ListviewHandle = ListViewHandle; + context.BaseAddress = symbolBaseAddress; + context.UdtList = PhCreateList(0x100); + + ShowSymbolInfo(symbolBaseAddress); + + SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, &context); + + // Print information about used defined types + //PrintUserDefinedTypes(&context); + } + } + + SymCleanup_I(NtCurrentProcess()); +} + +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; + + // Symbols page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESYMBOLS), + PvpSymbolsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } +} + +INT_PTR CALLBACK PvpSymbolsDlgProc( + _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, 40, L"Type"); + 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, 40, L"Size"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"PdbListViewColumns", lvHandle); + + PeDumpFileSymbols(lvHandle, PvFileName->Buffer); + + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"PdbListViewColumns", 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/peprp.c b/tools/peview/peprp.c index 38947aaf0420..6d3b832abaa7 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -195,6 +195,16 @@ VOID PvPeProperties( PvAddPropPage(propContext, newPage); } + // PDB page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESYMBOLS), + PvpSymbolsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + PhModalPropertySheet(&propContext->PropSheetHeader); PhDereferenceObject(propContext); @@ -1139,6 +1149,134 @@ BOOLEAN PvpLoadDbgHelp( return TRUE; } +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; +} + INT_PTR CALLBACK PvpPeCgfDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -1300,131 +1438,3 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( 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; -} diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index d8c1288b8a65..6151830379f2 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -108,6 +108,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 273 END + + IDD_PESYMBOLS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 273 + END END #endif // APSTUDIO_INVOKED @@ -202,6 +210,14 @@ BEGIN CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 END +IDD_PESYMBOLS DIALOGEX 0, 0, 300, 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_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -223,6 +239,11 @@ BEGIN 0 END +IDD_PESYMBOLS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index e37234a5e523..65d0ed06345b 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -194,11 +194,13 @@ + + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index 4abd9aaa1ebb..87d782e3bcb4 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -33,6 +33,9 @@ Source Files + + Source Files + @@ -44,6 +47,9 @@ Header Files + + Header Files + diff --git a/tools/peview/resource.h b/tools/peview/resource.h index aa0350eaa2d3..6c6e0f0a2ed0 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -9,6 +9,7 @@ #define IDD_PECLR 106 #define IDD_PELOADCONFIG 107 #define IDD_PECFG 108 +#define IDD_PESYMBOLS 109 #define IDC_TARGETMACHINE 1003 #define IDC_CHECKSUM 1004 #define IDC_SUBSYSTEM 1005 @@ -32,7 +33,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_RESOURCE_VALUE 111 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1017 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/tools/peview/settings.c b/tools/peview/settings.c index 4470b16fea46..1d9da291d023 100644 --- a/tools/peview/settings.c +++ b/tools/peview/settings.c @@ -39,6 +39,8 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"ImageExportsListViewColumns", L""); PhpAddStringSetting(L"ImageImportsListViewColumns", L""); PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); + PhpAddStringSetting(L"LibListViewColumns", L""); + PhpAddStringSetting(L"PdbListViewColumns", L""); } VOID PhUpdateCachedSettings( From d6bf0164847fc982c7e07e500c70141a71445c29 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 08:26:51 +1000 Subject: [PATCH 069/839] Add missing line from previous commit --- tools/peview/pdb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index a68ed42d0144..a9e4f4d5c58c 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -1955,12 +1955,12 @@ VOID PrintDataInfo( symDataKind = SymInfoDump_DataKindFromSymbolInfo(SymbolInfo); lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, symDataKind, NULL); - // Name - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, SymbolInfo->Name); - // Address SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, pointer); + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + + // Name + PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); // Size PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); From ae1599b09d2b552cb3c87a462f6a3877fd126c64 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 May 2017 20:49:43 +1000 Subject: [PATCH 070/839] BuildTools: Fix building multi-architecture appx packages, Fix setup installer object paths, Fix setup text clipping --- .gitignore | 1 + tools/CustomBuildTool/CustomBuildTool.csproj | 1 + .../Properties/Resources.Designer.cs | 18 +- .../CustomBuildTool/Properties/Resources.resx | 59 ++--- tools/CustomBuildTool/Source Files/Build.cs | 239 ++++++++++++++---- .../Source Files/NativeMethods.cs | 15 ++ tools/CustomBuildTool/Source Files/Program.cs | 19 +- .../bin/Release/CustomBuildTool.exe | Bin 157696 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 71168 -> 75264 bytes .../CustomSetupTool/CustomSetupTool.vcxproj | 2 +- .../CustomSetupTool.vcxproj.filters | 2 +- .../CustomSetupTool/CustomSetupTool/extract.c | 17 +- .../CustomSetupTool/resource.rc | 2 +- 13 files changed, 278 insertions(+), 97 deletions(-) diff --git a/.gitignore b/.gitignore index 91909cdebcd6..d38240e8b4e3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # Build results *.appx +*.appxbundle *.cer *.ilk *.meta diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index a5f7110ee6c5..2e456e11dfed 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -68,6 +68,7 @@ + diff --git a/tools/CustomBuildTool/Properties/Resources.Designer.cs b/tools/CustomBuildTool/Properties/Resources.Designer.cs index 88851ef585cc..7135019864c9 100644 --- a/tools/CustomBuildTool/Properties/Resources.Designer.cs +++ b/tools/CustomBuildTool/Properties/Resources.Designer.cs @@ -63,16 +63,14 @@ internal Resources() { ///

- + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index f3ed066b91de..fd0778f3dc06 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -105,7 +105,7 @@ - + Resource Files\Binary diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index f89aa45dbbef..c4a0d02c5c01 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -57,6 +57,7 @@ BOOLEAN SetupExtractBuild( ULONG64 totalLength = 0; ULONG64 currentLength = 0; mz_zip_archive zip_archive = { 0 }; + PPH_STRING extractPath = NULL; PVOID resourceBuffer; ULONG resourceLength; SYSTEM_INFO info; @@ -94,9 +95,10 @@ BOOLEAN SetupExtractBuild( } totalLength += zipFileStat.m_uncomp_size; - InterlockedExchange64(&ExtractTotalLength, totalLength); } + InterlockedExchange64(&ExtractTotalLength, totalLength); + SendMessage(Context, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) @@ -106,7 +108,6 @@ BOOLEAN SetupExtractBuild( ULONG indexOfFileName = -1; PPH_STRING fileName; PPH_STRING fullSetupPath; - PPH_STRING extractPath; PVOID buffer; mz_ulong zipFileCrc32 = 0; ULONG bufferLength = 0; @@ -213,19 +214,27 @@ BOOLEAN SetupExtractBuild( goto CleanupExit; currentLength += bufferLength; - InterlockedExchange64(&ExtractCurrentLength, currentLength); - SendMessage(Context, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); + SendMessage(Context, WM_UPDATE_SETUP, 0, (LPARAM)PhGetBaseName(extractPath)); NtClose(fileHandle); mz_free(buffer); } mz_zip_reader_end(&zip_archive); + + if (extractPath) + PhDereferenceObject(extractPath); + return TRUE; CleanupExit: + mz_zip_reader_end(&zip_archive); + + if (extractPath) + PhDereferenceObject(extractPath); + return FALSE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 3ffc04f2b300..cf68f769845f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -81,7 +81,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 From e5d8b4e9a3be2eb12912c5005fd263cf47257f1e Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 10 May 2017 14:57:49 +1000 Subject: [PATCH 071/839] BuildTools: Fix cleanup --- .gitattributes | 2 +- build/build_appx_makecert.cmd | 4 +--- tools/CustomBuildTool/Source Files/Build.cs | 3 ++- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.gitattributes b/.gitattributes index 961777c2611a..8d499c1de08f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files -* text=auto +* text=crlf # Custom for Visual Studio *.cs diff=csharp diff --git a/build/build_appx_makecert.cmd b/build/build_appx_makecert.cmd index d65686b5a244..282352f0efaa 100644 --- a/build/build_appx_makecert.cmd +++ b/build/build_appx_makecert.cmd @@ -2,6 +2,4 @@ @setlocal enableextensions @cd /d "%~dp0\..\" -start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxmakecert" - -pause \ No newline at end of file +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxmakecert" \ No newline at end of file diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 12ea6f115927..10fea83a4cbc 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -208,7 +208,8 @@ public static void CleanupBuildEnvironment() try { - Directory.Delete("build\\output", true); + if (Directory.Exists("build\\output")) + Directory.Delete("build\\output", true); for (int i = 0; i < cleanupFileArray.Length; i++) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index f64000cd50f9ba6d558ea03818794a11b5969893..6675d24006fe65a5d44294098977dbb2bd994260 100644 GIT binary patch delta 1724 zcmY+_drVtZ90&04DHQIxz_^qG#TIZg@~q2v45XF`gULpDI2dn+Q>zl4IFm&**-{>p zFke|ytmCjH}czrS~ij&$02N3&u;jFy!<9k+y*NFJw!$KhI@%8G>Iv72NH$ZSma1$yU1s~r0*VVJqh(4*xeQ+`S2~d(<*rc5HO@dsex*{qdHN-fQck+Y zb-d?Qs?w5qBBpDUE{Iu@fLf*ROhs!U&mVN`%{+r79DPWJ)*31QX6Q zo`gOBDVMLpJZaJC;VJMon6u#Jhl4?X&j&}mYW)YU-FsVp{75JD`F0u+lNOc*0+>g&m ztr>>Uc~Ysk&`NwmdQebi)37TTJz%pEKvA@W(fViXI?Cs zSr|V1W$wkrZStuFaoQE-RcLRQ3gZLq8sC6Drh@n(XAm24n{?9l8?QxOhpN1ov&ic( z*I_P_FuoG3$L>xQzR0(t0l$;7Q;xxhbzL$GqA~t&?t|VfHLiWHeZ+jQkJu%x z4?C5_x{nunxJTW@6=#y(h^SORJhsaj4%UKbuMESmI~;5)cJ(POimZ)VF=oC#?by6s zW+AQ4k*>F6gtU<05}F__C#(@VK>8!+7&=xov;$LS78t`*SYjQB7oXRE-`CC+rJYDM zvoTDfK<`8W=@qQiyHHFz!pcNfyri#Lwcdj|(skCT_pX?;4~;4-K@scL`|u@|F0w6r zJFc2iACLYO1N-qyWT-WB)TlZC^T5z1U&9;LbL+bg&K5=veLI=%7F&z?#4X}pTwa2?`vVQ=Z#$ojU delta 1723 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 Y^pF2)mZAy82U|Uv<-BjuS0R9Vv+yDRo delta 9518 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 From 59fa7403d3b50a1ced6271d8d9f6fd2f18e0b496 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 17:02:48 +1000 Subject: [PATCH 072/839] Fix about window debug version, Update about window text --- ProcessHacker/about.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index 834b055bd054..957c741c0b97 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -3,6 +3,7 @@ * about dialog * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -48,7 +49,7 @@ static INT_PTR CALLBACK PhpAboutDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); -#if (PHAPP_VERSION_REVISION != 0) +#if (PHAPP_VERSION_REVISION != 0x0D06F00D) appName = PhFormatString( L"Process Hacker %u.%u.%u", PHAPP_VERSION_MAJOR, @@ -67,12 +68,11 @@ static INT_PTR CALLBACK PhpAboutDlgProc( 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" wj32 - Wen Jia Liu\n" + L" dmex - Steven G\n" + L" XhmikosR\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" From 3af1cf35e79a196e8d3b3b4b79270ce2ac7887ab Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 17:03:48 +1000 Subject: [PATCH 073/839] phlib: remove legacy icon imports --- phlib/guisup.c | 11 ++--------- phlib/include/guisupp.h | 15 --------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index 3ae41b72c5a4..c8f8f6e3298e 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -565,17 +565,12 @@ HICON PhLoadIcon( _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); @@ -601,13 +596,11 @@ HICON PhLoadIcon( 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); + LoadIconMetric(InstanceHandle, Name, (Flags & PH_LOAD_ICON_SIZE_SMALL) ? LIM_SMALL : LIM_LARGE, &icon); } else { - if (loadIconWithScaleDown) - loadIconWithScaleDown(InstanceHandle, Name, Width, Height, &icon); + LoadIconWithScaleDown(InstanceHandle, Name, Width, Height, &icon); } if (!icon && !(Flags & PH_LOAD_ICON_STRICT)) diff --git a/phlib/include/guisupp.h b/phlib/include/guisupp.h index a2372bd3ddb5..9fd0465ff20e 100644 --- a/phlib/include/guisupp.h +++ b/phlib/include/guisupp.h @@ -1,21 +1,6 @@ #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; From d5cae0ddd91338de7c93c3005b73bcaed9afabe2 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 17:06:17 +1000 Subject: [PATCH 074/839] Improve window icon scaling, Add sysinfo window icons --- ProcessHacker/mainwnd.c | 4 ++-- ProcessHacker/miniinfo.c | 4 ++-- ProcessHacker/sysinfo.c | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 0154356736f9..3d4c8539dd18 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -342,12 +342,12 @@ BOOLEAN PhMwpInitializeWindowClass( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = LoadIcon(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(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); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); if (!RegisterClassEx(&wcex)) return FALSE; diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 743efd387a2b..01d139a083e8 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -110,11 +110,11 @@ VOID PhPinMiniInformation( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = LoadIcon(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(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); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 317667b112a2..5dcbb236ea54 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -281,6 +281,9 @@ VOID PhSipOnInitDialog( VOID ) { + SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDC_INSTRUCTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); From f7cc1e00da46c57b0b8e34b7bf458d64ad0e1043 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 17:14:01 +1000 Subject: [PATCH 075/839] NetworkTools: Fix graph layout bug --- plugins/NetworkTools/ping.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 56f39a697b02..0ccb364a7005 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -325,6 +325,7 @@ INT_PTR CALLBACK NetworkPingWndProc( 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); From 7d8fc713defafbb7c6b1f71cc7cf275f4088b462 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 19:18:22 +1000 Subject: [PATCH 076/839] BuildTools: Add vswere support, Fix various issues --- build/build_appx_makecert.cmd | 4 +- build/build_appx_package.cmd | 2 +- build/build_clean.cmd | 2 + build/build_sdk.cmd | 2 + tools/CustomBuildTool/Source Files/Build.cs | 57 +++++++++--------- .../Source Files/NativeMethods.cs | 2 +- tools/CustomBuildTool/Source Files/Program.cs | 16 ++--- .../Source Files/VisualStudio.cs | 32 ++++++++-- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 10 files changed, 71 insertions(+), 46 deletions(-) diff --git a/build/build_appx_makecert.cmd b/build/build_appx_makecert.cmd index 282352f0efaa..f28f3479e3b5 100644 --- a/build/build_appx_makecert.cmd +++ b/build/build_appx_makecert.cmd @@ -2,4 +2,6 @@ @setlocal enableextensions @cd /d "%~dp0\..\" -start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxmakecert" \ No newline at end of file +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 index 54a1f86cb0c3..06227454c85e 100644 --- a/build/build_appx_package.cmd +++ b/build/build_appx_package.cmd @@ -4,4 +4,4 @@ start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxbuild" -pause \ No newline at end of file +pause diff --git a/build/build_clean.cmd b/build/build_clean.cmd index 40de19df02f2..2d0f2c182fe3 100644 --- a/build/build_clean.cmd +++ b/build/build_clean.cmd @@ -3,3 +3,5 @@ @cd /d "%~dp0\..\" start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleanup" + +pause diff --git a/build/build_sdk.cmd b/build/build_sdk.cmd index c3dc6044491f..7a3a7fe039a6 100644 --- a/build/build_sdk.cmd +++ b/build/build_sdk.cmd @@ -3,3 +3,5 @@ @cd /d "%~dp0\..\" start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-sdk" + +pause diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 10fea83a4cbc..3cb18c2ddfc8 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -121,11 +121,11 @@ public static class Build public static bool InitializeBuildEnvironment(bool CheckDependencies) { TimeStart = DateTime.Now; + MSBuildExePath = VisualStudio.GetMsbuildFilePath(); + CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; GitExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles%\\Git\\cmd\\git.exe"); - MSBuildExePath = Environment.ExpandEnvironmentVariables(VisualStudio.GetMsbuildFilePath()); CertUtilExePath = Environment.ExpandEnvironmentVariables("%SystemRoot%\\system32\\Certutil.exe"); MakeAppxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeAppx.exe"); - CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; BuildNightly = !string.Equals(Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%"), "%APPVEYOR_BUILD_API%", StringComparison.OrdinalIgnoreCase); try @@ -231,8 +231,8 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) Program.PrintColorMessage(Platform, ConsoleColor.White, false); } - string currentGitTag = Win32.ExecCommand(GitExePath, "describe --abbrev=0 --tags --always"); - string latestGitRevision = Win32.ExecCommand(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); + string currentGitTag = Win32.ShellExecute(GitExePath, "describe --abbrev=0 --tags --always"); + string latestGitRevision = Win32.ShellExecute(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); if (string.IsNullOrEmpty(latestGitRevision)) BuildRevision = "0"; @@ -241,7 +241,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) BuildVersion = "3.0." + BuildRevision; BuildLongVersion = "3.0.0." + BuildRevision; - BuildMessage = Win32.ExecCommand(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); + BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); if (ShowBuildInfo) { @@ -249,17 +249,17 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) if (BuildNightly) { - buildMessage = Win32.ExecCommand(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); + buildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); } else { 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.ExecCommand(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(yellow)%h%Creset)\" --abbrev-commit"); + 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(yellow)%h%Creset)\" --abbrev-commit"); } - string currentBranch = Win32.ExecCommand(GitExePath, "rev-parse --abbrev-ref HEAD"); - string currentCommitTag = Win32.ExecCommand(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD + string currentBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); + string currentCommitTag = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD //string latestGitCount = Win32.GitExecCommand("rev-list --count " + BuildBranch); if (!string.IsNullOrEmpty(currentBranch)) @@ -284,14 +284,13 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) } } - public static void ShowBuildStats(bool block) + public static void ShowBuildStats() { TimeSpan buildTime = DateTime.Now - TimeStart; Console.WriteLine(); Console.WriteLine("Build Time: " + buildTime.Minutes + " minute(s), " + buildTime.Seconds + " second(s)"); Console.WriteLine("Build complete."); - if (!BuildNightly && block) Console.ReadKey(); } public static bool CopyTextFiles() @@ -660,13 +659,13 @@ public static bool BuildKphSignatureFile(bool DebugBuild) File.Create("bin\\Debug32\\ProcessHacker.sig").Dispose(); File.Create("bin\\Debug64\\ProcessHacker.sig").Dispose(); - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"))) + if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"))) { Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow); return false; } - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"))) + if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"))) { Program.PrintColorMessage("[WARN] (Debug64) " + output, ConsoleColor.Yellow); return false; @@ -694,13 +693,13 @@ public static bool BuildKphSignatureFile(bool DebugBuild) File.Create("bin\\Release32\\ProcessHacker.sig").Dispose(); File.Create("bin\\Release64\\ProcessHacker.sig").Dispose(); - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"))) + if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"))) { Program.PrintColorMessage("[ERROR] (Release32) " + output, ConsoleColor.Red); return false; } - if (!string.IsNullOrEmpty(output = Win32.ExecCommand(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"))) + if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"))) { Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red); return false; @@ -839,7 +838,7 @@ public static bool BuildSrcZip() if (File.Exists(BuildOutputFolder + "\\processhacker-build-src.zip")) File.Delete(BuildOutputFolder + "\\processhacker-build-src.zip"); - string output = Win32.ExecCommand( + string output = Win32.ShellExecute( GitExePath, "--git-dir=.git " + "--work-tree=.\\ archive " + @@ -935,7 +934,7 @@ public static bool BuildUpdateSignature() return false; } - BuildSetupSig = Win32.ExecCommand( + BuildSetupSig = Win32.ShellExecute( CustomSignToolPath, "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-setup.exe -h" ); @@ -1067,7 +1066,7 @@ public static bool AppveyorUploadBuildFiles() try { - Win32.ExecCommand("appveyor", "PushArtifact " + releaseFileArray[i]); + Win32.ShellExecute("appveyor", "PushArtifact " + releaseFileArray[i]); } catch (Exception ex) { @@ -1088,7 +1087,7 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) Program.PrintColorMessage("x32", ConsoleColor.Green, false, Flags); Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); - string error32 = Win32.ExecCommand( + string error32 = Win32.ShellExecute( MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + @@ -1108,7 +1107,7 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) Program.PrintColorMessage("x64", ConsoleColor.Green, false, Flags); Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); - string error64 = Win32.ExecCommand( + string error64 = Win32.ShellExecute( MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + @@ -1181,14 +1180,14 @@ public static void BuildAppxPackage(BuildFlags Flags) File.WriteAllText("build\\output\\package32.map", packageMap32.ToString()); // create the package - error = Win32.ExecCommand( + error = Win32.ShellExecute( MakeAppxExePath, "pack /o /f build\\output\\package32.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" ); Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the package - error = Win32.ExecCommand( + error = Win32.ShellExecute( signToolExePath, "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" ); @@ -1230,14 +1229,14 @@ public static void BuildAppxPackage(BuildFlags Flags) File.WriteAllText("build\\output\\package64.map", packageMap64.ToString()); // create the package - error = Win32.ExecCommand( + error = Win32.ShellExecute( MakeAppxExePath, "pack /o /f build\\output\\package64.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" ); Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the package - error = Win32.ExecCommand( + error = Win32.ShellExecute( signToolExePath, "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" ); @@ -1253,14 +1252,14 @@ public static void BuildAppxPackage(BuildFlags Flags) File.WriteAllText("build\\output\\bundle.map", bundleMap.ToString()); // create the appx bundle package - error = Win32.ExecCommand( + error = Win32.ShellExecute( MakeAppxExePath, "bundle /f build\\output\\bundle.map /p " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the appx bundle package - error = Win32.ExecCommand( + error = Win32.ShellExecute( signToolExePath, "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); @@ -1291,7 +1290,7 @@ public static bool BuildAppxSignature() if (File.Exists("build\\processhacker-appx.pfx")) File.Delete("build\\processhacker-appx.pfx"); - string output = Win32.ExecCommand(makeCertExePath, + string output = Win32.ShellExecute(makeCertExePath, "/n " + "\"CN=ProcessHacker, O=ProcessHacker, C=AU\" " + "/r /h 0 " + @@ -1307,7 +1306,7 @@ public static bool BuildAppxSignature() return false; } - output = Win32.ExecCommand(pvk2PfxExePath, + output = Win32.ShellExecute(pvk2PfxExePath, "/pvk build\\processhacker-appx.pvk " + "/spc build\\processhacker-appx.cer " + "/pfx build\\processhacker-appx.pfx " @@ -1319,7 +1318,7 @@ public static bool BuildAppxSignature() return false; } - output = Win32.ExecCommand(CertUtilExePath, + output = Win32.ShellExecute(CertUtilExePath, "-addStore TrustedPeople build\\processhacker-appx.cer" ); diff --git a/tools/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs index 865a24548d52..3f16de01e59d 100644 --- a/tools/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -7,7 +7,7 @@ namespace CustomBuildTool [System.Security.SuppressUnmanagedCodeSecurity] public static class Win32 { - public static string ExecCommand(string FileName, string args) + public static string ShellExecute(string FileName, string args) { string output = string.Empty; using (Process process = Process.Start(new ProcessStartInfo diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index b70f7ea3cc08..86e74aaceef4 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -23,7 +23,7 @@ public static void Main(string[] args) Build.CleanupAppxSignature(); Build.CleanupBuildEnvironment(); - Build.ShowBuildStats(true); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-phapppub_gen")) { @@ -56,7 +56,7 @@ public static void Main(string[] args) BuildFlags.BuildDebug | BuildFlags.BuildVerbose ); - Build.ShowBuildStats(false); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-cleansdk")) { @@ -77,7 +77,7 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - Build.ShowBuildStats(false); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-bin")) { @@ -119,7 +119,7 @@ public static void Main(string[] args) if (!Build.BuildBinZip()) return; - Build.ShowBuildStats(false); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-debug")) { @@ -163,7 +163,7 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildDebug | BuildFlags.BuildVerbose); - Build.ShowBuildStats(true); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-release")) { @@ -211,7 +211,7 @@ public static void Main(string[] args) Build.BuildSdkZip(); Build.BuildSrcZip(); - Build.ShowBuildStats(true); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-appveyor")) { @@ -292,7 +292,7 @@ public static void Main(string[] args) Build.BuildAppxPackage(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); - Build.ShowBuildStats(true); + Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-appxmakecert")) { @@ -306,7 +306,7 @@ public static void Main(string[] args) Build.BuildAppxSignature(); - Build.ShowBuildStats(true); + Build.ShowBuildStats(); } else { diff --git a/tools/CustomBuildTool/Source Files/VisualStudio.cs b/tools/CustomBuildTool/Source Files/VisualStudio.cs index f185d5be5f4d..adef9214779a 100644 --- a/tools/CustomBuildTool/Source Files/VisualStudio.cs +++ b/tools/CustomBuildTool/Source Files/VisualStudio.cs @@ -9,17 +9,37 @@ public static class VisualStudio { public static string GetMsbuildFilePath() { - VisualStudioInstance instance = FindVisualStudioInstance(); + string vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe"); - if (instance != null) + // Note: vswere.exe was only released with build 15.0.26418.1 + if (File.Exists(vswhere)) { - if (File.Exists(instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) + string vswhereResult = Win32.ShellExecute(vswhere, + "-latest " + + "-requires Microsoft.Component.MSBuild " + + "-property installationPath " + ); + + if (string.IsNullOrEmpty(vswhereResult)) + return null; + + if (File.Exists(vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) + return vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; + + return null; + } + else + { + VisualStudioInstance instance = FindVisualStudioInstance(); + + if (instance != null) { - return instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; + if (File.Exists(instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) + return instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; } - } - return null; + return null; + } } private static VisualStudioInstance FindVisualStudioInstance() diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 6675d24006fe65a5d44294098977dbb2bd994260..849821b11f52a0e3e5df8888a492e1be6d1b1e31 100644 GIT binary patch delta 14051 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 delta 14004 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 7f64725b18499f7050cdefb749e8a03e49e92f30..46d5a6f6accef820c7b1ea63aa124c0101187bc6 100644 GIT binary patch delta 13303 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 From fae9aa74492f90f49186f4b2f1be8ba8231dcb43 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 19:52:49 +1000 Subject: [PATCH 077/839] CommonUtil: Fix file encoding --- .gitattributes | 1 + plugins/CommonUtil/resource.h | Bin 1174 -> 567 bytes plugins/CommonUtil/resource.rc | Bin 5726 -> 2743 bytes plugins/Plugins.sln | 2 +- 4 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 8d499c1de08f..0fb12ca3d9a1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,6 +15,7 @@ *.config eol=crlf *.cmd eol=crlf *.csproj eol=crlf +*.filters eol=crlf *.h eol=crlf *.md eol=crlf *.manifest eol=crlf diff --git a/plugins/CommonUtil/resource.h b/plugins/CommonUtil/resource.h index 5be361921482250531dd764876da8bb0a181d13b..72caa7b05f55651c274fe1c7a6dd444148d73e91 100644 GIT binary patch literal 567 zcmaKp%WA_g5JmU?iUBVJvDBs?;7Ed++EVa~W+fbZTvUt=e)K`ezt>J_69>1m(QtKi zxubEM<54Qj#ZE{rB=bacI$4ebfyvvZYswx{(+yb#Y_)a4AaY-|PiDca+>Mv7TIT zrsV!VcIHgU#+pWv_|e0dI~%GV-m+@I4$7v58sDKgykg#W6K2__94T;jQJip3X-}r= zN$^RpXBVMxxsv`~NBdx_{OC849^Yyyo?{(~NF^Z?I(7X-{KTTXXz4nn2-u9c-K&8PdWeBq~ znLRtR=OkaB1r_v;MQUoOKsB{^BEO1MRh_7#5A0MY)E49vYNoMPS}Ng*wI>+za;23yVco(u+HHm+=`AQ&|foVosoQ0{%{_!XTJ?CdEg;u99GP6HsiPD#FjTXmloEKW&$*Myz( z#E=#IsFJh1RVU9pF`gUDbrc!4eFq;O&|dV_xfvlxy_0O!F$2{^AU?Rrv@P$k2VS7H z1(6zfrHzxQEhluJka6``Mao6;r=r1HqJd= z?vvu+_;lIv$7_-l6qfWTbVw$IMQ6*7%SDsVL&>|11bNw+=b>9k6q)lhs0nPHKs`xk?O>@f^i#RFBZye<~X33`iy4~^X zxP@mqEySBehULL$LtQj4wtW=1zt@rhrgri?(oZvU%@*Mzd}4|75+evPES!_&Ev9ZF3TFDY}j7?E(KrBvDslUDL^UiS0 zE-X$UskGH~a{|1E>?;-ZOg`=2kY=SM7JJgxOko z5a!*5b>jV#U0TPw*0WP0emOeuL8Cd2Tf*E&9H{FqH3dblxV`Krib$ zzC%k>dqDmL+L0c*Nc}6%CQ_dur}R;W{)&6uBA>RwhoIlMF0cRcY0MEt?|4L4@}xWD z-p@B&^Uk+it)ok@?u7We$5JUikl6Rg`$&I*y%nWno-YclpJrld9Q5>EE~bWo(;40r zW2r;j^oXhxb{ybUEiUo>i!0f>vJ0BG`0jvJ7xZ6*U0d`np!R4R!9Sf6=QZHnBu0O> zm+k0vZH0*l?9{5v`QIepFN6leZ@92O~tP8;48d4X8je)ZHQj)iD=h-4nig# z5?RVF@ltju$~2qa<~Ax_mgHx| znO0}tI9;m~W;BWH`r4cX4}OW?`5ot8!oQ6N_Q*lq&syQ!&qDTSfbN`Z(INH|xPP?Q zSg;l3kyoy=hVomToYNyp@;FzVB8TIr^|$2O6g@pZ|0pwGCzN*0uV{lW@_qwGU2m3a zR@&-P$y|ak&U>}-K4#4PEZ?~}_LX1T=vA7jfIz2*YO)a)8Y9^)cA9{7x)zPa+&n7s zT#u;mj_k)E(L0{`xL&O(%1!>(+dg(wbt4^Az^y81#z{A^Kk>V3Mz_#)dQ{M|MjyBA zSIp6+K4oRLZjBM(oa4R^Au1B~SN%E)dP{1o1HSXU5anah zksMP|Z_{Y8gtDh<^*OETgmtauac5Cim->!!_uwKADYjbVR=wVBfV;=Ifu37o<$I#Kx)YlAa(DT=G~u?_DC7!w3@b@?ZN@9?9;WwWZrkE^vuaGK+OEJN?;hKGVt5c@SeCyb z#;f)Ytg~f|3TH7!RSLy8zZS+A@t=>=HC`@%a5Rq2Tb(`n%JQ*V-No9@YC3P}$R$sE zZnTd_%!uL9&K|Ks@wG61$|KfsZvPhevhQ-YY57|BB5z~m{M_G`&2OOcN#%$nmHOU( j>3=tUlRp37&GqOA%GV0vc0Zp`!RgQN=3ll=yNlDmybi1J diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 15c2c8271d2b..931a8541035a 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26403.7 From 1f49a46b4c3216c021396650bdab6fe32cec5bd0 Mon Sep 17 00:00:00 2001 From: ForeverOf14 Date: Thu, 11 May 2017 19:44:16 +0800 Subject: [PATCH 078/839] PHNT header - new APIs (#137) * Add API-Def: LdrDisableThreadCalloutsForDll * Add API-Def EtwEventRegister * Create ntsmss.h * Add APIs * Complete basic API def * Update ntsmss.h --- phnt/include/ntdbg.h | 11 +++++++++++ phnt/include/ntldr.h | 7 +++++++ phnt/include/ntsmss.h | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 phnt/include/ntsmss.h diff --git a/phnt/include/ntdbg.h b/phnt/include/ntdbg.h index 2be47c2263d4..6d3ea5435607 100644 --- a/phnt/include/ntdbg.h +++ b/phnt/include/ntdbg.h @@ -244,4 +244,15 @@ DbgUiConvertStateChangeStructure( _Out_ struct _DEBUG_EVENT *DebugEvent ); + +NTSYSAPI +NTSTATUS +NTAPI +EtwEventRegister( + _In_ LPCGUID ProviderId, + _In_opt_ PENABLECALLBACK EnableCallback, + _In_opt_ PVOID CallbackContext, + _Out_ PREGHANDLE RegHandle + ); + #endif diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 2434c2bfe125..9eef05a15b73 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -525,6 +525,13 @@ LdrGetFileNameFromLoadAsDataTable( #endif +NTSYSAPI +NTSTATUS +NTAPI +LdrDisableThreadCalloutsForDll( + _In_ PVOID DllImageBase + ); + #endif // (PHNT_MODE != PHNT_MODE_KERNEL) // Module information diff --git a/phnt/include/ntsmss.h b/phnt/include/ntsmss.h new file mode 100644 index 000000000000..07b91b530b03 --- /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 From c911c86808e7d8ad6d09e9b3eddb6aef7e0bb698 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 11 May 2017 21:47:51 +1000 Subject: [PATCH 079/839] Fix spacing --- phnt/include/ntdbg.h | 11 +++++------ phnt/include/ntsmss.h | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/phnt/include/ntdbg.h b/phnt/include/ntdbg.h index 6d3ea5435607..f47d1240614d 100644 --- a/phnt/include/ntdbg.h +++ b/phnt/include/ntdbg.h @@ -244,15 +244,14 @@ DbgUiConvertStateChangeStructure( _Out_ struct _DEBUG_EVENT *DebugEvent ); - NTSYSAPI NTSTATUS NTAPI EtwEventRegister( - _In_ LPCGUID ProviderId, - _In_opt_ PENABLECALLBACK EnableCallback, - _In_opt_ PVOID CallbackContext, - _Out_ PREGHANDLE RegHandle - ); + _In_ LPCGUID ProviderId, + _In_opt_ PENABLECALLBACK EnableCallback, + _In_opt_ PVOID CallbackContext, + _Out_ PREGHANDLE RegHandle + ); #endif diff --git a/phnt/include/ntsmss.h b/phnt/include/ntsmss.h index 07b91b530b03..8f377ac64142 100644 --- a/phnt/include/ntsmss.h +++ b/phnt/include/ntsmss.h @@ -5,18 +5,18 @@ NTSYSAPI NTSTATUS NTAPI RtlConnectToSm( - _In_ PUNICODE_STRING ApiPortName, - _In_ HANDLE ApiPortHandle, - _In_ DWORD ProcessImageType, - _Out_ PHANDLE SmssConnection + _In_ PUNICODE_STRING ApiPortName, + _In_ HANDLE ApiPortHandle, + _In_ DWORD ProcessImageType, + _Out_ PHANDLE SmssConnection ); NTSYSAPI NTSTATUS NTAPI RtlSendMsgToSm( - _In_ HANDLE ApiPortHandle, - _In_ PPORT_MESSAGE MessageData + _In_ HANDLE ApiPortHandle, + _In_ PPORT_MESSAGE MessageData ); #endif From 30c4820d068ec6d3711c4ed2bba352d534772ca4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 01:49:07 +1000 Subject: [PATCH 080/839] Add missing defs from PR #137 --- phnt/include/ntdbg.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/phnt/include/ntdbg.h b/phnt/include/ntdbg.h index f47d1240614d..5cc3b32d4e10 100644 --- a/phnt/include/ntdbg.h +++ b/phnt/include/ntdbg.h @@ -244,6 +244,25 @@ DbgUiConvertStateChangeStructure( _Out_ struct _DEBUG_EVENT *DebugEvent ); +typedef struct _EVENT_FILTER_DESCRIPTOR +{ + ULONGLONG Ptr; + ULONG Size; + ULONG Type; +} EVENT_FILTER_DESCRIPTOR, *PEVENT_FILTER_DESCRIPTOR; + +typedef VOID (NTAPI *PENABLECALLBACK)( + _In_ LPCGUID SourceId, + _In_ ULONG IsEnabled, + _In_ UCHAR Level, + _In_ ULONGLONG MatchAnyKeyword, + _In_ ULONGLONG MatchAllKeyword, + _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, + _Inout_opt_ PVOID CallbackContext + ); + +typedef ULONGLONG REGHANDLE, *PREGHANDLE; + NTSYSAPI NTSTATUS NTAPI From db0a24949cc2003220d1ee8ac98fbd14297c252a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 01:54:35 +1000 Subject: [PATCH 081/839] *Fix last commit --- phnt/include/ntdbg.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/phnt/include/ntdbg.h b/phnt/include/ntdbg.h index 5cc3b32d4e10..deb804e5cee0 100644 --- a/phnt/include/ntdbg.h +++ b/phnt/include/ntdbg.h @@ -244,12 +244,7 @@ DbgUiConvertStateChangeStructure( _Out_ struct _DEBUG_EVENT *DebugEvent ); -typedef struct _EVENT_FILTER_DESCRIPTOR -{ - ULONGLONG Ptr; - ULONG Size; - ULONG Type; -} EVENT_FILTER_DESCRIPTOR, *PEVENT_FILTER_DESCRIPTOR; +struct _EVENT_FILTER_DESCRIPTOR; typedef VOID (NTAPI *PENABLECALLBACK)( _In_ LPCGUID SourceId, @@ -257,7 +252,7 @@ typedef VOID (NTAPI *PENABLECALLBACK)( _In_ UCHAR Level, _In_ ULONGLONG MatchAnyKeyword, _In_ ULONGLONG MatchAllKeyword, - _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, + _In_opt_ struct _EVENT_FILTER_DESCRIPTOR *FilterData, _Inout_opt_ PVOID CallbackContext ); From c9218f35837ed4878bbe51eb5db64bf53598709b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 01:57:16 +1000 Subject: [PATCH 082/839] peview: Fix symbol VA column --- tools/peview/pdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index a9e4f4d5c58c..124b0209c8b8 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -1831,7 +1831,7 @@ VOID SymInfoDump_SymbolLocationStr( else { //_swprintf(Buffer, L"%16I64x", SymbolInfo->Address); - PhPrintPointer(Buffer, (PVOID)SymbolInfo->Address); + PhPrintPointer(Buffer, PTR_SUB_OFFSET(SymbolInfo->Address, SymbolInfo->ModBase)); } } From bcec265f015e48b4f9431b52fcb1926dfdb6deb2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 14:45:23 +1000 Subject: [PATCH 083/839] Update ntpsapi.h --- phnt/include/ntpsapi.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 50d71d513272..2011aa3915cc 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1174,12 +1174,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 @@ -1215,18 +1214,18 @@ typedef enum _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) + PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, FALSE) #define PS_ATTRIBUTE_DEBUG_PORT \ - PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, TRUE) + PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, FALSE) #define PS_ATTRIBUTE_TOKEN \ - PsAttributeValue(PsAttributeToken, FALSE, TRUE, TRUE) + PsAttributeValue(PsAttributeToken, FALSE, TRUE, FALSE) #define PS_ATTRIBUTE_CLIENT_ID \ PsAttributeValue(PsAttributeClientId, TRUE, FALSE, FALSE) #define PS_ATTRIBUTE_TEB_ADDRESS \ From ace57e91087a89aae3d41b7eb2bfe5f54f4d7349 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 15:02:42 +1000 Subject: [PATCH 084/839] BuildTools: update binaries, update installer --- tools/CustomBuildTool/Source Files/Build.cs | 2 +- tools/CustomBuildTool/Source Files/Program.cs | 11 +++--- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes .../CustomSetupTool/CustomSetupTool/extract.c | 37 ++++++++---------- tools/CustomSetupTool/CustomSetupTool/setup.c | 2 +- 6 files changed, 24 insertions(+), 28 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 3cb18c2ddfc8..ddacc7449635 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -277,7 +277,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(currentCommitTag + Environment.NewLine, ConsoleColor.White); - if (!BuildNightly && !string.IsNullOrEmpty(buildMessage)) + if (!string.IsNullOrEmpty(buildMessage)) { Console.WriteLine(buildMessage + Environment.NewLine); } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 86e74aaceef4..a86af9947fed 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -159,10 +159,6 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.BuildDebug)) return; - Build.CopyRedistFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose); - Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-release")) @@ -201,8 +197,6 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.None)) return; - Build.CopyRedistFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); - if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) @@ -249,6 +243,11 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.None)) return; + Build.CopyRedistFiles( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + ); + if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 849821b11f52a0e3e5df8888a492e1be6d1b1e31..3ee36479e5975a4aa2752484c2f7ebc0eea2e8a4 100644 GIT binary patch delta 1805 zcmY+_4NTN!90&04a~$x0{&&cqoo=f1z^`9E*B zr}qKveLxTI4;d<38aR|dt_DuGkR3W9siCDRLA-urCR1-CsboUmOMFSEECl@JW^WTh zVSgTQybJJO_W)s;!6Z{h2)V593H{piH*|N{omeF;6Hv1>C2bY^PD|z=tJz0-ur7Qc zom>ds&YmQsPrs1c8cout234Vbr2Z@?=rkFmm{5A-&dUq6yd^|N`3{<|xce5xnr z`AyD9{U=vU@O<94(LQ$yAJ-s4myg4Y&!mpUEhuKVSBk90G0${UDmLzs5{k>IB2$^d zQ1*mSMZ$s-4r_+c&KRx%sW(_*(#^Y}m3q>q5Q;OZCLfMLiS};&9bb<9^#dL+xHA7>STmeUC>^Q7AR1N-ozoR)e zjB$W%-~amSS+2d<92Z5c=;SBIP%@3cgw#}AKGhKK;h7&hBf}7dxpktFU=58z;d-t? z{1VwiY#5Td6Inx}F~?-#;f}~2*wL>asA(XH!CN&6A-=3y-p;R_GkcMZJgG0t8q?EK zuiCxHLA6w$U1O@1(rjMjqD9JS)0j3%{lhXBdZet;8q+qZRncDLVW&^Bl!bZNCl$-W zd>oW|oMrjYrB=jvQGg>-*)bZ^38^GD&_bM%%3=d8#OQyOnioa5C{?6sOjm1-W8qge zDqa-hx(v4{8WY0mYD$hFa(@LCaHjc$0-e;^vRf`C>(&w-im!xb_Bk;i8%Ci86%B*@>yJ9IC#Z@82H`D_U6Pv$ZdCw@Vm*MPE9=1z07!6;L zN-?xW>U%@mr7GD8@?e)#iS-d1Xul9!!w}Vjo^~;bPvWRrgE=lATegp5Y-9uGI(RsU z-Sv3CX;^C3vYhFZ)D7ndnig$n6D}E< zuMeAXpESX_k#{Qou+57~n~`B;eK>&>wHf772a&F}V6D_?vRZ9LqtrK~RBc12)NSHc z+ZXlO0k6n%kU^T%4&0KZ56K$ZiFrd}FkOopc7X&>*8jOrn>zkx!Nsxe^&ci*xm!4z zJrMk8YIs>DyQGX2H?g_Ivpc7iT~VzwKz&11L#lJ=%kQ>@R_%}Oem(unkGq1qXA}}~ pR9;wo_UH7#y*P48AGz19ml3UR$?W+hvt;3b>8q0SQ6AGn@-K@abqxRj delta 1793 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 zai�JjmoAP7~a}{bBwE0!`&Hw?4fw#ID@-}cTTHxcKTEC zR=5zyFvoIeT~T4I?Sehs*(izNF|JZ16vp;gV9Cx5Pc0MLi*A}N&Wk$Omiv%*r4Gxj zo4?u?;6?s^p`yjvcS>arzHGRJ%1?26b|q{b2p=>ar{)lid6K$LtxJwlwAMaTV9R`5 zuTPuSt^BO7Yg;f`uel_T36nl?;UOvq ztA{9k+xm-3^w6$C!#UI$+&kQlr)9=dobmI;N&YFCRr)e8)zzj)hg!TMOnyp53(gFkQLyzJ50CT@y2HTc;Y7cw4UP6eJJIL)~#)8ZGl_r z=e87KRT!nM5QjEL@LZ`UqJ&uCT!Efi4vrT6oF2MsxP74wbZQFGBAt#LLFz_&6L!^8 z6u61&?rO2UzUwU9My$K*jAB<@cURhaUCYH&yA2Iv<_PW@&fw76+LD!|B_CUVC52Le ztutVYP?DD|Nl78p$&o=_W%+qoUjA4%%kn^39_~o3YUiq)Evpk{wLq>s^RfJ%EYFeU z1+x5uQ|@5BpC!Ftx?W?w#r>=&*L*)KJw}N4of)3macG!3NAT3HLd*SAO<_#yDSvv9 zWS;_$eQO~^mj^x`n)CtSXC+Vau)*o|n zM#gy=O)@UaxFO?r84qQ6*)%HD^OMnDhE7I=tyD!iEbhxj9DAK^DLpPHub2ETFUg*o zbjoyXKc1R&h^+q8HrL(F#Hmv{&P$=4)nfrmU($NW8oo`sFK+3h8g?f~@YKp%H|kMu zCA8o_t+(T@Rr-A5y7&CVj#hWmgG}lBlB=S>NPWB$k$8})!FNX2Rd{M4(6Hk?Jn(J6*R50Lw9Z6UFn0U#;FCg8X1L9;1$`n>KO{4 d`n{W;p$T43{ol^supk=aLj`*aJJK@0{|7ah5DfqT delta 6661 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 / 2) - 4)); } else { - if (strstr(zipFileStat.m_filename, "x64\\")) + if (!strncmp(zipFileStat.m_filename, "x64\\", 4)) + continue; + if (!strncmp(zipFileStat.m_filename, "x86\\", 4)) continue; + + fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); + + if (PhFindStringInString(fileName, 0, L"x32\\") != -1) + PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); } if (!(buffer = mz_zip_reader_extract_to_heap( @@ -140,21 +152,6 @@ BOOLEAN SetupExtractBuild( 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)); - } - extractPath = PhConcatStrings(3, PhGetString(SetupInstallPath), L"\\", PhGetString(fileName)); //OutputDebugString(PhFormatString(L"%s\r\n", extractPath->Buffer)->Buffer); diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index fd9cbe189fc9..a53268c0c517 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -265,7 +265,7 @@ VOID SetupInstallKph( VOID ) { - PPH_STRING clientPath = PhFormatString(L"%s\\ProcessHacker.exe", PhGetString(SetupInstallPath)); + PPH_STRING clientPath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); if (RtlDoesFileExists_U(PhGetString(clientPath))) { From 361b6b39f485c46861b06b5f57eaf64ac21f23e3 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 15:41:40 +1000 Subject: [PATCH 085/839] Remove splitter hack --- ProcessHacker/include/splitter.h | 2 +- ProcessHacker/splitter.c | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h index f872c7a7483a..c1d6fcb4a3dd 100644 --- a/ProcessHacker/include/splitter.h +++ b/ProcessHacker/include/splitter.h @@ -18,7 +18,7 @@ typedef struct _PH_HSPLITTER_CONTEXT LONG SplitterOffset; LONG SplitterPosition; - ULONG SplitterLayoutCount; + PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM Topitem; PPH_LAYOUT_ITEM Bottomitem; diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index ddecd07f23aa..f07d3a0d45f5 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -45,7 +45,7 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( PhInitializeLayoutManager(&context->LayoutManager, Parent); context->SplitterOffset = -4; - context->SplitterPosition = PhGetIntegerSetting(L"TokenSplitterPosition"); + context->SplitterPosition = 250;// PhGetIntegerSetting(L"TokenSplitterPosition"); context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); @@ -68,18 +68,11 @@ VOID PhHSplitterHandleWmSize( ) { // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. - - if (Context->SplitterLayoutCount >= 2) - { - // BUG: If the window is maximized and you move the splitter to the bottom, restoring the window causes - // the bottom control to get moved outside the visible area... Just move the splitter back up. - if ((Context->Bottomitem->Rect.bottom - Context->Bottomitem->Rect.top) <= 100) - Context->SplitterPosition = Context->Topitem->Rect.bottom - Context->Topitem->Rect.top - SPLITTER_PADDING; - } - else - { - Context->SplitterLayoutCount++; - } + + // BUG: If the window is maximized and you move the splitter to the bottom, restoring the window causes + // the bottom control to get moved outside the visible area... Just move the splitter back up. + if ((Context->Bottomitem->Rect.bottom - Context->Bottomitem->Rect.top) <= 100) + Context->SplitterPosition = Context->Topitem->Rect.bottom - Context->Topitem->Rect.top - SPLITTER_PADDING; // Set the bottom margin of the top control. Context->Topitem->Margin.bottom = Height - Context->SplitterPosition - SPLITTER_PADDING; From 281a5336c9dcf73d641ba055fb66cafcf938693b Mon Sep 17 00:00:00 2001 From: lucasg Date: Fri, 12 May 2017 07:42:33 +0200 Subject: [PATCH 086/839] Add description for strict CFG and AuditNonSystemFont (#140) --- ProcessHacker/procmtgn.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/procmtgn.c b/ProcessHacker/procmtgn.c index 26b9915e41e2..4a2c99011157 100644 --- a/ProcessHacker/procmtgn.c +++ b/ProcessHacker/procmtgn.c @@ -276,10 +276,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 +338,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; } From 782ec39aba03dc0e80eac3408d986f59f6bf6cdd Mon Sep 17 00:00:00 2001 From: lucasg Date: Fri, 12 May 2017 09:00:52 +0200 Subject: [PATCH 087/839] Add support for undecorate C++ names (#139) * phlib : Add PhUndecorateName(W) * peviewer : undecorate names on import and export entries --- phlib/include/symprv.h | 16 ++++++++++ phlib/include/symprvp.h | 14 +++++++++ phlib/symprv.c | 66 ++++++++++++++++++++++++++++++++++++++++- tools/peview/peprp.c | 37 +++++++++++++++++++---- 4 files changed, 126 insertions(+), 7 deletions(-) diff --git a/phlib/include/symprv.h b/phlib/include/symprv.h index 4d02924180bf..7c747fb9b913 100644 --- a/phlib/include/symprv.h +++ b/phlib/include/symprv.h @@ -296,6 +296,22 @@ PhWalkThreadStack( _In_opt_ PVOID Context ); +PHLIBAPI +PPH_STRING +NTAPI +PhUndecorateName( + _In_ HANDLE ProcessHandle, + _In_ PCSTR DecoratedName +); + +PHLIBAPI +PPH_STRING +NTAPI +PhUndecorateNameW( + _In_ HANDLE ProcessHandle, + _In_ PWSTR DecoratedName +); + #ifdef __cplusplus } #endif diff --git a/phlib/include/symprvp.h b/phlib/include/symprvp.h index e4435bfc9bb0..a760e142594e 100644 --- a/phlib/include/symprvp.h +++ b/phlib/include/symprvp.h @@ -167,4 +167,18 @@ typedef BOOL (CALLBACK *_SymbolServerSetOptions)( _In_ ULONG64 data ); +typedef DWORD(WINAPI *_UnDecorateSymbolName)( + _In_ PCSTR DecoratedName, + _Out_ PSTR UnDecoratedName, + _In_ DWORD UndecoratedLength, + _In_ DWORD Flags + ); + +typedef DWORD(WINAPI *_UnDecorateSymbolNameW)( + _In_ PCWSTR DecoratedName, + _Out_ PWSTR UnDecoratedName, + _In_ DWORD UndecoratedLength, + _In_ DWORD Flags + ); + #endif \ No newline at end of file diff --git a/phlib/symprv.c b/phlib/symprv.c index 837c92c65efb..641ad92da130 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -96,6 +96,8 @@ _StackWalk64 StackWalk64_I; _MiniDumpWriteDump MiniDumpWriteDump_I; _SymbolServerGetOptions SymbolServerGetOptions; _SymbolServerSetOptions SymbolServerSetOptions; +_UnDecorateSymbolName UnDecorateSymbolName_I; +_UnDecorateSymbolNameW UnDecorateSymbolNameW_I; BOOLEAN PhSymbolProviderInitialization( VOID @@ -150,9 +152,11 @@ VOID PhSymbolProviderCompleteInitialization( MiniDumpWriteDump_I = PhGetProcedureAddress(dbghelpHandle, "MiniDumpWriteDump", 0); SymbolServerGetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerGetOptions", 0); SymbolServerSetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerSetOptions", 0); + UnDecorateSymbolName_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolName", 0); + UnDecorateSymbolNameW_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolNameW", 0); if (SymGetOptions_I && SymSetOptions_I) - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED); + SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_UNDNAME); } PPH_SYMBOL_PROVIDER PhCreateSymbolProvider( @@ -1758,3 +1762,63 @@ NTSTATUS PhWalkThreadStack( return status; } + + +PPH_STRING PhUndecorateName( + _In_ HANDLE ProcessHandle, + _In_ PCSTR DecoratedName +) +{ + PPH_STRING UndecoratedStr = NULL; + PSTR UndecoratedName = NULL; + DWORD CandidateSize = 512; // there is no way to know the resulting length of an undecorated name + // if there is not enough place, the function does not fail. Instead it + // return a truncated name. + + if ((!SymInitialize_I) || (!UnDecorateSymbolName_I)) + return NULL; + + + SymInitialize_I(ProcessHandle, NULL, TRUE); + + UndecoratedName = PhAllocate(CandidateSize*sizeof(CHAR)); + + DWORD Length = UnDecorateSymbolName_I(DecoratedName, UndecoratedName, CandidateSize, UNDNAME_COMPLETE); + if (Length > 0) + { + UndecoratedStr = PhZeroExtendToUtf16(UndecoratedName); + } + + PhFree(UndecoratedName); + return UndecoratedStr; +} + +PPH_STRING PhUndecorateNameW( + _In_ HANDLE ProcessHandle, + _In_ PWSTR DecoratedName +) +{ + + PPH_STRING UndecoratedStr = NULL; + PWSTR UndecoratedName = NULL; + DWORD CandidateSize = 512; // there is no way to know the resulting length of an undecorated name + // if there is not enough place, the function does not fail. Instead it + // return a truncated name. + + if ((!SymInitialize_I) || (!UnDecorateSymbolNameW_I)) + return NULL; + + + SymInitialize_I(ProcessHandle, NULL, TRUE); + + UndecoratedName = PhAllocate(CandidateSize *sizeof(WCHAR)); + + if (UnDecorateSymbolNameW_I(DecoratedName, UndecoratedName, CandidateSize, UNDNAME_COMPLETE)) + { + UndecoratedStr = PhCreateString(UndecoratedName); + } + + PhFree(UndecoratedName); + return UndecoratedStr; + +} \ No newline at end of file diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 6d3b832abaa7..da280bd14f44 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -31,6 +31,10 @@ #include #include +BOOLEAN PvpLoadDbgHelp( + _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider +); + #define PVM_CHECKSUM_DONE (WM_APP + 1) #define PVM_VERIFY_DONE (WM_APP + 2) @@ -78,6 +82,7 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( PH_MAPPED_IMAGE PvMappedImage; PIMAGE_COR20_HEADER PvImageCor20Header; +PPH_SYMBOL_PROVIDER symbolProvider; HICON PvImageLargeIcon; PH_IMAGE_VERSION_INFO PvImageVersionInfo; @@ -105,6 +110,10 @@ VOID PvPeProperties( return; } + // Failing to load dbghelp is not critical : it just won't be possible + // to look up symbol names. No need to check the returned value. + PvpLoadDbgHelp(&symbolProvider); + if (propContext = PvCreatePropContext(PvFileName)) { PPV_PROPPAGECONTEXT newPage; @@ -705,6 +714,8 @@ VOID PvpProcessImports( PPH_STRING name; WCHAR number[PH_INT32_STR_LEN_1]; + + if (DelayImports) name = PhFormatString(L"%S (Delay)", importDll.Name); else @@ -715,7 +726,12 @@ VOID PvpProcessImports( if (importEntry.Name) { - name = PhZeroExtendToUtf16(importEntry.Name); + name = PhUndecorateName(symbolProvider->ProcessHandle, importEntry.Name); + if (!name) + { + name = PhZeroExtendToUtf16(importEntry.Name); + } + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); PhDereferenceObject(name); @@ -870,8 +886,14 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (exportFunction.ForwardedName) { - name = PhZeroExtendToUtf16(exportFunction.ForwardedName); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, name->Buffer); + name = PhUndecorateName(symbolProvider->ProcessHandle, exportFunction.ForwardedName); + if (!name) + { + name = PhZeroExtendToUtf16(exportFunction.ForwardedName); + } + + + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); PhDereferenceObject(name); } else @@ -882,7 +904,11 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (exportEntry.Name) { - name = PhZeroExtendToUtf16(exportEntry.Name); + name = PhUndecorateName(symbolProvider->ProcessHandle, exportEntry.Name); + if (!name) + { + name = PhZeroExtendToUtf16(exportEntry.Name); + } PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); PhDereferenceObject(name); } @@ -898,7 +924,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( } ExtendedListView_SortItems(lvHandle); - + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; @@ -1295,7 +1321,6 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( case WM_INITDIALOG: { HWND lvHandle; - PPH_SYMBOL_PROVIDER symbolProvider = NULL; PH_MAPPED_IMAGE_CFG cfgConfig = { 0 }; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); From c73c5c0e73632eefa50bc2c1c5aea308dd762580 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 17:22:18 +1000 Subject: [PATCH 088/839] peview: Add image import index column, Improve undecorate symbols performance (only undecorate symbols starting with ?) --- tools/peview/peprp.c | 81 ++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index da280bd14f44..e6b4d38e9466 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -694,7 +694,8 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( VOID PvpProcessImports( _In_ HWND ListViewHandle, _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, - _In_ BOOLEAN DelayImports + _In_ BOOLEAN DelayImports, + _Inout_ ULONG *Count ) { PH_MAPPED_IMAGE_IMPORT_DLL importDll; @@ -714,34 +715,39 @@ VOID PvpProcessImports( PPH_STRING name; WCHAR number[PH_INT32_STR_LEN_1]; - - if (DelayImports) name = PhFormatString(L"%S (Delay)", importDll.Name); else name = PhZeroExtendToUtf16(importDll.Name); - lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, name->Buffer, NULL); + PhPrintUInt64(number, ++(*Count)); // HACK + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); PhDereferenceObject(name); if (importEntry.Name) { - name = PhUndecorateName(symbolProvider->ProcessHandle, importEntry.Name); - if (!name) - { - name = PhZeroExtendToUtf16(importEntry.Name); - } + PPH_STRING importName = NULL; - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); - PhDereferenceObject(name); + if (importEntry.Name[0] == '?') + importName = PhUndecorateName(symbolProvider->ProcessHandle, importEntry.Name); + else + importName = PhZeroExtendToUtf16(importEntry.Name); + + if (!importName) + importName = PhZeroExtendToUtf16(importEntry.Name); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, importName->Buffer); + PhDereferenceObject(importName); PhPrintUInt32(number, importEntry.NameHint); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, number); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, number); } else { name = PhFormatString(L"(Ordinal %u)", importEntry.Ordinal); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, name->Buffer); PhDereferenceObject(name); } } @@ -767,6 +773,7 @@ INT_PTR CALLBACK PvpPeImportsDlgProc( { case WM_INITDIALOG: { + ULONG count = 0; ULONG fallbackColumns[] = { 0, 1, 2 }; HWND lvHandle; PH_MAPPED_IMAGE_IMPORTS imports; @@ -774,21 +781,22 @@ INT_PTR CALLBACK PvpPeImportsDlgProc( 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"); + 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); if (NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage))) { - PvpProcessImports(lvHandle, &imports, FALSE); + PvpProcessImports(lvHandle, &imports, FALSE, &count); } if (NT_SUCCESS(PhGetMappedImageDelayImports(&imports, &PvMappedImage))) { - PvpProcessImports(lvHandle, &imports, TRUE); + PvpProcessImports(lvHandle, &imports, TRUE, &count); } ExtendedListView_SortItems(lvHandle); @@ -871,7 +879,6 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( ) { INT lvItemIndex; - PPH_STRING name; WCHAR number[PH_INT32_STR_LEN_1]; WCHAR pointer[PH_PTR_STR_LEN_1]; @@ -886,15 +893,18 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (exportFunction.ForwardedName) { - name = PhUndecorateName(symbolProvider->ProcessHandle, exportFunction.ForwardedName); - if (!name) - { - name = PhZeroExtendToUtf16(exportFunction.ForwardedName); - } + PPH_STRING forwardName = NULL; + if (exportFunction.ForwardedName[0] == '?') + forwardName = PhUndecorateName(symbolProvider->ProcessHandle, exportFunction.ForwardedName); + else + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); - PhDereferenceObject(name); + if (!forwardName) + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); + + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, forwardName->Buffer, NULL); + PhDereferenceObject(forwardName); } else { @@ -904,13 +914,18 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (exportEntry.Name) { - name = PhUndecorateName(symbolProvider->ProcessHandle, exportEntry.Name); - if (!name) - { - name = PhZeroExtendToUtf16(exportEntry.Name); - } - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); - PhDereferenceObject(name); + PPH_STRING exportName = NULL; + + if (exportEntry.Name[0] == '?') + exportName = PhUndecorateName(symbolProvider->ProcessHandle, exportEntry.Name); + else + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + if (!exportName) + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, exportName->Buffer); + PhDereferenceObject(exportName); } else { From 88b9822887071135d923474817e4ab7bdecf87e2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 18:23:58 +1000 Subject: [PATCH 089/839] peview: Fix display of module export forwarders --- tools/peview/peprp.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index e6b4d38e9466..aebb9ea6fd34 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -33,7 +33,7 @@ BOOLEAN PvpLoadDbgHelp( _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider -); + ); #define PVM_CHECKSUM_DONE (WM_APP + 1) #define PVM_VERIFY_DONE (WM_APP + 2) @@ -882,12 +882,6 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( WCHAR number[PH_INT32_STR_LEN_1]; WCHAR pointer[PH_PTR_STR_LEN_1]; - // TODO: user32.dll and some other dlls have many (unnamed) exports with (invalid) 0x0 RVA, - // they might be exported variables or caused by incorrect math, ignore these entries - // until more information is available. - if (!exportFunction.Function) - continue; - PhPrintUInt64(number, i + 1); lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); @@ -903,7 +897,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( if (!forwardName) forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, forwardName->Buffer, NULL); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, forwardName->Buffer); PhDereferenceObject(forwardName); } else From 9fdfd0a65b9237f8c2ed03b70de66c3290c1c451 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 18:25:13 +1000 Subject: [PATCH 090/839] Add missing handles tab searchbox filters --- ProcessHacker/prpghndl.c | 42 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index ada104e8e0b3..6f0861eee24f 100644 --- a/ProcessHacker/prpghndl.c +++ b/ProcessHacker/prpghndl.c @@ -250,15 +250,17 @@ BOOLEAN PhpHandleTreeFilterCallback( ) { PPH_HANDLES_CONTEXT handlesContext = Context; - PPH_HANDLE_NODE processNode = (PPH_HANDLE_NODE)Node; - PPH_HANDLE_ITEM handleItem = processNode->HandleItem; + PPH_HANDLE_NODE handleNode = (PPH_HANDLE_NODE)Node; + PPH_HANDLE_ITEM handleItem = handleNode->HandleItem; - if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->BestObjectName)) + if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->ObjectName)) return FALSE; if (PhIsNullOrEmptyString(handlesContext->SearchboxText)) return TRUE; + // handle properties + if (!PhIsNullOrEmptyString(handleItem->TypeName)) { if (PhpWordMatchHandleStringRef(handlesContext->SearchboxText, &handleItem->TypeName->sr)) @@ -277,6 +279,40 @@ BOOLEAN PhpHandleTreeFilterCallback( return TRUE; } + if (handleItem->HandleString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleItem->HandleString)) + return TRUE; + } + + if (handleItem->ObjectString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleItem->ObjectString)) + return TRUE; + } + + if (handleItem->GrantedAccessString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleItem->GrantedAccessString)) + return TRUE; + } + + // TODO: Add search for handleItem->Attributes + + // node properties + + 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; } From 122e15c1a92472cbaf36eb0de4f16bb53b104f09 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 12 May 2017 18:30:01 +1000 Subject: [PATCH 091/839] peview: enable column reordering --- tools/peview/peprp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index aebb9ea6fd34..2995b7aefb07 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -552,7 +552,7 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( PhDeleteStringBuilder(&stringBuilder); lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + 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"); @@ -779,7 +779,7 @@ INT_PTR CALLBACK PvpPeImportsDlgProc( PH_MAPPED_IMAGE_IMPORTS imports; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + 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"); @@ -860,7 +860,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( ULONG i; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + 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"); @@ -991,7 +991,7 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( PIMAGE_LOAD_CONFIG_DIRECTORY64 config64; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + 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"); @@ -1333,7 +1333,7 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( PH_MAPPED_IMAGE_CFG cfgConfig = { 0 }; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetListViewStyle(lvHandle, TRUE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); From 5ed9fc8eed594d8c2a9afa7bbba126c9b761f249 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 13 May 2017 17:39:09 +1000 Subject: [PATCH 092/839] peview: fix unicode string parameters --- tools/peview/peprp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 2995b7aefb07..36db629c6fb6 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -1158,6 +1158,7 @@ BOOLEAN PvpLoadDbgHelp( UNICODE_STRING symbolPathUs = { .Buffer = buffer, + .Length = sizeof(buffer) - sizeof(UNICODE_NULL), .MaximumLength = sizeof(buffer) }; From 0e30a883b26167ac248613382d6cb5e964a449d2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 13 May 2017 17:40:24 +1000 Subject: [PATCH 093/839] BuildTool: Remove redist files --- tools/CustomBuildTool/Source Files/Program.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index a86af9947fed..dfaa12b29ea7 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -51,11 +51,6 @@ public static void Main(string[] args) if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - Build.CopyRedistFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose - ); - Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-cleansdk")) @@ -243,11 +238,6 @@ public static void Main(string[] args) if (!Build.CopyWow64Files(BuildFlags.None)) return; - Build.CopyRedistFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose - ); - if (!Build.BuildBinZip()) return; if (!Build.BuildSetupExe()) From 3fb3d8f183db671b194474e004e56bca374a84f1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 13 May 2017 19:48:54 +1000 Subject: [PATCH 094/839] Improve CfgBitmap memory tagging for wow64, Update LDR_INIT_BLOCK with correct definitions --- ProcessHacker/memprv.c | 79 ++++++++++-------------------------------- phnt/include/ntldr.h | 36 ++++++++++++++----- 2 files changed, 46 insertions(+), 69 deletions(-) diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index cf5979bd1fc8..998ed6eeca73 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -489,7 +489,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( #ifdef _WIN64 - LDR_INIT_BLOCK ldrInitBlock = { 0 }; + PS_SYSTEM_DLL_INIT_BLOCK ldrInitBlock = { 0 }; PVOID ldrInitBlockBaseAddress = NULL; PPH_MEMORY_ITEM cfgBitmapMemoryItem; PPH_STRING ntdllFileName; @@ -510,7 +510,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( ProcessHandle, ldrInitBlockBaseAddress, &ldrInitBlock, - sizeof(LDR_INIT_BLOCK), + sizeof(PS_SYSTEM_DLL_INIT_BLOCK), NULL ); } @@ -520,24 +520,23 @@ NTSTATUS PhpUpdateMemoryRegionTypes( if (NT_SUCCESS(status) && ldrInitBlock.Size) { PVOID cfgBitmapAddress = NULL; + PVOID cfgBitmapWow64Address = 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 (ldrInitBlock.Size == sizeof(PS_SYSTEM_DLL_INIT_BLOCK)) + { + 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); - // 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 + // 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; @@ -545,61 +544,19 @@ NTSTATUS PhpUpdateMemoryRegionTypes( memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); } } - } - - if (isWow64) - { - LDR_INIT_BLOCK ldrInitBlock32 = { 0 }; - PVOID ldrInitBlockBaseAddress32 = NULL; - PPH_MEMORY_ITEM cfgBitmapMemoryItem32; - PPH_STRING ntdllWow64FileName; - - ntdllWow64FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\ntdll.dll"); - status = PhGetProcedureAddressRemote( - ProcessHandle, - ntdllWow64FileName->Buffer, - "LdrSystemDllInitBlock", - 0, - &ldrInitBlockBaseAddress32, - NULL - ); - - if (NT_SUCCESS(status) && ldrInitBlockBaseAddress32) - { - status = NtReadVirtualMemory( - ProcessHandle, - ldrInitBlockBaseAddress32, - &ldrInitBlock32, - sizeof(LDR_INIT_BLOCK), - NULL - ); - } - - PhDereferenceObject(ntdllWow64FileName); - if (NT_SUCCESS(status) && ldrInitBlock32.Size) + // Note: Wow64 processes on 64bit also have CfgBitmap regions. + if (isWow64 && cfgBitmapWow64Address && (cfgBitmapMemoryItem = PhLookupMemoryItemList(List, cfgBitmapWow64Address))) { - PVOID cfgBitmapAddress = NULL; - - // TODO: Remove this code once most users have updated their machines. - if (ldrInitBlock32.Size == sizeof(LDR_INIT_BLOCK)) - cfgBitmapAddress = ldrInitBlock32.CfgBitmapAddress; // 15063 - else if (ldrInitBlock32.Size == 128) - cfgBitmapAddress = ldrInitBlock32.Unknown1[11]; // 14393 + PLIST_ENTRY listEntry = &cfgBitmapMemoryItem->ListEntry; + PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - if (cfgBitmapAddress && (cfgBitmapMemoryItem32 = PhLookupMemoryItemList(List, cfgBitmapAddress))) + while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem) { - PLIST_ENTRY listEntry = &cfgBitmapMemoryItem32->ListEntry; - PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - - // Tagging memory items - while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem32) - { - memoryItem->RegionType = CfgBitmap32Region; + memoryItem->RegionType = CfgBitmap32Region; - listEntry = listEntry->Flink; - memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - } + listEntry = listEntry->Flink; + memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); } } } diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 9eef05a15b73..6ec947c0f407 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -468,20 +468,40 @@ LdrUnregisterDllNotification( // end_msdn -// rev -typedef struct _LDR_INIT_BLOCK +// private +typedef struct _PS_MITIGATION_OPTIONS_MAP +{ + ULONG_PTR Map[2]; +} PS_MITIGATION_OPTIONS_MAP, *PPS_MITIGATION_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_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 From 4a5826078dc27840c42b3779e7a8ba7d26d22472 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 14 May 2017 05:54:15 +1000 Subject: [PATCH 095/839] Add initial CustomStartTool project --- .../CustomStartTool/CustomStartTool.manifest | 46 ++++ tools/CustomStartTool/CustomStartTool.sln | 28 +++ tools/CustomStartTool/CustomStartTool.vcxproj | 212 ++++++++++++++++++ .../CustomStartTool.vcxproj.filters | 27 +++ tools/CustomStartTool/main.c | 155 +++++++++++++ 5 files changed, 468 insertions(+) create mode 100644 tools/CustomStartTool/CustomStartTool.manifest create mode 100644 tools/CustomStartTool/CustomStartTool.sln create mode 100644 tools/CustomStartTool/CustomStartTool.vcxproj create mode 100644 tools/CustomStartTool/CustomStartTool.vcxproj.filters create mode 100644 tools/CustomStartTool/main.c 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..787cb050f1df --- /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.15063.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/tools/CustomStartTool/CustomStartTool.vcxproj.filters b/tools/CustomStartTool/CustomStartTool.vcxproj.filters new file mode 100644 index 000000000000..948f2cbc045f --- /dev/null +++ b/tools/CustomStartTool/CustomStartTool.vcxproj.filters @@ -0,0 +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 + + + + + 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..e967ae3f8434 --- /dev/null +++ b/tools/CustomStartTool/main.c @@ -0,0 +1,155 @@ +/* + * 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(PhInitializePhLibEx(0, 0, 0))) + return 1; + + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return 1; + + info.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(info.lpAttributeList, 1, 0, &attributeListLength)) + return 1; + + if (!UpdateProcThreadAttribute(info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &PhMitigationPolicy, sizeof(ULONG64), NULL, NULL)) + 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()); + + CreateProcess( + NULL, + fileName->Buffer, + NULL, + NULL, + FALSE, + EXTENDED_STARTUPINFO_PRESENT, + NULL, + NULL, + &info.StartupInfo, + &processInfo + ); + + PhDereferenceObject(fileName); + + if (processInfo.hProcess) + NtClose(processInfo.hProcess); + + if (processInfo.hThread) + NtClose(processInfo.hThread); + + DeleteProcThreadAttributeList(info.lpAttributeList); + + return 0; +} From aef144bdaae0a86cf05aa5df075784a0e14c0ca1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 14 May 2017 20:01:40 +1000 Subject: [PATCH 096/839] peview: update project file layout (needed for future additions) --- tools/peview/cfgprp.c | 173 +++++++ tools/peview/clrprp.c | 152 ++++++ tools/peview/expprp.c | 157 ++++++ tools/peview/impprp.c | 169 ++++++ tools/peview/include/pdb.h | 78 +-- tools/peview/include/peview.h | 44 ++ tools/peview/ldprp.c | 160 ++++++ tools/peview/pdb.c | 292 ++++++----- tools/peview/peprp.c | 778 +--------------------------- tools/peview/peview.vcxproj | 7 +- tools/peview/peview.vcxproj.filters | 147 +++--- 11 files changed, 1148 insertions(+), 1009 deletions(-) create mode 100644 tools/peview/cfgprp.c create mode 100644 tools/peview/clrprp.c create mode 100644 tools/peview/expprp.c create mode 100644 tools/peview/impprp.c create mode 100644 tools/peview/ldprp.c diff --git a/tools/peview/cfgprp.c b/tools/peview/cfgprp.c new file mode 100644 index 000000000000..eafb3d8bea3f --- /dev/null +++ b/tools/peview/cfgprp.c @@ -0,0 +1,173 @@ +/* + * 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 (!(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; + } + + return FALSE; +} diff --git a/tools/peview/clrprp.c b/tools/peview/clrprp.c new file mode 100644 index 000000000000..d0c46d7bccf6 --- /dev/null +++ b/tools/peview/clrprp.c @@ -0,0 +1,152 @@ +/* + * 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 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; +} \ No newline at end of file diff --git a/tools/peview/expprp.c b/tools/peview/expprp.c new file mode 100644 index 000000000000..0688891d8c11 --- /dev/null +++ b/tools/peview/expprp.c @@ -0,0 +1,157 @@ +/* + * 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 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"); + 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]; + + PhPrintUInt64(number, i + 1); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + if (exportFunction.ForwardedName) + { + PPH_STRING forwardName = NULL; + + if (exportFunction.ForwardedName[0] == '?') + forwardName = PhUndecorateName(PvSymbolProvider->ProcessHandle, exportFunction.ForwardedName); + else + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); + + if (!forwardName) + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); + + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, forwardName->Buffer); + PhDereferenceObject(forwardName); + } + else + { + PhPrintPointer(pointer, exportFunction.Function); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + } + + if (exportEntry.Name) + { + PPH_STRING exportName = NULL; + + if (exportEntry.Name[0] == '?') + exportName = PhUndecorateName(PvSymbolProvider->ProcessHandle, exportEntry.Name); + else + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + if (!exportName) + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, exportName->Buffer); + PhDereferenceObject(exportName); + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } + + PhPrintUInt32(number, exportEntry.Ordinal); + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, 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; + } + + return FALSE; +} diff --git a/tools/peview/impprp.c b/tools/peview/impprp.c new file mode 100644 index 000000000000..9a1dd38b2256 --- /dev/null +++ b/tools/peview/impprp.c @@ -0,0 +1,169 @@ +/* + * 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 + +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; + + 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 = PhFormatString(L"%S (Delay)", importDll.Name); + else + name = PhZeroExtendToUtf16(importDll.Name); + + PhPrintUInt64(number, ++(*Count)); // HACK + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); + PhDereferenceObject(name); + + if (importEntry.Name) + { + PPH_STRING importName = NULL; + + if (importEntry.Name[0] == '?') + importName = PhUndecorateName(PvSymbolProvider->ProcessHandle, importEntry.Name); + else + importName = PhZeroExtendToUtf16(importEntry.Name); + + if (!importName) + importName = PhZeroExtendToUtf16(importEntry.Name); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, importName->Buffer); + PhDereferenceObject(importName); + + PhPrintUInt32(number, importEntry.NameHint); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, number); + } + else + { + name = PhFormatString(L"(Ordinal %u)", importEntry.Ordinal); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, 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 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); + + 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); + + 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: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h index 7b49cc6f668f..b21ac2d946c7 100644 --- a/tools/peview/include/pdb.h +++ b/tools/peview/include/pdb.h @@ -385,61 +385,61 @@ typedef struct _PDB_SYMBOL_CONTEXT PPH_LIST UdtList; } PDB_SYMBOL_CONTEXT, *PPDB_SYMBOL_CONTEXT; -BOOLEAN SymInfoDump_DumpBasicType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseTypeInfo* Info); -BOOLEAN SymInfoDump_DumpPointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, PointerTypeInfo* Info); -BOOLEAN SymInfoDump_DumpTypedef(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypedefInfo* Info); -BOOLEAN SymInfoDump_DumpEnum(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, EnumInfo* Info); -BOOLEAN SymInfoDump_DumpArrayType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ArrayTypeInfo* Info); -BOOLEAN SymInfoDump_DumpUDT(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); -BOOLEAN SymInfoDump_DumpUDTClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtClassInfo* Info); -BOOLEAN SymInfoDump_DumpUDTUnion(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtUnionInfo* Info); -BOOLEAN SymInfoDump_DumpFunctionType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionTypeInfo* Info); -BOOLEAN SymInfoDump_DumpFunctionArgType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionArgTypeInfo* Info); -BOOLEAN SymInfoDump_DumpBaseClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseClassInfo* Info); -BOOLEAN SymInfoDump_DumpData(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, DataInfo* Info); -BOOLEAN SymInfoDump_DumpType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); -BOOLEAN SymInfoDump_DumpSymbolType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info, ULONG* TypeIndex); - -BOOLEAN SymInfoDump_CheckTag( +BOOLEAN SymbolInfo_DumpBasicType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseTypeInfo* Info); +BOOLEAN SymbolInfo_DumpPointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, PointerTypeInfo* Info); +BOOLEAN SymbolInfo_DumpTypedef(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypedefInfo* Info); +BOOLEAN SymbolInfo_DumpEnum(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, EnumInfo* Info); +BOOLEAN SymbolInfo_DumpArrayType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ArrayTypeInfo* Info); +BOOLEAN SymbolInfo_DumpUDT(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); +BOOLEAN SymbolInfo_DumpUDTClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtClassInfo* Info); +BOOLEAN SymbolInfo_DumpUDTUnion(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtUnionInfo* Info); +BOOLEAN SymbolInfo_DumpFunctionType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionTypeInfo* Info); +BOOLEAN SymbolInfo_DumpFunctionArgType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionArgTypeInfo* Info); +BOOLEAN SymbolInfo_DumpBaseClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseClassInfo* Info); +BOOLEAN SymbolInfo_DumpData(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, DataInfo* Info); +BOOLEAN SymbolInfo_DumpType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); +BOOLEAN SymbolInfo_DumpSymbolType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info, ULONG* TypeIndex); + +BOOLEAN SymbolInfo_CheckTag( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _In_ ULONG Tag ); -BOOLEAN SymInfoDump_SymbolSize(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* Size); -BOOLEAN SymInfoDump_ArrayElementTypeIndex(_Inout_ PPDB_SYMBOL_CONTEXT Context, ULONG ArrayIndex, ULONG* ElementTypeIndex); -BOOLEAN SymInfoDump_ArrayDims(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* pDims, ULONG* Dims, _In_ ULONG MaxDims); -BOOLEAN SymInfoDump_UdtVariables(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pVars, ULONG* Vars, _In_ ULONG MaxVars); -BOOLEAN SymInfoDump_UdtFunctions(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pFuncs, ULONG* Funcs, _In_ ULONG MaxFuncs); -BOOLEAN SymInfoDump_UdtBaseClasses(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pBases, ULONG* Bases, _In_ ULONG MaxBases); -BOOLEAN SymInfoDump_UdtUnionMembers(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pMembers, ULONG* Members, _In_ ULONG MaxMembers); -BOOLEAN SymInfoDump_FunctionArguments(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pArgs, ULONG* Args, _In_ ULONG MaxArgs); -BOOLEAN SymInfoDump_Enumerators(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pEnums, ULONG* Enums, _In_ ULONG MaxEnums); -BOOLEAN SymInfoDump_TypeDefType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex); -BOOLEAN SymInfoDump_PointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex, ULONG* NumPointers); - -BOOLEAN SymInfoDump_GetTypeNameHelper( +BOOLEAN SymbolInfo_SymbolSize(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* Size); +BOOLEAN SymbolInfo_ArrayElementTypeIndex(_Inout_ PPDB_SYMBOL_CONTEXT Context, ULONG ArrayIndex, ULONG* ElementTypeIndex); +BOOLEAN SymbolInfo_ArrayDims(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* pDims, ULONG* Dims, _In_ ULONG MaxDims); +BOOLEAN SymbolInfo_UdtVariables(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pVars, ULONG* Vars, _In_ ULONG MaxVars); +BOOLEAN SymbolInfo_UdtFunctions(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pFuncs, ULONG* Funcs, _In_ ULONG MaxFuncs); +BOOLEAN SymbolInfo_UdtBaseClasses(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pBases, ULONG* Bases, _In_ ULONG MaxBases); +BOOLEAN SymbolInfo_UdtUnionMembers(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pMembers, ULONG* Members, _In_ ULONG MaxMembers); +BOOLEAN SymbolInfo_FunctionArguments(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pArgs, ULONG* Args, _In_ ULONG MaxArgs); +BOOLEAN SymbolInfo_Enumerators(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pEnums, ULONG* Enums, _In_ ULONG MaxEnums); +BOOLEAN SymbolInfo_TypeDefType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex); +BOOLEAN SymbolInfo_PointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex, ULONG* NumPointers); + +BOOLEAN SymbolInfo_GetTypeNameHelper( _In_ ULONG Index, _Inout_ PPDB_SYMBOL_CONTEXT Context, _Out_ PWSTR *VarName, _Out_ PWSTR *TypeName ); -BOOLEAN SymInfoDump_GetTypeName( +BOOLEAN SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _In_ PWSTR pVarName, _In_ PWSTR pTypeName, _In_ ULONG MaxChars ); -PWSTR SymInfoDump_TagStr(enum SymTagEnum Tag); -PWSTR SymInfoDump_BaseTypeStr(BasicType Type, ULONG64 Length); -PWSTR SymInfoDump_CallConvStr(CV_call_e CallConv); -PWSTR SymInfoDump_DataKindFromSymbolInfo(_In_ PSYMBOL_INFOW rSymbol); -PWSTR SymInfoDump_DataKindStr(DataKind dataKind); -VOID SymInfoDump_SymbolLocationStr(PSYMBOL_INFOW rSymbol, PWSTR pBuffer); -PWSTR SymInfoDump_RegisterStr(CV_HREG_e RegCode); -PWSTR SymInfoDump_UdtKindStr(UdtKind KindType); -PWSTR SymInfoDump_LocationTypeStr(LocationType LocType); +PWSTR SymbolInfo_TagStr(enum SymTagEnum Tag); +PWSTR SymbolInfo_BaseTypeStr(BasicType Type, ULONG64 Length); +PWSTR SymbolInfo_CallConvStr(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 PrintDataInfo( diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 639222135ceb..04a3cb8152a2 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -3,13 +3,20 @@ #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; // peprp @@ -66,4 +73,41 @@ VOID PvPdbProperties( VOID ); +// + +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 + ); + #endif diff --git a/tools/peview/ldprp.c b/tools/peview/ldprp.c new file mode 100644 index 000000000000..22d6e24d5117 --- /dev/null +++ b/tools/peview/ldprp.c @@ -0,0 +1,160 @@ +/* + * 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 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_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_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; + } + + return FALSE; +} diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 124b0209c8b8..19fc41cdcde2 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -73,12 +73,18 @@ typedef ULONG64 (WINAPI *_SymLoadModuleExW)( _In_ ULONG Flags ); -typedef BOOL(WINAPI *_SymGetModuleInfoW64)( +typedef BOOL (WINAPI *_SymGetModuleInfoW64)( _In_ HANDLE hProcess, _In_ ULONG64 qwAddr, _Out_ PIMAGEHLP_MODULEW64 ModuleInfo ); +typedef BOOL (WINAPI *_SymSetContext)( + _In_ HANDLE hProcess, + _In_ PIMAGEHLP_STACK_FRAME StackFrame, + _In_opt_ PIMAGEHLP_CONTEXT Context + ); + _SymInitialize SymInitialize_I = NULL; _SymCleanup SymCleanup_I = NULL; _SymEnumSymbolsW SymEnumSymbolsW_I = NULL; @@ -88,8 +94,9 @@ _SymSetOptions SymSetOptions_I = NULL; _SymLoadModuleExW SymLoadModuleExW_I = NULL; _SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; _SymGetTypeInfo SymGetTypeInfo_I = NULL; +_SymSetContext SymSetContext_I = NULL; -BOOLEAN SymInfoDump_DumpBasicType( +BOOLEAN SymbolInfo_DumpBasicType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ BaseTypeInfo *Info @@ -98,7 +105,7 @@ BOOLEAN SymInfoDump_DumpBasicType( ULONG baseType = btNoType; ULONG64 length = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagBaseType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagBaseType)) return FALSE; // Basic type ("basicType" in DIA) @@ -114,7 +121,7 @@ BOOLEAN SymInfoDump_DumpBasicType( return TRUE; } -BOOLEAN SymInfoDump_DumpPointerType( +BOOLEAN SymbolInfo_DumpPointerType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ PointerTypeInfo* Info @@ -123,7 +130,7 @@ BOOLEAN SymInfoDump_DumpPointerType( ULONG TypeIndex = 0; ULONG64 Length = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagPointerType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagPointerType)) return FALSE; // Type index ("typeId" in DIA) @@ -144,7 +151,7 @@ BOOLEAN SymInfoDump_DumpPointerType( return TRUE; } -BOOLEAN SymInfoDump_DumpTypedef( +BOOLEAN SymbolInfo_DumpTypedef( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ TypedefInfo* Info @@ -153,7 +160,7 @@ BOOLEAN SymInfoDump_DumpTypedef( ULONG typeIndex = 0; PWSTR symbolName = NULL; - if (!SymInfoDump_CheckTag(Context, Index, SymTagTypedef)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) return FALSE; // Type index ("typeId" in DIA) @@ -173,7 +180,7 @@ BOOLEAN SymInfoDump_DumpTypedef( return TRUE; } -BOOLEAN SymInfoDump_DumpEnum( +BOOLEAN SymbolInfo_DumpEnum( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ EnumInfo* Info @@ -183,7 +190,7 @@ BOOLEAN SymInfoDump_DumpEnum( ULONG Nested = 0; PWSTR symbolName = NULL; - if (!SymInfoDump_CheckTag(Context, Index, SymTagEnum)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) return FALSE; // Name ("name" in DIA) @@ -203,7 +210,7 @@ BOOLEAN SymInfoDump_DumpEnum( return FALSE; // Enumerators - if (!SymInfoDump_Enumerators(Context, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) + if (!SymbolInfo_Enumerators(Context, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) return FALSE; Info->TypeIndex = TypeIndex; @@ -212,7 +219,7 @@ BOOLEAN SymInfoDump_DumpEnum( return TRUE; } -BOOLEAN SymInfoDump_DumpArrayType( +BOOLEAN SymbolInfo_DumpArrayType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ArrayTypeInfo* Info @@ -223,11 +230,11 @@ BOOLEAN SymInfoDump_DumpArrayType( ULONG indexTypeIndex = 0; // Check if it is really SymTagArrayType - if (!SymInfoDump_CheckTag(Context, Index, SymTagArrayType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagArrayType)) return FALSE; // Element type index - if (!SymInfoDump_ArrayElementTypeIndex(Context, Index, &elementTypeIndex)) + if (!SymbolInfo_ArrayElementTypeIndex(Context, Index, &elementTypeIndex)) return FALSE; // Length ("length" in DIA) @@ -245,7 +252,7 @@ BOOLEAN SymInfoDump_DumpArrayType( if (length > 0) { // Dimensions - if (!SymInfoDump_ArrayDims( + if (!SymbolInfo_ArrayDims( Context, Index, Info->Dimensions, @@ -264,7 +271,7 @@ BOOLEAN SymInfoDump_DumpArrayType( return TRUE; } -BOOLEAN SymInfoDump_DumpUDT( +BOOLEAN SymbolInfo_DumpUDT( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ TypeInfo* Info @@ -273,7 +280,7 @@ BOOLEAN SymInfoDump_DumpUDT( ULONG UDTKind = 0; BOOLEAN result = FALSE; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; // Determine UDT kind (class/structure or union?) @@ -284,22 +291,22 @@ BOOLEAN SymInfoDump_DumpUDT( { case UdtStruct: Info->UdtKind = TRUE; - result = SymInfoDump_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + result = SymbolInfo_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); break; case UdtClass: Info->UdtKind = TRUE; - result = SymInfoDump_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + result = SymbolInfo_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); break; case UdtUnion: Info->UdtKind = FALSE; - result = SymInfoDump_DumpUDTUnion(Context, Index, &Info->sUdtUnionInfo); + result = SymbolInfo_DumpUDTUnion(Context, Index, &Info->sUdtUnionInfo); break; } return result; } -BOOLEAN SymInfoDump_DumpUDTClass( +BOOLEAN SymbolInfo_DumpUDTClass( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ UdtClassInfo* Info @@ -310,7 +317,7 @@ BOOLEAN SymInfoDump_DumpUDTClass( ULONG64 Length = 0; ULONG Nested = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; // Check if it is really a class or structure UDT ? @@ -337,15 +344,15 @@ BOOLEAN SymInfoDump_DumpUDTClass( return FALSE; // Member variables - if (!SymInfoDump_UdtVariables(Context, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) + if (!SymbolInfo_UdtVariables(Context, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) return FALSE; // Member functions - if (!SymInfoDump_UdtFunctions(Context, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) + if (!SymbolInfo_UdtFunctions(Context, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) return FALSE; // Base classes - if (!SymInfoDump_UdtBaseClasses(Context, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) + if (!SymbolInfo_UdtBaseClasses(Context, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) return FALSE; Info->UDTKind = (UdtKind)UDTKind; @@ -355,7 +362,7 @@ BOOLEAN SymInfoDump_DumpUDTClass( return TRUE; } -BOOLEAN SymInfoDump_DumpUDTUnion( +BOOLEAN SymbolInfo_DumpUDTUnion( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ UdtUnionInfo *Info @@ -366,7 +373,7 @@ BOOLEAN SymInfoDump_DumpUDTUnion( ULONG64 Length = 0; ULONG Nested = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; // Check if it is really a union UDT ? @@ -393,7 +400,7 @@ BOOLEAN SymInfoDump_DumpUDTUnion( return FALSE; // Union members - if (!SymInfoDump_UdtUnionMembers(Context, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) + if (!SymbolInfo_UdtUnionMembers(Context, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) return FALSE; Info->UDTKind = (UdtKind)UDTKind; @@ -403,7 +410,7 @@ BOOLEAN SymInfoDump_DumpUDTUnion( return TRUE; } -BOOLEAN SymInfoDump_DumpFunctionType( +BOOLEAN SymbolInfo_DumpFunctionType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ FunctionTypeInfo *Info @@ -414,7 +421,7 @@ BOOLEAN SymInfoDump_DumpFunctionType( ULONG CallConv = 0; ULONG ClassIndex = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) return FALSE; // Index of the return type symbol ("typeId" in DIA) @@ -479,7 +486,7 @@ BOOLEAN SymInfoDump_DumpFunctionType( */ // Dump function arguments - if (!SymInfoDump_FunctionArguments(Context, Index, Info->Args, &Info->NumArgs, ARRAYSIZE(Info->Args))) + if (!SymbolInfo_FunctionArguments(Context, Index, Info->Args, &Info->NumArgs, ARRAYSIZE(Info->Args))) return FALSE; // Is the function static ? (If it is a member function) @@ -496,7 +503,7 @@ BOOLEAN SymInfoDump_DumpFunctionType( return TRUE; } -BOOLEAN SymInfoDump_DumpFunctionArgType( +BOOLEAN SymbolInfo_DumpFunctionArgType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ FunctionArgTypeInfo *Info @@ -504,7 +511,7 @@ BOOLEAN SymInfoDump_DumpFunctionArgType( { ULONG typeIndex = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionArgType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionArgType)) return FALSE; // Index of the argument type ("typeId" in DIA) @@ -516,7 +523,7 @@ BOOLEAN SymInfoDump_DumpFunctionArgType( return TRUE; } -BOOLEAN SymInfoDump_DumpBaseClass( +BOOLEAN SymbolInfo_DumpBaseClass( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ BaseClassInfo *Info @@ -525,7 +532,7 @@ BOOLEAN SymInfoDump_DumpBaseClass( ULONG typeIndex = 0; ULONG virtualBase = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagBaseClass)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagBaseClass)) return FALSE; // Base class UDT @@ -597,7 +604,7 @@ BOOLEAN SymInfoDump_DumpBaseClass( return TRUE; } -BOOLEAN SymInfoDump_DumpData( +BOOLEAN SymbolInfo_DumpData( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ DataInfo *Info @@ -607,7 +614,7 @@ BOOLEAN SymInfoDump_DumpData( ULONG TypeIndex = 0; ULONG dataKind = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagData)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagData)) return FALSE; // Name ("name" in DIA) @@ -677,7 +684,7 @@ BOOLEAN SymInfoDump_DumpData( return TRUE; } -BOOLEAN SymInfoDump_DumpType( +BOOLEAN SymbolInfo_DumpType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ TypeInfo *Info @@ -704,41 +711,41 @@ BOOLEAN SymInfoDump_DumpType( switch (tag) { case SymTagBaseType: - result = SymInfoDump_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); + result = SymbolInfo_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); break; case SymTagPointerType: - result = SymInfoDump_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); + result = SymbolInfo_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); break; case SymTagTypedef: - result = SymInfoDump_DumpTypedef(Context, Index, &Info->sTypedefInfo); + result = SymbolInfo_DumpTypedef(Context, Index, &Info->sTypedefInfo); break; case SymTagEnum: - result = SymInfoDump_DumpEnum(Context, Index, &Info->sEnumInfo); + result = SymbolInfo_DumpEnum(Context, Index, &Info->sEnumInfo); break; case SymTagArrayType: - result = SymInfoDump_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); + result = SymbolInfo_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); break; case SymTagUDT: - result = SymInfoDump_DumpUDT(Context, Index, Info); + result = SymbolInfo_DumpUDT(Context, Index, Info); break; case SymTagFunctionType: - result = SymInfoDump_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); + result = SymbolInfo_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); break; case SymTagFunctionArgType: - result = SymInfoDump_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); + result = SymbolInfo_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); break; case SymTagBaseClass: - result = SymInfoDump_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); + result = SymbolInfo_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); break; case SymTagData: - result = SymInfoDump_DumpData(Context, Index, &Info->sDataInfo); + result = SymbolInfo_DumpData(Context, Index, &Info->sDataInfo); break; } return result; } -BOOLEAN SymInfoDump_DumpSymbolType( +BOOLEAN SymbolInfo_DumpSymbolType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ TypeInfo *Info, @@ -750,10 +757,10 @@ BOOLEAN SymInfoDump_DumpSymbolType( return FALSE; // Dump the type symbol - return SymInfoDump_DumpType(Context, *TypeIndex, Info); + return SymbolInfo_DumpType(Context, *TypeIndex, Info); } -BOOLEAN SymInfoDump_CheckTag( +BOOLEAN SymbolInfo_CheckTag( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _In_ ULONG Tag @@ -761,21 +768,13 @@ BOOLEAN SymInfoDump_CheckTag( { ULONG symTag = SymTagNull; - if (!SymGetTypeInfo_I( - NtCurrentProcess(), - Context->BaseAddress, - Index, - TI_GET_SYMTAG, - &symTag - )) - { + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMTAG, &symTag)) return FALSE; - } return symTag == Tag; } -BOOLEAN SymInfoDump_SymbolSize( +BOOLEAN SymbolInfo_SymbolSize( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG64* Size @@ -793,7 +792,7 @@ BOOLEAN SymInfoDump_SymbolSize( else { // No, it does not - it can be SymTagTypedef - if (!SymInfoDump_CheckTag(Context, Index, SymTagTypedef)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) { // No, this symbol does not have length return FALSE; @@ -812,7 +811,7 @@ BOOLEAN SymInfoDump_SymbolSize( index = tempIndex; - } while (SymInfoDump_CheckTag(Context, index, SymTagTypedef)); + } while (SymbolInfo_CheckTag(Context, index, SymTagTypedef)); // And get the length if (SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_LENGTH, &length)) @@ -826,7 +825,7 @@ BOOLEAN SymInfoDump_SymbolSize( return FALSE; } -BOOLEAN SymInfoDump_ArrayElementTypeIndex( +BOOLEAN SymbolInfo_ArrayElementTypeIndex( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG ArrayIndex, _Inout_ ULONG* ElementTypeIndex @@ -835,7 +834,7 @@ BOOLEAN SymInfoDump_ArrayElementTypeIndex( ULONG index; ULONG elementIndex = 0; - if (!SymInfoDump_CheckTag(Context, ArrayIndex, SymTagArrayType)) + if (!SymbolInfo_CheckTag(Context, ArrayIndex, SymTagArrayType)) return FALSE; // Get the array element type @@ -845,7 +844,7 @@ BOOLEAN SymInfoDump_ArrayElementTypeIndex( // If the array element type is SymTagArrayType, skip to its type index = elementIndex; - while (SymInfoDump_CheckTag(Context, elementIndex, SymTagArrayType)) + while (SymbolInfo_CheckTag(Context, elementIndex, SymTagArrayType)) { if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, &elementIndex)) return FALSE; @@ -859,7 +858,7 @@ BOOLEAN SymInfoDump_ArrayElementTypeIndex( return TRUE; } -BOOLEAN SymInfoDump_ArrayDims( +BOOLEAN SymbolInfo_ArrayDims( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG64* pDims, @@ -870,7 +869,7 @@ BOOLEAN SymInfoDump_ArrayDims( ULONG index; ULONG dimCount; - if (!SymInfoDump_CheckTag(Context, Index, SymTagArrayType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagArrayType)) return FALSE; if (MaxDims <= 0) @@ -901,7 +900,7 @@ BOOLEAN SymInfoDump_ArrayDims( } // Size of its type - if (!SymInfoDump_SymbolSize(Context, typeIndex, &typeSize) || (typeSize == 0)) + if (!SymbolInfo_SymbolSize(Context, typeIndex, &typeSize) || (typeSize == 0)) return FALSE; // Size of the dimension @@ -925,7 +924,7 @@ BOOLEAN SymInfoDump_ArrayDims( } */ // If the type symbol is not SymTagArrayType, we are done - if (!SymInfoDump_CheckTag(Context, typeIndex, SymTagArrayType)) + if (!SymbolInfo_CheckTag(Context, typeIndex, SymTagArrayType)) break; index = typeIndex; @@ -939,7 +938,7 @@ BOOLEAN SymInfoDump_ArrayDims( return TRUE; } -BOOLEAN SymInfoDump_UdtVariables( +BOOLEAN SymbolInfo_UdtVariables( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG* pVars, @@ -949,7 +948,7 @@ BOOLEAN SymInfoDump_UdtVariables( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; if (MaxVars <= 0) @@ -979,7 +978,7 @@ BOOLEAN SymInfoDump_UdtVariables( // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) { pVars[*Vars] = params->ChildId[i]; @@ -993,7 +992,7 @@ BOOLEAN SymInfoDump_UdtVariables( return TRUE; } -BOOLEAN SymInfoDump_UdtFunctions( +BOOLEAN SymbolInfo_UdtFunctions( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG* pFuncs, @@ -1003,7 +1002,7 @@ BOOLEAN SymInfoDump_UdtFunctions( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; if (MaxFuncs <= 0) @@ -1034,7 +1033,7 @@ BOOLEAN SymInfoDump_UdtFunctions( // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagFunction)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagFunction)) { pFuncs[*Funcs] = params->ChildId[i]; @@ -1048,7 +1047,7 @@ BOOLEAN SymInfoDump_UdtFunctions( return TRUE; } -BOOLEAN SymInfoDump_UdtBaseClasses( +BOOLEAN SymbolInfo_UdtBaseClasses( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG* pBases, @@ -1058,7 +1057,7 @@ BOOLEAN SymInfoDump_UdtBaseClasses( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; if (MaxBases <= 0) @@ -1089,7 +1088,7 @@ BOOLEAN SymInfoDump_UdtBaseClasses( // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagBaseClass)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagBaseClass)) { pBases[*Bases] = params->ChildId[i]; @@ -1103,7 +1102,7 @@ BOOLEAN SymInfoDump_UdtBaseClasses( return TRUE; } -BOOLEAN SymInfoDump_UdtUnionMembers( +BOOLEAN SymbolInfo_UdtUnionMembers( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG* pMembers, @@ -1113,7 +1112,7 @@ BOOLEAN SymInfoDump_UdtUnionMembers( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagUDT)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; if (MaxMembers <= 0) @@ -1144,7 +1143,7 @@ BOOLEAN SymInfoDump_UdtUnionMembers( // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) { pMembers[*Members] = params->ChildId[i]; @@ -1158,7 +1157,7 @@ BOOLEAN SymInfoDump_UdtUnionMembers( return TRUE; } -BOOLEAN SymInfoDump_FunctionArguments( +BOOLEAN SymbolInfo_FunctionArguments( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG* pArgs, @@ -1168,7 +1167,7 @@ BOOLEAN SymInfoDump_FunctionArguments( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagFunctionType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) return FALSE; if (MaxArgs <= 0) @@ -1199,7 +1198,7 @@ BOOLEAN SymInfoDump_FunctionArguments( // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagFunctionArgType)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagFunctionArgType)) { pArgs[*Args] = params->ChildId[i]; @@ -1213,7 +1212,7 @@ BOOLEAN SymInfoDump_FunctionArguments( return TRUE; } -BOOLEAN SymInfoDump_Enumerators( +BOOLEAN SymbolInfo_Enumerators( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG *pEnums, @@ -1223,7 +1222,7 @@ BOOLEAN SymInfoDump_Enumerators( { ULONG childrenLength = 0; - if (!SymInfoDump_CheckTag(Context, Index, SymTagEnum)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) return FALSE; if (MaxEnums <= 0) @@ -1254,7 +1253,7 @@ BOOLEAN SymInfoDump_Enumerators( // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymInfoDump_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) { pEnums[*Enums] = params->ChildId[i]; @@ -1268,7 +1267,7 @@ BOOLEAN SymInfoDump_Enumerators( return TRUE; } -BOOLEAN SymInfoDump_TypeDefType( +BOOLEAN SymbolInfo_TypeDefType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG *UndTypeIndex @@ -1276,13 +1275,13 @@ BOOLEAN SymInfoDump_TypeDefType( { ULONG index; - if (!SymInfoDump_CheckTag(Context, Index, SymTagTypedef)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) return FALSE; // Skip to the type behind the type definition index = Index; - while (SymInfoDump_CheckTag(Context, index, SymTagTypedef)) + while (SymbolInfo_CheckTag(Context, index, SymTagTypedef)) { if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) return FALSE; @@ -1293,7 +1292,7 @@ BOOLEAN SymInfoDump_TypeDefType( return TRUE; } -BOOLEAN SymInfoDump_PointerType( +BOOLEAN SymbolInfo_PointerType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _Inout_ ULONG *UndTypeIndex, @@ -1302,14 +1301,14 @@ BOOLEAN SymInfoDump_PointerType( { ULONG index; - if (!SymInfoDump_CheckTag(Context, Index, SymTagPointerType)) + if (!SymbolInfo_CheckTag(Context, Index, SymTagPointerType)) return FALSE; // Skip to the type pointer points to *NumPointers = 0; index = Index; - while (SymInfoDump_CheckTag(Context, index, SymTagPointerType)) + while (SymbolInfo_CheckTag(Context, index, SymTagPointerType)) { if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) return FALSE; @@ -1322,7 +1321,7 @@ BOOLEAN SymInfoDump_PointerType( return TRUE; } -BOOLEAN SymInfoDump_GetTypeNameHelper( +BOOLEAN SymbolInfo_GetTypeNameHelper( _In_ ULONG Index, _Inout_ PPDB_SYMBOL_CONTEXT Obj, _Out_ PWSTR *VarName, @@ -1332,7 +1331,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( TypeInfo Info; // Get the type information - if (!SymInfoDump_DumpType(Obj, Index, &Info)) + if (!SymbolInfo_DumpType(Obj, Index, &Info)) { return FALSE; } @@ -1346,11 +1345,11 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( // Yes, get the number of * to show ULONG typeIndex = 0; - if (!SymInfoDump_PointerType(Obj, Index, &typeIndex, &numPointers)) + if (!SymbolInfo_PointerType(Obj, Index, &typeIndex, &numPointers)) return FALSE; // Get more information about the type the pointer points to - if (!SymInfoDump_DumpType(Obj, typeIndex, &Info)) + if (!SymbolInfo_DumpType(Obj, typeIndex, &Info)) return FALSE; // Save the index of the type the pointer points to @@ -1362,7 +1361,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( switch (Info.Tag) { case SymTagBaseType: - *TypeName = SymInfoDump_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length); + *TypeName = SymbolInfo_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length); break; case SymTagTypedef: @@ -1399,12 +1398,12 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( } // Print return value - if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) return FALSE; // Print calling convention //TypeName += " "; - //TypeName += SymInfoDump_CallConvStr(Info.Info.sFunctionTypeInfo.CallConv); + //TypeName += SymbolInfo_CallConvStr(Info.Info.sFunctionTypeInfo.CallConv); //TypeName += " "; // If member function, save the class type index @@ -1415,7 +1414,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( /* // It is not needed to print the class name here, because // it is contained in the function name - if( !SymInfoDump_GetTypeNameHelper( Info.Info.sFunctionTypeInfo.ClassIndex, Obj, VarName, TypeName ) ) + if( !SymbolInfo_GetTypeNameHelper( Info.Info.sFunctionTypeInfo.ClassIndex, Obj, VarName, TypeName ) ) return false; TypeName += "::"); @@ -1428,7 +1427,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( //TypeName += " ("; for (ULONG i = 0; i < Info.sFunctionTypeInfo.NumArgs; i++) { - if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionTypeInfo.Args[i], Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.Args[i], Obj, VarName, TypeName)) return FALSE; //if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) @@ -1452,7 +1451,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( case SymTagFunctionArgType: { - if (!SymInfoDump_GetTypeNameHelper(Info.sFunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) return FALSE; } break; @@ -1460,7 +1459,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( case SymTagArrayType: { // Print element type name - if (!SymInfoDump_GetTypeNameHelper(Info.sArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.sArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) return FALSE; //TypeName += " "; @@ -1497,7 +1496,7 @@ BOOLEAN SymInfoDump_GetTypeNameHelper( return TRUE; } -BOOLEAN SymInfoDump_GetTypeName( +BOOLEAN SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, _In_ PWSTR pVarName, @@ -1515,7 +1514,7 @@ BOOLEAN SymInfoDump_GetTypeName( VarName = pVarName; // Obtain the type name - if (!SymInfoDump_GetTypeNameHelper(Index, Context, &VarName, &TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Index, Context, &VarName, &TypeName)) return FALSE; if (!wcslen(TypeName)) @@ -1529,7 +1528,7 @@ BOOLEAN SymInfoDump_GetTypeName( /////////////////////////////////////////////////////////////////////////////// // Data-to-string conversion functions -PWSTR SymInfoDump_TagStr( +PWSTR SymbolInfo_TagStr( _In_ enum SymTagEnum Tag ) { @@ -1603,7 +1602,7 @@ PWSTR SymInfoDump_TagStr( } -PWSTR SymInfoDump_BaseTypeStr( +PWSTR SymbolInfo_BaseTypeStr( _In_ BasicType Type, _In_ ULONG64 Length ) @@ -1694,7 +1693,7 @@ PWSTR SymInfoDump_BaseTypeStr( } -PWSTR SymInfoDump_CallConvStr( +PWSTR SymbolInfo_CallConvStr( _In_ CV_call_e CallConv ) { @@ -1752,7 +1751,7 @@ PWSTR SymInfoDump_CallConvStr( } -PWSTR SymInfoDump_DataKindFromSymbolInfo( +PWSTR SymbolInfo_DataKindFromSymbolInfo( _In_ PSYMBOL_INFOW SymbolInfo ) { @@ -1761,10 +1760,10 @@ PWSTR SymInfoDump_DataKindFromSymbolInfo( if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) return L"UNKNOWN"; - return SymInfoDump_DataKindStr(dataKindType); + return SymbolInfo_DataKindStr(dataKindType); } -PWSTR SymInfoDump_DataKindStr( +PWSTR SymbolInfo_DataKindStr( _In_ DataKind SymDataKind ) { @@ -1793,14 +1792,14 @@ PWSTR SymInfoDump_DataKindStr( return L"UNKNOWN"; } -VOID SymInfoDump_SymbolLocationStr( +VOID SymbolInfo_SymbolLocationStr( _In_ PSYMBOL_INFOW SymbolInfo, _In_ PWSTR Buffer ) { if (SymbolInfo->Flags & SYMFLAG_REGISTER) { - PWSTR regString = SymInfoDump_RegisterStr((CV_HREG_e)SymbolInfo->Register); + PWSTR regString = SymbolInfo_RegisterStr(SymbolInfo->Register); if (regString) wcscpy(Buffer, regString); @@ -1812,7 +1811,7 @@ VOID SymInfoDump_SymbolLocationStr( else if (SymbolInfo->Flags & SYMFLAG_REGREL) { WCHAR szReg[32]; - PWSTR regString = SymInfoDump_RegisterStr((CV_HREG_e)SymbolInfo->Register); + PWSTR regString = SymbolInfo_RegisterStr(SymbolInfo->Register); if (regString) wcscpy(szReg, regString); @@ -1830,12 +1829,11 @@ VOID SymInfoDump_SymbolLocationStr( } else { - //_swprintf(Buffer, L"%16I64x", SymbolInfo->Address); PhPrintPointer(Buffer, PTR_SUB_OFFSET(SymbolInfo->Address, SymbolInfo->ModBase)); } } -PWSTR SymInfoDump_RegisterStr( +PWSTR SymbolInfo_RegisterStr( _In_ CV_HREG_e RegCode ) { @@ -1862,7 +1860,7 @@ PWSTR SymInfoDump_RegisterStr( return L"Unknown"; } -PWSTR SymInfoDump_UdtKindStr( +PWSTR SymbolInfo_UdtKindStr( _In_ UdtKind KindType ) { @@ -1879,7 +1877,7 @@ PWSTR SymInfoDump_UdtKindStr( return L"UNKNOWN"; } -PWSTR SymInfoDump_LocationTypeStr( +PWSTR SymbolInfo_LocationTypeStr( _In_ LocationType LocType ) { @@ -1952,11 +1950,11 @@ VOID PrintDataInfo( return; // Type - symDataKind = SymInfoDump_DataKindFromSymbolInfo(SymbolInfo); + symDataKind = SymbolInfo_DataKindFromSymbolInfo(SymbolInfo); lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, symDataKind, NULL); // Address - SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); // Name @@ -1966,7 +1964,7 @@ VOID PrintDataInfo( PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); // Data - //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); // Flags: %x, SymbolInfo->Flags @@ -1988,7 +1986,7 @@ VOID PrintFunctionInfo( lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"FUNCTION", NULL); // Address - SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); // Name @@ -1998,7 +1996,7 @@ VOID PrintFunctionInfo( PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); // Data - //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); // Flags: %x, SymbolInfo->Flags @@ -2007,8 +2005,8 @@ VOID PrintFunctionInfo( // Enumerate function parameters and local variables... //IMAGEHLP_STACK_FRAME sf; //sf.InstructionOffset = SymbolInfo->Address; - //SymSetContext(NtCurrentProcess(), &sf, 0); - //SymEnumSymbolsW(NtCurrentProcess(), 0, NULL, EnumCallbackProc, pTypeInfo); + //SymSetContext_I(NtCurrentProcess(), &sf, 0); + //SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); } VOID PrintDefaultInfo( @@ -2026,7 +2024,7 @@ VOID PrintDefaultInfo( lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"SYMBOL", NULL); // Address - SymInfoDump_SymbolLocationStr(SymbolInfo, pointer); + SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); // Name @@ -2036,7 +2034,7 @@ VOID PrintDefaultInfo( PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); // Data - //if (SymInfoDump_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) + //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); // Flags: %x, SymbolInfo->Flags @@ -2050,7 +2048,7 @@ VOID PrintClassInfo( ) { // UDT kind - //OutputDebugString(PhaFormatString(L"%s", SymInfoDump_UdtKindStr(Info->UDTKind))->Buffer); + //OutputDebugString(PhaFormatString(L"%s", SymbolInfo_UdtKindStr(Info->UDTKind))->Buffer); // Name //OutputDebugString(PhaFormatString(L" %s \n", Info->Name)->Buffer); // Size @@ -2072,9 +2070,9 @@ VOID PrintClassInfo( { TypeInfo VarInfo; - if (!SymInfoDump_DumpType(Context, Info->Variables[i], &VarInfo)) + if (!SymbolInfo_DumpType(Context, Info->Variables[i], &VarInfo)) { - //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); // Continue with the next variable } else if (VarInfo.Tag != SymTagData) @@ -2095,7 +2093,7 @@ VOID PrintClassInfo( //#define cMaxTypeNameLen 1024 //TCHAR szTypeName[cMaxTypeNameLen + 1] = _T(""); - //if (SymInfoDump_GetTypeName(VarInfo.Info.sDataInfo.TypeIndex, W2T(VarInfo.Info.sDataInfo.Name), szTypeName, cMaxTypeNameLen)) + //if (SymbolInfo_GetTypeName(VarInfo.Info.sDataInfo.TypeIndex, W2T(VarInfo.Info.sDataInfo.Name), szTypeName, cMaxTypeNameLen)) //_tprintf(szTypeName); //else //_tprintf(_T("n/a")); @@ -2141,9 +2139,9 @@ VOID PrintClassInfo( { TypeInfo baseInfo; - if (!SymInfoDump_DumpType(Context, Info->BaseClasses[i], &baseInfo)) + if (!SymbolInfo_DumpType(Context, Info->BaseClasses[i], &baseInfo)) { - //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); // Continue with the next base class } else if (baseInfo.Tag != SymTagBaseClass) @@ -2156,9 +2154,9 @@ VOID PrintClassInfo( // Obtain the name of the base class TypeInfo BaseUdtInfo; - if (!SymInfoDump_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &BaseUdtInfo)) + if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &BaseUdtInfo)) { - //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); // Continue with the next base class } else if (BaseUdtInfo.Tag != SymTagUDT) @@ -2194,7 +2192,7 @@ VOID PrintUserDefinedTypes( { index = PtrToUlong(Context->UdtList->Items[i]); - if (SymInfoDump_DumpType(Context, index, &info)) + if (SymbolInfo_DumpType(Context, index, &info)) { if (info.Tag == SymTagUDT) { @@ -2208,9 +2206,9 @@ VOID PrintUserDefinedTypes( { TypeInfo baseInfo; - if (!SymInfoDump_DumpType(Context, info.sUdtClassInfo.BaseClasses[i], &baseInfo)) + if (!SymbolInfo_DumpType(Context, info.sUdtClassInfo.BaseClasses[i], &baseInfo)) { - //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); // Continue with the next base class } else if (baseInfo.Tag != SymTagBaseClass) @@ -2223,9 +2221,9 @@ VOID PrintUserDefinedTypes( // Obtain information about the base class TypeInfo baseUdtInfo; - if (!SymInfoDump_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo)) + if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo)) { - //_ASSERTE(!_T("SymInfoDump::DumpType() failed.")); + //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); // Continue with the next base class } else if (baseUdtInfo.Tag != SymTagUDT) @@ -2443,7 +2441,6 @@ VOID ShowSymbolInfo( //OutputDebugString(PhaFormatString(L"Public symbols: %s\n", info.Publics ? L"Available" : L"Not available")->Buffer); } - VOID PeDumpFileSymbols( _In_ HWND ListViewHandle, _In_ PWSTR FileName @@ -2451,12 +2448,12 @@ VOID PeDumpFileSymbols( { HMODULE dbghelpHandle; HMODULE symsrvHandle; - ULONG64 symbolBaseAddress = 0; + ULONG64 symbolBaseAddress; ULONG64 baseAddress = 0; ULONG fileLength = 0; PdbLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); - + if (!(dbghelpHandle = GetModuleHandle(L"dbghelp.dll"))) return; @@ -2470,8 +2467,9 @@ VOID PeDumpFileSymbols( SymLoadModuleExW_I = PhGetProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); SymGetModuleInfoW64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetModuleInfoW64", 0); SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); + SymSetContext_I = PhGetProcedureAddress(dbghelpHandle, "SymSetContext", 0); - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME); // SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED + SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME | SYMOPT_ALLOW_ZERO_ADDRESS); // SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED if (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) return; @@ -2498,7 +2496,7 @@ VOID PeDumpFileSymbols( context.BaseAddress = symbolBaseAddress; context.UdtList = PhCreateList(0x100); - ShowSymbolInfo(symbolBaseAddress); + //ShowSymbolInfo(symbolBaseAddress); SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, &context); @@ -2560,7 +2558,7 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( HWND lvHandle; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetListViewStyle(lvHandle, TRUE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"Type"); PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 80, L"VA"); diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 36db629c6fb6..73ee2331031a 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -23,57 +23,19 @@ #include #include -#include #include #include #include -#include #include -#include - -BOOLEAN PvpLoadDbgHelp( - _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider - ); #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 +BOOLEAN PvpLoadDbgHelp( + _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider ); -INT_PTR CALLBACK PvpPeCgfDlgProc( +INT_PTR CALLBACK PvpPeGeneralDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -81,13 +43,12 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( ); PH_MAPPED_IMAGE PvMappedImage; -PIMAGE_COR20_HEADER PvImageCor20Header; -PPH_SYMBOL_PROVIDER symbolProvider; - -HICON PvImageLargeIcon; -PH_IMAGE_VERSION_INFO PvImageVersionInfo; -VERIFY_RESULT PvImageVerifyResult; -PPH_STRING PvImageSignerName; +PIMAGE_COR20_HEADER PvImageCor20Header = NULL; +PPH_SYMBOL_PROVIDER PvSymbolProvider = NULL; +static HICON PvImageLargeIcon; +static PH_IMAGE_VERSION_INFO PvImageVersionInfo; +static VERIFY_RESULT PvImageVerifyResult; +static PPH_STRING PvImageSignerName; VOID PvPeProperties( VOID @@ -110,9 +71,17 @@ VOID PvPeProperties( return; } - // Failing to load dbghelp is not critical : it just won't be possible - // to look up symbol names. No need to check the returned value. - PvpLoadDbgHelp(&symbolProvider); + if (PvpLoadDbgHelp(&PvSymbolProvider)) + { + // Load current PE pdb + // TODO: Move into seperate thread. + PhLoadModuleSymbolProvider( + PvSymbolProvider, + PvFileName->Buffer, + (ULONG64)PvMappedImage.NtHeaders->OptionalHeader.ImageBase, + PvMappedImage.NtHeaders->OptionalHeader.SizeOfImage + ); + } if (propContext = PvCreatePropContext(PvFileName)) { @@ -204,7 +173,7 @@ VOID PvPeProperties( PvAddPropPage(propContext, newPage); } - // PDB page + // Symbols page { newPage = PvCreatePropPageContext( MAKEINTRESOURCE(IDD_PESYMBOLS), @@ -691,420 +660,6 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( return FALSE; } -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; - - 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 = PhFormatString(L"%S (Delay)", importDll.Name); - else - name = PhZeroExtendToUtf16(importDll.Name); - - PhPrintUInt64(number, ++(*Count)); // HACK - lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); - - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); - PhDereferenceObject(name); - - if (importEntry.Name) - { - PPH_STRING importName = NULL; - - if (importEntry.Name[0] == '?') - importName = PhUndecorateName(symbolProvider->ProcessHandle, importEntry.Name); - else - importName = PhZeroExtendToUtf16(importEntry.Name); - - if (!importName) - importName = PhZeroExtendToUtf16(importEntry.Name); - - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, importName->Buffer); - PhDereferenceObject(importName); - - PhPrintUInt32(number, importEntry.NameHint); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, number); - } - else - { - name = PhFormatString(L"(Ordinal %u)", importEntry.Ordinal); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, 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 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); - - 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); - - 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: - { - 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, 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"); - 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]; - - PhPrintUInt64(number, i + 1); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); - - if (exportFunction.ForwardedName) - { - PPH_STRING forwardName = NULL; - - if (exportFunction.ForwardedName[0] == '?') - forwardName = PhUndecorateName(symbolProvider->ProcessHandle, exportFunction.ForwardedName); - else - forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); - - if (!forwardName) - forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); - - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, forwardName->Buffer); - PhDereferenceObject(forwardName); - } - else - { - PhPrintPointer(pointer, exportFunction.Function); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); - } - - if (exportEntry.Name) - { - PPH_STRING exportName = NULL; - - if (exportEntry.Name[0] == '?') - exportName = PhUndecorateName(symbolProvider->ProcessHandle, exportEntry.Name); - else - exportName = PhZeroExtendToUtf16(exportEntry.Name); - - if (!exportName) - exportName = PhZeroExtendToUtf16(exportEntry.Name); - - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, exportName->Buffer); - PhDereferenceObject(exportName); - } - else - { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); - } - - PhPrintUInt32(number, exportEntry.Ordinal); - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, 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; - } - - 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, 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_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_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; - } - - return FALSE; -} - VOID PvpLoadDbgHelpFromPath( _In_ PWSTR DbgHelpPath ) @@ -1183,293 +738,4 @@ BOOLEAN PvpLoadDbgHelp( *SymbolProvider = symbolProvider; return TRUE; -} - -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; -} - -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); - - // 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 }; - 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 (!(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_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; - } - - return FALSE; -} +} \ No newline at end of file diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 65d0ed06345b..54d1bce3d038 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -1,4 +1,4 @@ - + @@ -191,6 +191,11 @@ + + + + + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index 87d782e3bcb4..a073793965cd 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -1,67 +1,82 @@ - - - - - {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 - - - Source Files - - - Source Files - - - - - Header 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 + + + + + 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 + + + + + Resource Files + + + Resource Files + + + + + Resource Files + + \ No newline at end of file From 1b1d08c37faf541ae9028c8f82073b26f31188d3 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 00:58:15 +1000 Subject: [PATCH 097/839] Fix file encoding --- ProcessHacker.sln | 140 +-- ProcessHacker/ProcessHacker.vcxproj | 2 +- ProcessHacker/ProcessHacker.vcxproj.filters | 1226 +++++++++---------- plugins/Plugins.sln | 2 +- tools/CustomSignTool/CustomSignTool.vcxproj | 2 +- 5 files changed, 686 insertions(+), 686 deletions(-) diff --git a/ProcessHacker.sln b/ProcessHacker.sln index 490ed238ad0c..b9f1116bcef8 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -1,70 +1,70 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 -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("{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 - {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} - {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.26403.7 +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("{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 + {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} + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} = {2758DC86-368B-430C-9D29-F1EF20032A71} + EndGlobalSection +EndGlobal diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index f212a670d21b..6273867a710a 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 671c41cb8543..5517737409be 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -1,614 +1,614 @@ - - - - - {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 - - - - - 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 - - - Resources - - - Resources - - - Module - - - - - Resources - - - Resources - - - - - Resources - - - - - Resources - - - 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 + + + + + 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 + + + Resources + + + Resources + + + Module + + + + + Resources + + + Resources + + + + + Resources + + + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + \ No newline at end of file diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 931a8541035a..15c2c8271d2b 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26403.7 diff --git a/tools/CustomSignTool/CustomSignTool.vcxproj b/tools/CustomSignTool/CustomSignTool.vcxproj index 9e345451a89b..c8a08527627b 100644 --- a/tools/CustomSignTool/CustomSignTool.vcxproj +++ b/tools/CustomSignTool/CustomSignTool.vcxproj @@ -1,4 +1,4 @@ - + From caa7280f544b1696a48b01c7878f48c9ef31ad09 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 01:27:09 +1000 Subject: [PATCH 098/839] peview: Add CLR sections --- tools/peview/clrprp.c | 108 ++++++++++++++++++++++++++++++++--------- tools/peview/peview.rc | 2 + 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/tools/peview/clrprp.c b/tools/peview/clrprp.c index d0c46d7bccf6..8584c433bdb2 100644 --- a/tools/peview/clrprp.c +++ b/tools/peview/clrprp.c @@ -23,6 +23,39 @@ #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]; // dmex: added for convenience. +} 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 + INT_PTR CALLBACK PvpPeClrDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -40,12 +73,21 @@ INT_PTR CALLBACK PvpPeClrDlgProc( { case WM_INITDIALOG: { - PH_STRING_BUILDER stringBuilder; + HWND lvHandle; PPH_STRING string; - PVOID metaData; - ULONG versionStringLength; - - string = PhaFormatString(L"%u.%u", PvImageCor20Header->MajorRuntimeVersion, + PH_STRING_BUILDER stringBuilder; + PSTORAGESIGNATURE metaData; + + 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"); + + string = PhaFormatString( + L"%u.%u", + PvImageCor20Header->MajorRuntimeVersion, PvImageCor20Header->MinorRuntimeVersion); SetDlgItemText(hwndDlg, IDC_RUNTIMEVERSION, string->Buffer); @@ -93,25 +135,43 @@ INT_PTR CALLBACK PvpPeClrDlgProc( } } - 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) + if (metaData && metaData->VersionLength != 0) { - string = PhZeroExtendToUtf16Ex((PCHAR)metaData + 12 + 4, versionStringLength); + string = PhZeroExtendToUtf16Ex((PCHAR)metaData->VersionString, metaData->VersionLength); SetDlgItemText(hwndDlg, IDC_VERSIONSTRING, string->Buffer); PhDereferenceObject(string); + + { + PSTORAGEHEADER storageHeader = PTR_ADD_OFFSET(metaData, (sizeof(STORAGESIGNATURE) - sizeof(UCHAR)) + metaData->VersionLength); + PSTORAGESTREAM streamHeader = PTR_ADD_OFFSET(storageHeader, sizeof(STORAGEHEADER)); + + for (USHORT 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(lvHandle, MAXINT, sectionName, NULL); + + PhPrintPointer(pointer, UlongToPtr(streamHeader->Offset)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PhaFormatSize(streamHeader->Size, -1)->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(FIELD_OFFSET(STORAGESTREAM, Name) + strlen(streamHeader->Name) + 1, 4)); + } + } } else { @@ -123,8 +183,12 @@ INT_PTR CALLBACK PvpPeClrDlgProc( { if (!propPageContext->LayoutInitialized) { - PvAddPropPageLayoutItem(hwndDlg, hwndDlg, + 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); diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 6151830379f2..ed559acfa245 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -192,6 +192,8 @@ BEGIN 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 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,57,286,216 + LTEXT "Sections:",IDC_STATIC,7,47,30,8 END IDD_PELOADCONFIG DIALOGEX 0, 0, 300, 280 From 9831e2550acf687dbe84b4375d4fa7267dd21545 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 12:42:01 +1000 Subject: [PATCH 099/839] peview: Fix loader config version check --- tools/peview/ldprp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/peview/ldprp.c b/tools/peview/ldprp.c index 22d6e24d5117..826ec0d6a711 100644 --- a/tools/peview/ldprp.c +++ b/tools/peview/ldprp.c @@ -67,7 +67,7 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( RtlSecondsSince1970ToTime((Config)->TimeDateStamp, &time); \ PhLargeIntegerToLocalSystemTime(&systemTime, &time); \ \ - ADD_VALUE(L"Time stamp", PhaFormatDateTime(&systemTime)->Buffer); \ + 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); \ @@ -85,7 +85,7 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( 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)) \ + if ((Config)->Size >= (ULONG)FIELD_OFFSET(Type, GuardCFCheckFunctionPointer)) \ { \ 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); \ @@ -101,7 +101,7 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( } \ if ((Config)->Size >= (ULONG)FIELD_OFFSET(Type, GuardLongJumpTargetTable) \ + sizeof((Config)->GuardLongJumpTargetTable) \ - + sizeof((Config)->GuardLongJumpTargetCount))\ + + 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); \ From 8f143ed66b4c98fa386e9817026b82a1770a0538 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 12:48:09 +1000 Subject: [PATCH 100/839] ExtendedTools: Update UnloadedModules window --- plugins/ExtendedTools/ExtendedTools.rc | 7 +++++- plugins/ExtendedTools/exttools.h | 7 +++++- plugins/ExtendedTools/main.c | 7 ++++-- plugins/ExtendedTools/unldll.c | 31 ++++++++++++++++++++++++-- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/plugins/ExtendedTools/ExtendedTools.rc b/plugins/ExtendedTools/ExtendedTools.rc index 6b90a093c160..6535eaeda856 100644 --- a/plugins/ExtendedTools/ExtendedTools.rc +++ b/plugins/ExtendedTools/ExtendedTools.rc @@ -88,7 +88,7 @@ END // IDD_UNLOADEDDLLS DIALOGEX 0, 0, 342, 265 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +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 @@ -526,6 +526,11 @@ BEGIN 0 END +IDD_UNLOADEDDLLS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index 47ddf23fb115..abff31baadd4 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -2,6 +2,7 @@ #define EXTTOOLS_H #include +#include #include #include #include @@ -22,8 +23,13 @@ extern HWND NetworkTreeNewHandle; #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 ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) +#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(NtCurrentPeb()->ImageBaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) +#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(NtCurrentPeb()->ImageBaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) // Graph update message @@ -536,7 +542,6 @@ BOOLEAN EtUiCancelIoThread( // unldll VOID EtShowUnloadedDllsDialog( - _In_ HWND ParentWindowHandle, _In_ PPH_PROCESS_ITEM ProcessItem ); diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 108a747d714e..22c279729158 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -86,7 +86,7 @@ VOID NTAPI MenuItemCallback( { case ID_PROCESS_UNLOADEDMODULES: { - EtShowUnloadedDllsDialog(PhMainWndHandle, menuItem->Context); + EtShowUnloadedDllsDialog(menuItem->Context); } break; case ID_PROCESS_WSWATCH: @@ -604,7 +604,10 @@ LOGICAL DllMain( { 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" } + { 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"" }, }; PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 5145c62348e4..54d9f4b8ae27 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -26,6 +26,7 @@ typedef struct _UNLOADED_DLLS_CONTEXT { PPH_PROCESS_ITEM ProcessItem; HWND ListViewHandle; + PH_LAYOUT_MANAGER LayoutManager; PVOID CapturedEventTrace; } UNLOADED_DLLS_CONTEXT, *PUNLOADED_DLLS_CONTEXT; @@ -37,7 +38,6 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( ); VOID EtShowUnloadedDllsDialog( - _In_ HWND ParentWindowHandle, _In_ PPH_PROCESS_ITEM ProcessItem ) { @@ -49,7 +49,7 @@ VOID EtShowUnloadedDllsDialog( DialogBoxParam( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_UNLOADEDDLLS), - ParentWindowHandle, + NULL, EtpUnloadedDllsDlgProc, (LPARAM)&context ); @@ -305,6 +305,9 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( { HWND lvHandle; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); @@ -324,6 +327,17 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( ExtendedListView_SetCompareFunction(lvHandle, 3, EtpSizeCompareFunction); ExtendedListView_SetCompareFunction(lvHandle, 4, EtpTimeStampCompareFunction); ExtendedListView_SetCompareFunction(lvHandle, 5, EtpCheckSumCompareFunction); + 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); if (!EtpRefreshUnloadedDlls(hwndDlg, context)) { @@ -332,6 +346,14 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( } } 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); + } + break; case WM_COMMAND: { switch (LOWORD(wParam)) @@ -351,6 +373,11 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); } break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; } return FALSE; From f73312e162c0aa07036c4199424955ede9f67481 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 15:21:47 +1000 Subject: [PATCH 101/839] Add PS_MITIGATION_OPTION --- phnt/include/ntpsapi.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 2011aa3915cc..ead669d1bb9e 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1322,6 +1322,31 @@ typedef enum _PS_CREATE_STATE PsCreateMaximumStates } PS_CREATE_STATE; +// 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; + typedef struct _PS_CREATE_INFO { SIZE_T Size; From efb9503bbc357defe58596f90e5ea2ff977787dc Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 May 2017 16:27:23 +1000 Subject: [PATCH 102/839] Fix HideUnnamedHandles regression --- ProcessHacker/prpghndl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index 6f0861eee24f..36d1d027dd3c 100644 --- a/ProcessHacker/prpghndl.c +++ b/ProcessHacker/prpghndl.c @@ -253,7 +253,7 @@ BOOLEAN PhpHandleTreeFilterCallback( PPH_HANDLE_NODE handleNode = (PPH_HANDLE_NODE)Node; PPH_HANDLE_ITEM handleItem = handleNode->HandleItem; - if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->ObjectName)) + if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->BestObjectName)) return FALSE; if (PhIsNullOrEmptyString(handlesContext->SearchboxText)) From cf1f4ba485bfa003b8a7b8fb8b54bb0bc64999ba Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 16 May 2017 03:48:03 +1000 Subject: [PATCH 103/839] ExtendedTools: Fix crash on devices with special graphics cards --- plugins/ExtendedTools/gpumon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/ExtendedTools/gpumon.c b/plugins/ExtendedTools/gpumon.c index 7f941d54a4be..ca608783baa3 100644 --- a/plugins/ExtendedTools/gpumon.c +++ b/plugins/ExtendedTools/gpumon.c @@ -33,7 +33,7 @@ BOOLEAN EtGpuEnabled; static PPH_LIST EtpGpuAdapterList; static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -ULONG EtGpuTotalNodeCount; +ULONG EtGpuTotalNodeCount = 0; ULONG EtGpuTotalSegmentCount; ULONG64 EtGpuDedicatedLimit; ULONG64 EtGpuSharedLimit; @@ -274,6 +274,9 @@ BOOLEAN EtpInitializeD3DStatistics( PhFree(deviceInterfaceList); + if (EtGpuTotalNodeCount == 0) + return FALSE; + EtGpuNodeBitMapBuffer = PhAllocate(BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)); RtlInitializeBitMap(&EtGpuNodeBitMap, EtGpuNodeBitMapBuffer, EtGpuTotalNodeCount); RtlSetBits(&EtGpuNodeBitMap, 0, 1); From af48478d200c4a65c7b2985a280dbfa6d0449d5f Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 05:07:50 +1000 Subject: [PATCH 104/839] peview: Add symbol treelist support, Fix symbol paramaters --- tools/peview/colmgr.c | 600 ++++++++++++++++++++++ tools/peview/colmgr.h | 87 ++++ tools/peview/include/pdb.h | 52 +- tools/peview/include/peview.h | 125 ++++- tools/peview/main.c | 5 +- tools/peview/pdb.c | 750 +++++++++++++--------------- tools/peview/pdbprp.c | 714 ++++++++++++++++++++++++++ tools/peview/peview.rc | 31 +- tools/peview/peview.vcxproj | 7 +- tools/peview/peview.vcxproj.filters | 11 +- tools/peview/resource.h | 13 +- tools/peview/settings.c | 2 +- 12 files changed, 1922 insertions(+), 475 deletions(-) create mode 100644 tools/peview/colmgr.c create mode 100644 tools/peview/colmgr.h create mode 100644 tools/peview/pdbprp.c diff --git a/tools/peview/colmgr.c b/tools/peview/colmgr.c new file mode 100644 index 000000000000..dc4b4c963cc4 --- /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 : -1; + 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) != -1) + 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 = -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; +} + +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"%u,%u,%u|", + i, + column.Fixed ? 0 : column.DisplayIndex + increment, + column.Width + ); + } + else + { + PPH_CM_COLUMN cmColumn; + + cmColumn = column.Context; + PhAppendFormatStringBuilder( + &stringBuilder, + L"+%u,%u,%u|", + 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"+%u,,%u|", + 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"+%u,%u", 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/include/pdb.h b/tools/peview/include/pdb.h index b21ac2d946c7..966415a1844e 100644 --- a/tools/peview/include/pdb.h +++ b/tools/peview/include/pdb.h @@ -112,15 +112,14 @@ 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_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 @@ -361,8 +360,7 @@ typedef struct _TypeInfo // a class or a structure, "false" if the symbol is a union) BOOL UdtKind; - // Union of all type information structures - union // TypeInfoStructures + union { BaseTypeInfo sBaseTypeInfo; // If Tag == SymTagBaseType TypedefInfo sTypedefInfo; // If Tag == SymTagTypedef @@ -378,13 +376,6 @@ typedef struct _TypeInfo }; } TypeInfo; -typedef struct _PDB_SYMBOL_CONTEXT -{ - HWND ListviewHandle; - ULONG64 BaseAddress; - PPH_LIST UdtList; -} PDB_SYMBOL_CONTEXT, *PPDB_SYMBOL_CONTEXT; - BOOLEAN SymbolInfo_DumpBasicType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseTypeInfo* Info); BOOLEAN SymbolInfo_DumpPointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, PointerTypeInfo* Info); BOOLEAN SymbolInfo_DumpTypedef(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypedefInfo* Info); @@ -422,15 +413,16 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( _In_ ULONG Index, _Inout_ PPDB_SYMBOL_CONTEXT Context, _Out_ PWSTR *VarName, - _Out_ PWSTR *TypeName + _Inout_ PPH_STRING_BUILDER TypeName ); + BOOLEAN SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, + _Inout_ PPH_STRING_BUILDER TypeName, _In_ ULONG Index, - _In_ PWSTR pVarName, - _In_ PWSTR pTypeName, - _In_ ULONG MaxChars + _In_ PWSTR VarName ); + PWSTR SymbolInfo_TagStr(enum SymTagEnum Tag); PWSTR SymbolInfo_BaseTypeStr(BasicType Type, ULONG64 Length); PWSTR SymbolInfo_CallConvStr(CV_call_e CallConv); @@ -441,22 +433,6 @@ PWSTR SymbolInfo_RegisterStr(CV_HREG_e RegCode); PWSTR SymbolInfo_UdtKindStr(UdtKind KindType); PWSTR SymbolInfo_LocationTypeStr(LocationType LocType); - -VOID PrintDataInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ); - -VOID PrintFunctionInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ); - -VOID PrintDefaultInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ); - VOID PrintClassInfo( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 04a3cb8152a2..69987ec41906 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -2,12 +2,15 @@ #define PEVIEW_H #include +#include #include #include #include #include +#include #include #include +#include #include @@ -57,6 +60,118 @@ VOID PeSaveSettings( // symbols + +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_MAXIMUM +} WCT_TREE_COLUMN_ITEM_NAME; + +typedef enum _PV_SYMBOL_TYPE +{ + PV_SYMBOL_TYPE_NONE, + 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_MEMBER, + PV_SYMBOL_TYPE_STATIC_MEMBER, + PV_SYMBOL_TYPE_CONSTANT +} 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; + WCHAR Pointer[PH_PTR_STR_LEN_1]; + + PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM]; +} PV_SYMBOL_NODE, *PPV_SYMBOL_NODE; + + +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; + + 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, @@ -64,15 +179,19 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( _In_ LPARAM lParam ); -VOID PeDumpFileSymbols( - _In_ HWND ListViewHandle, - _In_ PWSTR FileName +NTSTATUS PeDumpFileSymbols( + _In_ PPDB_SYMBOL_CONTEXT Context ); VOID PvPdbProperties( VOID ); +VOID PluginsAddTreeNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPV_SYMBOL_NODE Entry + ); + // INT_PTR CALLBACK PvpPeImportsDlgProc( diff --git a/tools/peview/main.c b/tools/peview/main.c index 4c845c4e9193..a8de840ebf1a 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -71,6 +71,7 @@ INT WINAPI wWinMain( PhSettingsInitialization(); PeInitializeSettings(); PvPropInitialization(); + PhTreeNewInitialization(); PhApplicationName = L"PE Viewer"; @@ -123,8 +124,8 @@ INT WINAPI wWinMain( if (PhEndsWithString2(PvFileName, L".lib", TRUE)) PvLibProperties(); - //else if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) - // PvPdbProperties(); + else if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) + PvPdbProperties(); else PvPeProperties(); diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 19fc41cdcde2..039dc60454e3 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -691,9 +692,8 @@ BOOLEAN SymbolInfo_DumpType( ) { ULONG tag = SymTagNull; - BOOLEAN result = FALSE; - // Get the symbol's tag + // Get the symbol tag if (!SymGetTypeInfo_I( NtCurrentProcess(), Context->BaseAddress, @@ -711,38 +711,28 @@ BOOLEAN SymbolInfo_DumpType( switch (tag) { case SymTagBaseType: - result = SymbolInfo_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); - break; + return SymbolInfo_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); case SymTagPointerType: - result = SymbolInfo_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); - break; + return SymbolInfo_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); case SymTagTypedef: - result = SymbolInfo_DumpTypedef(Context, Index, &Info->sTypedefInfo); - break; + return SymbolInfo_DumpTypedef(Context, Index, &Info->sTypedefInfo); case SymTagEnum: - result = SymbolInfo_DumpEnum(Context, Index, &Info->sEnumInfo); - break; + return SymbolInfo_DumpEnum(Context, Index, &Info->sEnumInfo); case SymTagArrayType: - result = SymbolInfo_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); - break; + return SymbolInfo_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); case SymTagUDT: - result = SymbolInfo_DumpUDT(Context, Index, Info); - break; + return SymbolInfo_DumpUDT(Context, Index, Info); case SymTagFunctionType: - result = SymbolInfo_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); - break; + return SymbolInfo_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); case SymTagFunctionArgType: - result = SymbolInfo_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); - break; + return SymbolInfo_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); case SymTagBaseClass: - result = SymbolInfo_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); - break; + return SymbolInfo_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); case SymTagData: - result = SymbolInfo_DumpData(Context, Index, &Info->sDataInfo); - break; + return SymbolInfo_DumpData(Context, Index, &Info->sDataInfo); } - return result; + return FALSE; } BOOLEAN SymbolInfo_DumpSymbolType( @@ -1325,7 +1315,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( _In_ ULONG Index, _Inout_ PPDB_SYMBOL_CONTEXT Obj, _Out_ PWSTR *VarName, - _Out_ PWSTR *TypeName + _Inout_ PPH_STRING_BUILDER TypeName ) { TypeInfo Info; @@ -1361,11 +1351,13 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( switch (Info.Tag) { case SymTagBaseType: - *TypeName = SymbolInfo_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length); + PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length)); + PhAppendStringBuilder2(TypeName, L" "); break; case SymTagTypedef: - *TypeName = _wcsdup(Info.sTypedefInfo.Name); + PhAppendStringBuilder2(TypeName, Info.sTypedefInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); break; case SymTagUDT: @@ -1373,7 +1365,8 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( if (Info.UdtKind) { // A class/structure - *TypeName = _wcsdup(Info.sUdtClassInfo.Name); + PhAppendStringBuilder2(TypeName, Info.sUdtClassInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); // Add the UDT and its base classes to the collection of UDT indexes PhAddItemList(Obj->UdtList, UlongToPtr(Index)); @@ -1381,30 +1374,30 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( else { // A union - *TypeName = _wcsdup(Info.sUdtUnionInfo.Name); + PhAppendStringBuilder2(TypeName, Info.sUdtUnionInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); } } break; case SymTagEnum: - *TypeName = _wcsdup(Info.sEnumInfo.Name); + PhAppendStringBuilder2(TypeName, Info.sEnumInfo.Name); break; case SymTagFunctionType: { if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.StaticFunction) { - //*TypeName = L"static "; + PhAppendStringBuilder2(TypeName, L"static "); } - // Print return value + // return value if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) return FALSE; - // Print calling convention - //TypeName += " "; - //TypeName += SymbolInfo_CallConvStr(Info.Info.sFunctionTypeInfo.CallConv); - //TypeName += " "; + // calling convention + PhAppendStringBuilder2(TypeName, SymbolInfo_CallConvStr(Info.sFunctionTypeInfo.CallConv)); + PhAppendStringBuilder2(TypeName, L" "); // If member function, save the class type index if (Info.sFunctionTypeInfo.MemberFunction) @@ -1412,39 +1405,42 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( PhAddItemList(Obj->UdtList, UlongToPtr(Info.sFunctionTypeInfo.ClassIndex)); /* - // It is not needed to print the class name here, because - // it is contained in the function name - if( !SymbolInfo_GetTypeNameHelper( Info.Info.sFunctionTypeInfo.ClassIndex, Obj, VarName, TypeName ) ) + // 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 that it is a function - //TypeName += VarName; + PhAppendStringBuilder2(TypeName, *VarName); + // Print parameters - //TypeName += " ("; + PhAppendStringBuilder2(TypeName, L" ("); + for (ULONG i = 0; i < Info.sFunctionTypeInfo.NumArgs; i++) { if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.Args[i], Obj, VarName, TypeName)) return FALSE; - //if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) - //TypeName += ", "; + if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) + { + PhAppendStringBuilder2(TypeName, L", "); + } } - //TypeName += ")"; + + PhAppendStringBuilder2(TypeName, L") "); // Print "this" adjustment value if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.ThisAdjust != 0) { WCHAR buffer[MAX_PATH + 1] = L""; - //TypeName += "ThisAdjust: "; _snwprintf(buffer, ARRAYSIZE(buffer), L"this+%u", Info.sFunctionTypeInfo.ThisAdjust); - //TypeName += buffer; - *TypeName = _wcsdup(buffer); + PhAppendStringBuilder2(TypeName, L": "); + PhAppendStringBuilder2(TypeName, buffer); + PhAppendStringBuilder2(TypeName, L" "); } } break; @@ -1462,15 +1458,17 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( if (!SymbolInfo_GetTypeNameHelper(Info.sArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) return FALSE; - //TypeName += " "; - //TypeName += VarName; + PhAppendStringBuilder2(TypeName, L" "); + //PhAppendStringBuilder2(TypeName, *VarName); // Print dimensions for (ULONG i = 0; i < Info.sArrayTypeInfo.NumDimensions; i++) { - //WCHAR buffer[MAX_PATH + 1] = L""; - //_snwprintf(buffer, cTempBufSize, "[%u]"), Info.Info.sArrayTypeInfo.Dimensions[i]); - //TypeName += buffer; + WCHAR buffer[MAX_PATH + 1] = L""; + + _snwprintf(buffer, ARRAYSIZE(buffer), L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); + + PhAppendStringBuilder2(TypeName, buffer); } } break; @@ -1480,7 +1478,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( _snwprintf(buffer, ARRAYSIZE(buffer), L"Unknown(%lu)", Info.Tag); - *TypeName = _wcsdup(buffer); + PhAppendStringBuilder2(TypeName, buffer); } break; } @@ -1488,8 +1486,8 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( // If it is a pointer, display * characters if (numPointers != 0) { - //for (ULONG i = 0; i < NumPointers; i++) - // TypeName += "*"; + for (ULONG i = 0; i < numPointers; i++) + PhAppendStringBuilder2(TypeName, L"*"); } } @@ -1498,30 +1496,22 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( BOOLEAN SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, + _Inout_ PPH_STRING_BUILDER TypeName, _In_ ULONG Index, - _In_ PWSTR pVarName, - _In_ PWSTR pTypeName, - _In_ ULONG MaxChars + _In_ PWSTR VarName ) { - PWSTR VarName = NULL; - PWSTR TypeName = NULL; - - if (!pTypeName) - return FALSE; + PWSTR typeVarName = NULL; - if (pVarName) - VarName = pVarName; + if (VarName) + typeVarName = VarName; // Obtain the type name - if (!SymbolInfo_GetTypeNameHelper(Index, Context, &VarName, &TypeName)) - return FALSE; - - if (!wcslen(TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Index, Context, &typeVarName, TypeName)) return FALSE; // Return the type name to the caller - _snwprintf(pTypeName, MaxChars, L"%s", TypeName); + //_snwprintf(pTypeName, MaxChars, L"%s", TypeName.String->Buffer); return TRUE; } @@ -1804,9 +1794,7 @@ VOID SymbolInfo_SymbolLocationStr( if (regString) wcscpy(Buffer, regString); else - _swprintf(Buffer, L"Reg%u", SymbolInfo->Register); - - return; + _swprintf(Buffer, L"Reg+%u", SymbolInfo->Register); } else if (SymbolInfo->Flags & SYMFLAG_REGREL) { @@ -1816,16 +1804,13 @@ VOID SymbolInfo_SymbolLocationStr( if (regString) wcscpy(szReg, regString); else - _swprintf(szReg, L"Reg%u", SymbolInfo->Register); - - _swprintf(Buffer, L"%s%+lu", szReg, (long)SymbolInfo->Address); + _swprintf(szReg, L"Reg+%u", SymbolInfo->Register); - return; + _swprintf(Buffer, L"%s+%I64u", szReg, SymbolInfo->Address); } else if (SymbolInfo->Flags & SYMFLAG_FRAMEREL) { wcscpy(Buffer, L"N/A"); - return; } else { @@ -1918,127 +1903,148 @@ BOOL CALLBACK EnumCallbackProc( _In_ PVOID Context ) { - if (!SymbolInfo) // Try to enumerate other symbols - return TRUE; - - switch (SymbolInfo->Tag) + if (SymbolInfo) { - case SymTagFunction: - PrintFunctionInfo(Context, SymbolInfo); - break; - case SymTagData: - PrintDataInfo(Context, SymbolInfo); - break; - default: - PrintDefaultInfo(Context, SymbolInfo); - break; - } - - return TRUE; -} - -VOID PrintDataInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ) -{ - INT lvItemIndex; - PWSTR symDataKind; - WCHAR pointer[PH_PTR_STR_LEN_1] = L""; - - if (SymbolInfo->Tag != SymTagData) - return; - - // Type - symDataKind = SymbolInfo_DataKindFromSymbolInfo(SymbolInfo); - lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, symDataKind, NULL); + switch (SymbolInfo->Tag) + { + case SymTagFunction: + { + PPDB_SYMBOL_CONTEXT context = Context; + PH_STRING_BUILDER typeName; + PPV_SYMBOL_NODE symbol; - // Address - SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + symbol = PhAllocate(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)); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Name - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); + PhInitializeStringBuilder(&typeName, 0x100); - // Size - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) + symbol->Data = PhFinalStringBuilderString(&typeName); - // Data - //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) - // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex -} + PluginsAddTreeNode(context, symbol); -VOID PrintFunctionInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ) -{ - INT lvItemIndex; - WCHAR pointer[PH_PTR_STR_LEN_1] = L""; + // Enumerate function parameters and local variables... + IMAGEHLP_STACK_FRAME sf; + sf.InstructionOffset = SymbolInfo->Address; + SymSetContext_I(NtCurrentProcess(), &sf, 0); + SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, context); + } + break; + case SymTagData: + { + PPDB_SYMBOL_CONTEXT context = Context; + PH_STRING_BUILDER typeName; + PPV_SYMBOL_NODE symbol; + PWSTR symDataKind; + ULONG dataKindType = 0; + + // TODO: Remove filter + if (!SymbolInfo->Address) + break; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) + break; + + // Type + symDataKind = SymbolInfo_DataKindStr(dataKindType); + + // TODO: Remove filter + if (!wcscmp(symDataKind, L"LOCAL_VAR") || + !wcscmp(symDataKind, L"OBJECT_PTR") || + !wcscmp(symDataKind, L"PARAMETER")) + { + break; + } - if (SymbolInfo->Tag != SymTagFunction) - return; - - // Type - lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"FUNCTION", NULL); + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); - // Address - SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + switch (dataKindType) + { + case DataIsLocal: + 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_MEMBER; + break; + case DataIsStaticMember: + symbol->Type = PV_SYMBOL_TYPE_STATIC_MEMBER; + break; + case DataIsConstant: + symbol->Type = PV_SYMBOL_TYPE_CONSTANT; + break; + } - // Name - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); + symbol->Address = SymbolInfo->Address; + symbol->Size = SymbolInfo->Size; + symbol->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Size - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + PhInitializeStringBuilder(&typeName, 0x100); - // Data - //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) - // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + // Data + if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) + symbol->Data = PhFinalStringBuilderString(&typeName); - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - // Enumerate function parameters and local variables... - //IMAGEHLP_STACK_FRAME sf; - //sf.InstructionOffset = SymbolInfo->Address; - //SymSetContext_I(NtCurrentProcess(), &sf, 0); - //SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); -} + PluginsAddTreeNode(context, symbol); + } + break; + default: + { + PPDB_SYMBOL_CONTEXT context = Context; + PH_STRING_BUILDER typeName; + PPV_SYMBOL_NODE symbol; -VOID PrintDefaultInfo( - _In_ PPDB_SYMBOL_CONTEXT Context, - _In_ PSYMBOL_INFOW SymbolInfo - ) -{ - INT lvItemIndex; - WCHAR pointer[PH_PTR_STR_LEN_1] = L""; + // TODO: Remove filter + if (SymbolInfo->Tag != SymTagPublicSymbol) + break; - if (SymbolInfo->Tag != SymTagPublicSymbol) - return; - - // Type - lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"SYMBOL", NULL); + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + symbol->Type = PV_SYMBOL_TYPE_SYMBOL; + symbol->Address = SymbolInfo->Address; + symbol->Size = SymbolInfo->Size; + symbol->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Address - SymbolInfo_SymbolLocationStr(SymbolInfo, pointer); - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); + PhInitializeStringBuilder(&typeName, 0x100); - // Name - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, SymbolInfo->Name); + if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) + symbol->Data = PhFinalStringBuilderString(&typeName); - // Size - PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%lu", SymbolInfo->Size)->Buffer); + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - // Data - //if (SymbolInfo_GetTypeName(Context, SymbolInfo->TypeIndex, SymbolInfo->Name, szTypeName, cMaxTypeNameLen)) - // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, szTypeName); + PluginsAddTreeNode(context, symbol); + } + break; + } + } - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + return TRUE; } VOID PrintClassInfo( @@ -2047,133 +2053,174 @@ VOID PrintClassInfo( _In_ UdtClassInfo* Info ) { + //INT lvItemIndex; + + if (Info->UDTKind == UdtClass) + { + //PhAddItemList(L"CLASS", Info->Name) + //return; + } + // UDT kind - //OutputDebugString(PhaFormatString(L"%s", SymbolInfo_UdtKindStr(Info->UDTKind))->Buffer); - // Name - //OutputDebugString(PhaFormatString(L" %s \n", Info->Name)->Buffer); + //lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_UdtKindStr(Info->UDTKind), NULL); + // Name + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, Info->Name); // Size - //OutputDebugString(PhaFormatString(L"Size: %I64u", Info->Length)->Buffer); + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatSize(Info->Length, -1)->Buffer); // Nested - //if (Info.Nested) - //OutputDebugString(L" Nested"); + //if (Info.Nested) OutputDebugString(L" Nested"); // Number of member variables //OutputDebugString(PhaFormatString(L" Variables: %d", Info->NumVariables)->Buffer); // Number of member functions //OutputDebugString(PhaFormatString(L" Functions: %d", Info->NumFunctions)->Buffer); // Number of base classes //OutputDebugString(PhaFormatString(L" Base classes: %d", Info->NumBaseClasses)->Buffer); - // Extended information about member variables - //OutputDebugString(L"\n"); + // Extended information about member variables for (ULONG i = 0; i < Info->NumVariables; i++) { TypeInfo VarInfo; if (!SymbolInfo_DumpType(Context, Info->Variables[i], &VarInfo)) { - //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); - // Continue with the next variable + // Continue } else if (VarInfo.Tag != SymTagData) { - //_ASSERTE(!_T("Unexpected symbol tag.")); - // Continue with the next variable + // Unexpected symbol tag. } else { - // Display information about the variable - //_tprintf(_T("%s"), Context->DataKindStr(VarInfo.Info.sDataInfo.dataKind)); - - // Name - //wprintf(L" %s \n", VarInfo.Info.sDataInfo.Name); - // Type name - //_tprintf(_T(" Type: ")); - - //#define cMaxTypeNameLen 1024 - //TCHAR szTypeName[cMaxTypeNameLen + 1] = _T(""); - - //if (SymbolInfo_GetTypeName(VarInfo.Info.sDataInfo.TypeIndex, W2T(VarInfo.Info.sDataInfo.Name), szTypeName, cMaxTypeNameLen)) - //_tprintf(szTypeName); - //else - //_tprintf(_T("n/a")); - //_tprintf(_T("\n")); - + //INT lvItemSubIndex; + //PH_STRING_BUILDER typeName; + // + //PhInitializeStringBuilder(&typeName, 0x100); + // + //// Variable + //lvItemSubIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_DataKindStr(VarInfo.sDataInfo.dataKind), NULL); + // + //// Name + //PhSetListViewSubItem(Context->ListviewHandle, lvItemSubIndex, 2, VarInfo.sDataInfo.Name); + // + //// Data + //if (SymbolInfo_GetTypeName(Context, &typeName, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name)) + // PhSetListViewSubItem(Context->ListviewHandle, lvItemSubIndex, 4, PhFinalStringBuilderString(&typeName)->Buffer); + //PhDeleteStringBuilder(&typeName); // Location - switch (VarInfo.sDataInfo.dataKind) - { - case DataIsGlobal: - case DataIsStaticLocal: - case DataIsFileStatic: - case DataIsStaticMember: - { - // Use Address - //_tprintf(_T(" Address: %16I64x"), VarInfo.Info.sDataInfo.Address); - } - break; - case DataIsLocal: - case DataIsParam: - case DataIsObjectPtr: - case DataIsMember: - { - // Use Offset - //_tprintf(_T(" Offset: %8d"), (long)VarInfo.Info.sDataInfo.Offset); - } - break; - default: - break; // Add support for constants - } - - // Indices - //_tprintf(_T(" Index: %8u TypeIndex: %8u"), Index, VarInfo.Info.sDataInfo.TypeIndex); - //_tprintf(_T("\n")); + //switch (VarInfo.sDataInfo.dataKind) + //{ + //case DataIsGlobal: + //case DataIsStaticLocal: + //case DataIsFileStatic: + //case DataIsStaticMember: + // { + // // Use Address + // // " Address: %16I64x" VarInfo.sDataInfo.Address + // } + // break; + //case DataIsLocal: + //case DataIsParam: + //case DataIsObjectPtr: + //case DataIsMember: + // { + // // Use Offset + // // " Offset: %8d" (long)VarInfo.sDataInfo.Offset + // } + // break; + //default: + // break; // TODO Add support for constants + //} + // + // Indices " Index: %8u TypeIndex: %8u" Index, VarInfo.sDataInfo.TypeIndex } } - // Extended information about member functions - // Implement - // Extended information about base classes - // Implement + // TODO Implement information about member functions + for (ULONG i = 0; i < Info->NumFunctions; i++) + { + TypeInfo VarInfo; + + if (!SymbolInfo_DumpType(Context, Info->Variables[i], &VarInfo)) + { + // Continue + } + else if (VarInfo.Tag != SymTagFunction) + { + // Unexpected symbol tag. + } + else + { + //INT lvItemSubIndex; + //PH_STRING_BUILDER typeName; + // + //PhInitializeStringBuilder(&typeName, 0x100); + // + // Type + //lvItemSubIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"VARIABLE", NULL); + + // Address + // SymbolInfo_SymbolLocationStr(Context, VarInfo.sDataInfo.Address); + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); VarInfo.sDataInfo.Address + // + // Name : SymbolInfo_DataKindStr(VarInfo.sDataInfo.dataKind) + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, VarInfo.sDataInfo.Name); + // + // Size + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, VarInfo.sDataInfo.Offset + // + // Data + //if (SymbolInfo_GetTypeName(Context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) + // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, PhFinalStringBuilderString(&typeName)->Buffer); + // + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + // + //PhDeleteStringBuilder(&typeName); + // + // Enumerate function parameters and local variables... + // IMAGEHLP_STACK_FRAME sf; + //sf.InstructionOffset = SymbolInfo->Address; VarInfo.sDataInfo.Offset + //SymSetContext_I(NtCurrentProcess(), &sf, 0); + //SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); + } + } + // TODO Implement information about base classes for (ULONG i = 0; i < Info->NumBaseClasses; i++) { TypeInfo baseInfo; if (!SymbolInfo_DumpType(Context, Info->BaseClasses[i], &baseInfo)) { - //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); - // Continue with the next base class + // Continue } else if (baseInfo.Tag != SymTagBaseClass) { - //_ASSERTE(!_T("Unexpected symbol tag.")); - // Continue with the next base class + // Unexpected symbol tag } else { - // Obtain the name of the base class TypeInfo BaseUdtInfo; + // Obtain the next base class if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &BaseUdtInfo)) { - //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); - // Continue with the next base class + // Continue } else if (BaseUdtInfo.Tag != SymTagUDT) { - //_ASSERTE(!_T("Unexpected symbol tag.")); - // Continue with the next base class + // Unexpected symbol tag } else { - // Print the name of the base class if (baseInfo.sBaseClassInfo.Virtual) { - //wprintf(L"VIRTUAL_BASE_CLASS %s \n", BaseUdtInfo.Info.sUdtClassInfo.Name); + //PhAddItemList(L"VIRTUAL_BASE_CLASS", BaseUdtInfo.sUdtClassInfo.Name) } else { - //wprintf(L"BASE_CLASS %s \n", BaseUdtInfo.Info.sUdtClassInfo.Name); + //PhAddItemList(L"BASE_CLASS", BaseUdtInfo.sUdtClassInfo.Name) } } } @@ -2208,13 +2255,11 @@ VOID PrintUserDefinedTypes( if (!SymbolInfo_DumpType(Context, info.sUdtClassInfo.BaseClasses[i], &baseInfo)) { - //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); - // Continue with the next base class + // Continue } else if (baseInfo.Tag != SymTagBaseClass) { - //_ASSERTE(!_T("Unexpected symbol tag.")); - // Continue with the next base class + // Continue } else { @@ -2223,13 +2268,11 @@ VOID PrintUserDefinedTypes( if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo)) { - //_ASSERTE(!_T("SymbolInfo::DumpType() failed.")); - // Continue with the next base class + // Continue } else if (baseUdtInfo.Tag != SymTagUDT) { - //_ASSERTE(!_T("Unexpected symbol tag.")); - // Continue with the next base class + // Continue } else { @@ -2241,15 +2284,14 @@ VOID PrintUserDefinedTypes( } else { + //INT lvItemIndex; // UDT kind - //printf("%s", Context->UdtKindStr(Info.Info.sUdtClassInfo.UDTKind)); - // - // Name - //printf("%s", Info.Info.sUdtClassInfo.Name); - // + //lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_UdtKindStr(info.sUdtClassInfo.UDTKind), NULL); + // Name + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, info.sUdtClassInfo.Name); // Size - //printf("Size: %I64u", Info.Info.sUdtClassInfo.Length); - // + //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%I64u", info.sUdtClassInfo.Length)->Buffer); + // Nested //if (Info.Info.sUdtClassInfo.Nested) // printf("Nested"); @@ -2262,11 +2304,13 @@ VOID PrintUserDefinedTypes( } } -/////////////////////////////////////////////////////////////////////////////// -BOOLEAN GetFileParamsSize(_In_ PWSTR pFileName, _Out_ ULONG* FileLength) +BOOLEAN GetFileParamsSize( + _In_ PWSTR FileName, + _Out_ ULONG *FileLength + ) { HANDLE hFile = CreateFile( - pFileName, + FileName, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -2286,28 +2330,22 @@ BOOLEAN GetFileParamsSize(_In_ PWSTR pFileName, _Out_ ULONG* FileLength) return (*FileLength != INVALID_FILE_SIZE); } -BOOLEAN GetFileParams(_In_ PWSTR pFileName, _Out_ ULONG64 *BaseAddress, _Out_ ULONG* FileLength) +BOOLEAN GetFileParams( + _In_ PWSTR FileName, + _Out_ ULONG64 *BaseAddress, + _Out_ ULONG *FileLength + ) { TCHAR szFileExt[_MAX_EXT] = { 0 }; - _wsplitpath(pFileName, NULL, NULL, NULL, szFileExt); + _wsplitpath(FileName, NULL, NULL, NULL, szFileExt); - // Is it .PDB file ? if (_wcsicmp(szFileExt, L".PDB") == 0) - { - // Yes, it is a .PDB file + { *BaseAddress = 0x10000000; - // Determine its size, and use a dummy base address. - // it can be any non-zero value, but if we load symbols - // from more than one file, memory regions specified - // for different files should not overlap - // (region is "base address + file size") - - if (!GetFileParamsSize(pFileName, FileLength)) - { + if (!GetFileParamsSize(FileName, FileLength)) return FALSE; - } } else { @@ -2318,7 +2356,10 @@ BOOLEAN GetFileParams(_In_ PWSTR pFileName, _Out_ ULONG64 *BaseAddress, _Out_ UL return TRUE; } -/////////////////////////////////////////////////////////////////////////////// +// +// TODO: Move to pdbprp.c +// + VOID PdbLoadDbgHelpFromPath( _In_ PWSTR DbgHelpPath ) @@ -2361,7 +2402,6 @@ VOID PdbLoadDbgHelpFromPath( PhSymbolProviderCompleteInitialization(dbghelpModule); } - VOID ShowSymbolInfo( _In_ ULONG64 BaseAddress ) @@ -2377,7 +2417,7 @@ VOID ShowSymbolInfo( switch (info.SymType) { case SymNone: - //OutputDebugString(L"No symbols available for the module.\r\n"); + OutputDebugString(L"No symbols available for the module.\r\n"); break; case SymExport: //OutputDebugString(L"Loaded symbols: Exports\r\n"); @@ -2441,9 +2481,8 @@ VOID ShowSymbolInfo( //OutputDebugString(PhaFormatString(L"Public symbols: %s\n", info.Publics ? L"Available" : L"Not available")->Buffer); } -VOID PeDumpFileSymbols( - _In_ HWND ListViewHandle, - _In_ PWSTR FileName +NTSTATUS PeDumpFileSymbols( + _In_ PPDB_SYMBOL_CONTEXT Context ) { HMODULE dbghelpHandle; @@ -2455,7 +2494,7 @@ VOID PeDumpFileSymbols( PdbLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); if (!(dbghelpHandle = GetModuleHandle(L"dbghelp.dll"))) - return; + return 1; symsrvHandle = GetModuleHandle(L"symsrv.dll"); SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); @@ -2472,136 +2511,39 @@ VOID PeDumpFileSymbols( SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME | SYMOPT_ALLOW_ZERO_ADDRESS); // SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED if (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) - return; + return 1; if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*http://msdl.microsoft.com/download/symbols")) - return; + goto CleanupExit; - if (GetFileParams(FileName, &baseAddress, &fileLength)) + if (GetFileParams(PvFileName->Buffer, &baseAddress, &fileLength)) { if ((symbolBaseAddress = SymLoadModuleExW_I( - NtCurrentProcess(), - NULL, - FileName, - NULL, - baseAddress, - fileLength, - NULL, + NtCurrentProcess(), + NULL, + PvFileName->Buffer, + NULL, + baseAddress, + fileLength, + NULL, 0 ))) { - PDB_SYMBOL_CONTEXT context; - - context.ListviewHandle = ListViewHandle; - context.BaseAddress = symbolBaseAddress; - context.UdtList = PhCreateList(0x100); + Context->BaseAddress = symbolBaseAddress; + Context->UdtList = PhCreateList(0x100); - //ShowSymbolInfo(symbolBaseAddress); + ShowSymbolInfo(symbolBaseAddress); - SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, &context); + SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, Context); // Print information about used defined types - //PrintUserDefinedTypes(&context); + PrintUserDefinedTypes(Context); } } - SymCleanup_I(NtCurrentProcess()); -} - -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; - - // Symbols page - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PESYMBOLS), - PvpSymbolsDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - - PhModalPropertySheet(&propContext->PropSheetHeader); - - PhDereferenceObject(propContext); - } -} - -INT_PTR CALLBACK PvpSymbolsDlgProc( - _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"Type"); - 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, 40, L"Size"); - PhSetExtendedListView(lvHandle); - PhLoadListViewColumnsFromSetting(L"PdbListViewColumns", lvHandle); - - PeDumpFileSymbols(lvHandle, PvFileName->Buffer); - - ExtendedListView_SortItems(lvHandle); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PhSaveListViewColumnsToSetting(L"PdbListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; +CleanupExit: - 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; - } + SymCleanup_I(NtCurrentProcess()); - return FALSE; + return 0; } diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c new file mode 100644 index 000000000000..93459540ebf7 --- /dev/null +++ b/tools/peview/pdbprp.c @@ -0,0 +1,714 @@ +/* + * 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 "colmgr.h" + +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); +} + +BOOLEAN PluginsNodeHashtableCompareFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); +ULONG PluginsNodeHashtableHashFunction( + _In_ PVOID Entry + ); +VOID DestroyPluginsNode( + _In_ PPV_SYMBOL_NODE Node + ); + +VOID DeletePluginsTree( + _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++) + { + DestroyPluginsNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +struct _PH_TN_FILTER_SUPPORT* GetPluginListFilterSupport( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + return &Context->FilterSupport; +} + +BOOLEAN PluginsNodeHashtableCompareFunction( + _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 PluginsNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashStringRef(&(*(PPV_SYMBOL_NODE*)Entry)->Name->sr, TRUE); +} + +VOID PluginsAddTreeNode( + _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; + + PhAddEntryHashtable(Context->NodeHashtable, &Entry); + PhAddItemList(Context->NodeList, Entry); + + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + TreeNew_NodesStructured(Context->TreeNewHandle); + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); + + if (Context->FilterSupport.NodeList) + { + Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); + } +} + +PPV_SYMBOL_NODE FindTreeNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPH_STRING Name + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPV_SYMBOL_NODE entry = Context->NodeList->Items[i]; + + if (PhEqualString(entry->Name, Name, TRUE)) + return entry; + } + + return NULL; +} + +VOID WeRemoveWindowNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPV_SYMBOL_NODE 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); + } + + DestroyPluginsNode(Node); +} + +VOID DestroyPluginsNode( + _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(Symbol) +{ + sortResult = PhCompareString(node1->Name, node2->Name, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VA) +{ + sortResult = PhCompareString(node1->Name, node2->Name, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(node1->Name, node2->Name, 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 + ) +{ + 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(Name), + SORT_FUNCTION(VA), + SORT_FUNCTION(Symbol) + }; + 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_MEMBER: + PhInitializeStringRef(&getCellText->Text, L"MEMBER"); + 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; + } + } + 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; + 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_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; + } + + return FALSE; +} + +VOID PluginsClearTree( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyPluginsNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); +} + +PPV_SYMBOL_NODE WeGetSelectedWindowNode( + _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 WeGetSelectedWindowNodes( + _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 InitializePluginsTree( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle + ) +{ + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPV_SYMBOL_NODE), + PluginsNodeHashtableCompareFunction, + PluginsNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + PhSetControlTheme(TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, 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); + /* + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"Symbol"); + 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, 40, L"Size"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Type"); + */ + TreeNew_SetSort(TreeNewHandle, 0, NoSortOrder); + + 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 TreeFilterCallback( + _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; + + if (!PhIsNullOrEmptyString(node->Name)) + { + if (WordMatchStringRef(context, &node->Name->sr)) + return TRUE; + } + + return FALSE; +} + + +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: + { + 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(); + + InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); + //PhAddTreeNewFilter(GetPluginListFilterSupport(context), TreeFilterCallback, context); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PeDumpFileSymbols, context); + + //SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_OPTIONS), TRUE); + } + break; + case WM_DESTROY: + { + DeletePluginsTree(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, GetDlgItem(hwndDlg, IDC_SYMSEARCH), + dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, context->TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_DBLCLK: + { + if (header->hwndFrom == context->TreeNewHandle) + { + + } + } + break; + } + } + 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/peview.rc b/tools/peview/peview.rc index ed559acfa245..7ca0f9265d2d 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -111,10 +111,10 @@ BEGIN IDD_PESYMBOLS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 298 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END END #endif // APSTUDIO_INVOKED @@ -192,8 +192,9 @@ BEGIN 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 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,57,286,216 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,57,286,85 LTEXT "Sections:",IDC_STATIC,7,47,30,8 + CONTROL "",IDC_SECTION,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,145,286,128 END IDD_PELOADCONFIG DIALOGEX 0, 0, 300, 280 @@ -212,12 +213,13 @@ BEGIN CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 END -IDD_PESYMBOLS DIALOGEX 0, 0, 300, 280 +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_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 + CONTROL "",IDC_SYMBOLTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,3,20,295,257,WS_EX_CLIENTEDGE + EDITTEXT IDC_SYMSEARCH,166,3,132,14,ES_AUTOHSCROLL END @@ -226,21 +228,6 @@ 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 - IDD_PESYMBOLS AFX_DIALOG_LAYOUT BEGIN 0 diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 54d1bce3d038..c3d2df1f85af 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -1,4 +1,4 @@ - + @@ -146,6 +146,7 @@ StreamingSIMDExtensions true true + true noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) @@ -175,6 +176,7 @@ true true true + true noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) @@ -193,6 +195,7 @@ + @@ -200,11 +203,13 @@ + + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index a073793965cd..7395ed945c8f 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -51,6 +51,12 @@ Source Files + + Source Files + + + Source Files + @@ -65,6 +71,9 @@ Header Files + + Header Files + diff --git a/tools/peview/resource.h b/tools/peview/resource.h index 6c6e0f0a2ed0..56994a3e0af7 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -10,6 +10,11 @@ #define IDD_PELOADCONFIG 107 #define IDD_PECFG 108 #define IDD_PESYMBOLS 109 +#define IDC_SYMBOLTREE 110 +#define IDB_SEARCH_ACTIVE 111 +#define IDB_SEARCH_INACTIVE 112 +#define IDB_SEARCH_ACTIVE_BMP 113 +#define IDB_SEARCH_INACTIVE_BMP 114 #define IDC_TARGETMACHINE 1003 #define IDC_CHECKSUM 1004 #define IDC_SUBSYSTEM 1005 @@ -17,6 +22,7 @@ #define IDC_CHARACTERISTICS 1007 #define IDC_LIST 1008 #define IDC_FILEICON 1009 +#define IDC_SECTION 1009 #define IDC_TIMESTAMP 1010 #define IDC_RUNTIMEVERSION 1011 #define IDC_FILE 1011 @@ -26,6 +32,7 @@ #define IDC_VERSIONSTRING 1014 #define IDC_IMAGEBASE 1015 #define IDC_ENTRYPOINT 1016 +#define IDC_SYMSEARCH 1017 #define IDC_NAME 1044 #define IDC_COMPANYNAME_LINK 1279 @@ -33,9 +40,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_RESOURCE_VALUE 116 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1017 -#define _APS_NEXT_SYMED_VALUE 101 +#define _APS_NEXT_CONTROL_VALUE 1019 +#define _APS_NEXT_SYMED_VALUE 116 #endif #endif diff --git a/tools/peview/settings.c b/tools/peview/settings.c index 1d9da291d023..750ce1ace2ea 100644 --- a/tools/peview/settings.c +++ b/tools/peview/settings.c @@ -40,7 +40,7 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"ImageImportsListViewColumns", L""); PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); PhpAddStringSetting(L"LibListViewColumns", L""); - PhpAddStringSetting(L"PdbListViewColumns", L""); + PhpAddStringSetting(L"PdbTreeListColumns", L""); } VOID PhUpdateCachedSettings( From e83471cf44606da43a53dc3a3ed5c8f5d0907970 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 05:20:51 +1000 Subject: [PATCH 105/839] Updater: Fix missing changelog --- plugins/Updater/page4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Updater/page4.c b/plugins/Updater/page4.c index 2f636ac13d0b..237e49e5de0b 100644 --- a/plugins/Updater/page4.c +++ b/plugins/Updater/page4.c @@ -62,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_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 | TDF_EXPAND_FOOTER_AREA; config.dwCommonButtons = TDCBF_CANCEL_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; @@ -76,7 +76,7 @@ VOID ShowProgressDialog( Context->RevisionVersion )->Buffer; config.pszContent = L"Downloaded: ~ of ~ (0%)\r\nSpeed: ~ KB/s"; - config.pszExpandedInformation = L"View Changelog"; + config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file From 8610f0b6c38fdde7c447252f7fc836cdff140a6c Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 07:19:22 +1000 Subject: [PATCH 106/839] ExtendedTools: Add gpu nodes window icon --- plugins/ExtendedTools/exttools.h | 4 ++-- plugins/ExtendedTools/gpunodes.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index abff31baadd4..967f6d30f356 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -28,8 +28,8 @@ extern HWND NetworkTreeNewHandle; #define SETTING_NAME_UNLOADED_COLUMNS (PLUGIN_NAME L".UnloadedListColumns") #define ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) -#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(NtCurrentPeb()->ImageBaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(NtCurrentPeb()->ImageBaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) +#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) +#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) // Graph update message diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index bc863cb45bc2..fe9467dda387 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -121,6 +121,9 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( WindowHandle = hwndDlg; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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; From 8a5fc2313ffe7cd477706703d917b5c637381f8b Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 22:47:47 +1000 Subject: [PATCH 107/839] peview: Fix Symbol tab crash, Add Searchbox, Add more symbol types --- tools/peview/include/pdb.h | 3 +- tools/peview/include/peview.h | 65 +- tools/peview/pdb.c | 581 ++++++++-------- tools/peview/pdbprp.c | 474 ++++++++++++-- tools/peview/peview.rc | 99 ++- tools/peview/peview.vcxproj | 16 +- tools/peview/peview.vcxproj.filters | 20 + tools/peview/resource.h | 39 +- tools/peview/resources/ProcessHacker.ico | Bin 0 -> 97449 bytes tools/peview/resources/active_search.bmp | Bin 0 -> 1494 bytes tools/peview/resources/active_search.png | Bin 0 -> 569 bytes tools/peview/resources/inactive_search.bmp | Bin 0 -> 1494 bytes tools/peview/resources/inactive_search.png | Bin 0 -> 755 bytes tools/peview/searchbox.c | 728 +++++++++++++++++++++ 14 files changed, 1614 insertions(+), 411 deletions(-) create mode 100644 tools/peview/resources/ProcessHacker.ico create mode 100644 tools/peview/resources/active_search.bmp create mode 100644 tools/peview/resources/active_search.png create mode 100644 tools/peview/resources/inactive_search.bmp create mode 100644 tools/peview/resources/inactive_search.png create mode 100644 tools/peview/searchbox.c diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h index 966415a1844e..b9e7ef2addaa 100644 --- a/tools/peview/include/pdb.h +++ b/tools/peview/include/pdb.h @@ -416,9 +416,8 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( _Inout_ PPH_STRING_BUILDER TypeName ); -BOOLEAN SymbolInfo_GetTypeName( +PPH_STRING SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, - _Inout_ PPH_STRING_BUILDER TypeName, _In_ ULONG Index, _In_ PWSTR VarName ); diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 69987ec41906..f626710a2439 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -12,7 +12,9 @@ #include #include +#include #include +#include #include "resource.h" @@ -60,6 +62,16 @@ VOID PeSaveSettings( // symbols +#define WM_PV_SEARCH_FINISHED (WM_APP + 701) + +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 { @@ -67,12 +79,13 @@ typedef enum _WCT_TREE_COLUMN_ITEM_NAME 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_NONE, + PV_SYMBOL_TYPE_UNKNOWN, PV_SYMBOL_TYPE_FUNCTION, PV_SYMBOL_TYPE_SYMBOL, PV_SYMBOL_TYPE_LOCAL_VAR, @@ -81,15 +94,17 @@ typedef enum _PV_SYMBOL_TYPE PV_SYMBOL_TYPE_OBJECT_PTR, PV_SYMBOL_TYPE_STATIC_VAR, PV_SYMBOL_TYPE_GLOBAL_VAR, - PV_SYMBOL_TYPE_MEMBER, + PV_SYMBOL_TYPE_STRUCT, PV_SYMBOL_TYPE_STATIC_MEMBER, - PV_SYMBOL_TYPE_CONSTANT + PV_SYMBOL_TYPE_CONSTANT, + PV_SYMBOL_TYPE_CLASS, } PV_SYMBOL_TYPE; typedef struct _PV_SYMBOL_NODE { PH_TREENEW_NODE Node; + ULONG64 Index; PV_SYMBOL_TYPE Type; ULONG Size; ULONG64 Address; @@ -100,6 +115,43 @@ typedef struct _PV_SYMBOL_NODE 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 { @@ -155,14 +207,17 @@ typedef struct _PDB_SYMBOL_CONTEXT HWND SearchHandle; HWND TreeNewHandle; HWND ParentWindowHandle; + HANDLE SearchThreadHandle; + HANDLE UpdateTimer; ULONG64 BaseAddress; PPH_STRING FileName; PPH_STRING SearchboxText; PPH_STRING TreeText; + PPH_LIST SymbolList; PPH_LIST UdtList; - + PH_LAYOUT_MANAGER LayoutManager; ULONG TreeNewSortColumn; @@ -187,7 +242,7 @@ VOID PvPdbProperties( VOID ); -VOID PluginsAddTreeNode( +VOID PvSymbolAddTreeNode( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ PPV_SYMBOL_NODE Entry ); diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 039dc60454e3..12ebe46f59bd 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -97,6 +97,10 @@ _SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; _SymGetTypeInfo SymGetTypeInfo_I = NULL; _SymSetContext SymSetContext_I = NULL; +ULONG SearchResultsAddIndex = 0; +PPH_LIST SearchResults = NULL; +PH_QUEUED_LOCK SearchResultsLock = PH_QUEUED_LOCK_INIT; + BOOLEAN SymbolInfo_DumpBasicType( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, @@ -284,7 +288,6 @@ BOOLEAN SymbolInfo_DumpUDT( if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; - // Determine UDT kind (class/structure or union?) if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) return FALSE; @@ -637,7 +640,6 @@ BOOLEAN SymbolInfo_DumpData( Info->TypeIndex = TypeIndex; Info->dataKind = (DataKind)dataKind; - // Location, depending on the data kind switch (dataKind) { case DataIsGlobal: @@ -645,12 +647,12 @@ BOOLEAN SymbolInfo_DumpData( 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) - ULONG64 address = 0; - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_ADDRESS, &address)) return FALSE; @@ -664,9 +666,9 @@ BOOLEAN SymbolInfo_DumpData( case DataIsObjectPtr: case DataIsMember: { - // Use Offset; Address is not defined ULONG offset = 0; + // Use Offset; Address is not defined if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_OFFSET, &offset)) return FALSE; @@ -676,7 +678,6 @@ BOOLEAN SymbolInfo_DumpData( break; default: - // Unknown location Info->Address = 0; Info->Offset = 0; break; @@ -693,7 +694,6 @@ BOOLEAN SymbolInfo_DumpType( { ULONG tag = SymTagNull; - // Get the symbol tag if (!SymGetTypeInfo_I( NtCurrentProcess(), Context->BaseAddress, @@ -707,7 +707,6 @@ BOOLEAN SymbolInfo_DumpType( Info->Tag = (enum SymTagEnum)tag; - // Dump information about the symbol (depending on the tag). switch (tag) { case SymTagBaseType: @@ -1494,30 +1493,26 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( return TRUE; } -BOOLEAN SymbolInfo_GetTypeName( +PPH_STRING SymbolInfo_GetTypeName( _Inout_ PPDB_SYMBOL_CONTEXT Context, - _Inout_ PPH_STRING_BUILDER TypeName, _In_ ULONG Index, _In_ PWSTR VarName ) { PWSTR typeVarName = NULL; + PH_STRING_BUILDER typeNamesb; + + PhInitializeStringBuilder(&typeNamesb, 0x100); if (VarName) typeVarName = VarName; - // Obtain the type name - if (!SymbolInfo_GetTypeNameHelper(Index, Context, &typeVarName, TypeName)) - return FALSE; - - // Return the type name to the caller - //_snwprintf(pTypeName, MaxChars, L"%s", TypeName.String->Buffer); + if (!SymbolInfo_GetTypeNameHelper(Index, Context, &typeVarName, &typeNamesb)) + return NULL; - return TRUE; + return PhFinalStringBuilderString(&typeNamesb); } -/////////////////////////////////////////////////////////////////////////////// -// Data-to-string conversion functions PWSTR SymbolInfo_TagStr( _In_ enum SymTagEnum Tag ) @@ -1814,7 +1809,8 @@ VOID SymbolInfo_SymbolLocationStr( } else { - PhPrintPointer(Buffer, PTR_SUB_OFFSET(SymbolInfo->Address, SymbolInfo->ModBase)); + if (SymbolInfo->Address) + PhPrintPointer(Buffer, PTR_SUB_OFFSET(SymbolInfo->Address, SymbolInfo->ModBase)); } } @@ -1910,27 +1906,29 @@ BOOL CALLBACK EnumCallbackProc( case SymTagFunction: { PPDB_SYMBOL_CONTEXT context = Context; - PH_STRING_BUILDER typeName; 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); - PhInitializeStringBuilder(&typeName, 0x100); - - if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) - symbol->Data = PhFinalStringBuilderString(&typeName); - // Flags: %x, SymbolInfo->Flags // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); - PluginsAddTreeNode(context, symbol); - - // Enumerate function parameters and local variables... + // Enumerate parameters and variables... IMAGEHLP_STACK_FRAME sf; sf.InstructionOffset = SymbolInfo->Address; SymSetContext_I(NtCurrentProcess(), &sf, 0); @@ -1940,30 +1938,27 @@ BOOL CALLBACK EnumCallbackProc( case SymTagData: { PPDB_SYMBOL_CONTEXT context = Context; - PH_STRING_BUILDER typeName; PPV_SYMBOL_NODE symbol; PWSTR symDataKind; ULONG dataKindType = 0; - // TODO: Remove filter if (!SymbolInfo->Address) break; if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) break; - // Type symDataKind = SymbolInfo_DataKindStr(dataKindType); - // TODO: Remove filter - if (!wcscmp(symDataKind, L"LOCAL_VAR") || - !wcscmp(symDataKind, L"OBJECT_PTR") || - !wcscmp(symDataKind, L"PARAMETER")) - { - break; - } + //if (dataKindType == DataIsLocal || + // dataKindType == DataIsObjectPtr || + // dataKindType == DataIsParam) + //{ + // break; + //} symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); switch (dataKindType) { @@ -1986,7 +1981,7 @@ BOOL CALLBACK EnumCallbackProc( symbol->Type = PV_SYMBOL_TYPE_GLOBAL_VAR; break; case DataIsMember: - symbol->Type = PV_SYMBOL_TYPE_MEMBER; + symbol->Type = PV_SYMBOL_TYPE_STRUCT; break; case DataIsStaticMember: symbol->Type = PV_SYMBOL_TYPE_STATIC_MEMBER; @@ -1994,51 +1989,48 @@ BOOL CALLBACK EnumCallbackProc( 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); - PhInitializeStringBuilder(&typeName, 0x100); - - // Data - if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) - symbol->Data = PhFinalStringBuilderString(&typeName); - // Flags: %x, SymbolInfo->Flags // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - - PluginsAddTreeNode(context, symbol); + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); } break; default: { PPDB_SYMBOL_CONTEXT context = Context; - PH_STRING_BUILDER typeName; PPV_SYMBOL_NODE symbol; // TODO: Remove filter - if (SymbolInfo->Tag != SymTagPublicSymbol) - break; + //if (SymbolInfo->Tag != SymTagPublicSymbol) + // 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->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + symbol->Data = SymbolInfo_GetTypeName(context, SymbolInfo->TypeIndex, SymbolInfo->Name); SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - PhInitializeStringBuilder(&typeName, 0x100); - - if (SymbolInfo_GetTypeName(context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) - symbol->Data = PhFinalStringBuilderString(&typeName); - // Flags: %x, SymbolInfo->Flags // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - - PluginsAddTreeNode(context, symbol); + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); } break; } @@ -2053,31 +2045,31 @@ VOID PrintClassInfo( _In_ UdtClassInfo* Info ) { - //INT lvItemIndex; + 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); - if (Info->UDTKind == UdtClass) - { - //PhAddItemList(L"CLASS", Info->Name) - //return; - } + if (PhEqualString2(symbol->Name, L"STRUCT", TRUE)) + symbol->Type = PV_SYMBOL_TYPE_STRUCT; + + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); + + // Info.Nested + // Info->NumVariables + // Info->NumFunctions + // Info->NumBaseClasses - // UDT kind - //lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_UdtKindStr(Info->UDTKind), NULL); - // Name - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, Info->Name); - // Size - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatSize(Info->Length, -1)->Buffer); - - // Nested - //if (Info.Nested) OutputDebugString(L" Nested"); - // Number of member variables - //OutputDebugString(PhaFormatString(L" Variables: %d", Info->NumVariables)->Buffer); - // Number of member functions - //OutputDebugString(PhaFormatString(L" Functions: %d", Info->NumFunctions)->Buffer); - // Number of base classes - //OutputDebugString(PhaFormatString(L" Base classes: %d", Info->NumBaseClasses)->Buffer); - - // Extended information about member variables for (ULONG i = 0; i < Info->NumVariables; i++) { TypeInfo VarInfo; @@ -2091,22 +2083,7 @@ VOID PrintClassInfo( // Unexpected symbol tag. } else - { - //INT lvItemSubIndex; - //PH_STRING_BUILDER typeName; - // - //PhInitializeStringBuilder(&typeName, 0x100); - // - //// Variable - //lvItemSubIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_DataKindStr(VarInfo.sDataInfo.dataKind), NULL); - // - //// Name - //PhSetListViewSubItem(Context->ListviewHandle, lvItemSubIndex, 2, VarInfo.sDataInfo.Name); - // - //// Data - //if (SymbolInfo_GetTypeName(Context, &typeName, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name)) - // PhSetListViewSubItem(Context->ListviewHandle, lvItemSubIndex, 4, PhFinalStringBuilderString(&typeName)->Buffer); - //PhDeleteStringBuilder(&typeName); + { // Location //switch (VarInfo.sDataInfo.dataKind) //{ @@ -2132,11 +2109,39 @@ VOID PrintClassInfo( // break; // TODO Add support for constants //} // - // Indices " Index: %8u TypeIndex: %8u" Index, VarInfo.sDataInfo.TypeIndex + //PPV_SYMBOL_NODE symbol; + // + //symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + //memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + // + //symbol->Type = PV_SYMBOL_TYPE_MEMBER; + //symbol->Address = VarInfo.sDataInfo.Address; + //symbol->Size = (ULONG)VarInfo.sDataInfo.Offset; + //symbol->Name = PhCreateString(VarInfo.sDataInfo.Name); + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + // + // Data + //if (SymbolInfo_GetTypeName(Context, &typeName, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name)) + // symbol->Data = PhFinalStringBuilderString(&typeName); + // Flags: %x, SymbolInfo->Flags + // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex + // Nested + //if (Info.Info.sUdtClassInfo.Nested) + // printf("Nested"); + // + // Number of members + //printf(" Members: %d", Info.Info.sUdtClassInfo.NumVariables); + // + //PhAcquireQueuedLockExclusive(&SearchResultsLock); + //PhAddItemList(SearchResults, symbol); + //PhReleaseQueuedLockExclusive(&SearchResultsLock); + // + // Update the search results in batches of 2000. + //if (SearchResults->Count % 2000 == 0) + // PostMessage(Context->DialogHandle, WM_PV_SEARCH_UPDATE, 0, 0); } } - // TODO Implement information about member functions for (ULONG i = 0; i < Info->NumFunctions; i++) { TypeInfo VarInfo; @@ -2151,42 +2156,34 @@ VOID PrintClassInfo( } else { - //INT lvItemSubIndex; - //PH_STRING_BUILDER typeName; - // - //PhInitializeStringBuilder(&typeName, 0x100); - // - // Type - //lvItemSubIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, L"VARIABLE", NULL); + PPV_SYMBOL_NODE symbol; + + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + + symbol->Type = PV_SYMBOL_TYPE_STRUCT; + symbol->Address = VarInfo.sDataInfo.Address; + 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); - // Address - // SymbolInfo_SymbolLocationStr(Context, VarInfo.sDataInfo.Address); - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 1, pointer); VarInfo.sDataInfo.Address - // - // Name : SymbolInfo_DataKindStr(VarInfo.sDataInfo.dataKind) - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, VarInfo.sDataInfo.Name); - // - // Size - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, VarInfo.sDataInfo.Offset - // - // Data - //if (SymbolInfo_GetTypeName(Context, &typeName, SymbolInfo->TypeIndex, SymbolInfo->Name)) - // PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 4, PhFinalStringBuilderString(&typeName)->Buffer); - // - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - // - //PhDeleteStringBuilder(&typeName); - // // Enumerate function parameters and local variables... - // IMAGEHLP_STACK_FRAME sf; - //sf.InstructionOffset = SymbolInfo->Address; VarInfo.sDataInfo.Offset - //SymSetContext_I(NtCurrentProcess(), &sf, 0); - //SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); + IMAGEHLP_STACK_FRAME sf; + + sf.InstructionOffset = VarInfo.sDataInfo.Address; + + SymSetContext_I(NtCurrentProcess(), &sf, 0); + SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); } } - // TODO Implement information about base classes for (ULONG i = 0; i < Info->NumBaseClasses; i++) { TypeInfo baseInfo; @@ -2284,132 +2281,56 @@ VOID PrintUserDefinedTypes( } else { - //INT lvItemIndex; - // UDT kind - //lvItemIndex = PhAddListViewItem(Context->ListviewHandle, MAXINT, SymbolInfo_UdtKindStr(info.sUdtClassInfo.UDTKind), NULL); - // Name - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 2, info.sUdtClassInfo.Name); - // Size - //PhSetListViewSubItem(Context->ListviewHandle, lvItemIndex, 3, PhaFormatString(L"%I64u", info.sUdtClassInfo.Length)->Buffer); - - // Nested - //if (Info.Info.sUdtClassInfo.Nested) - // printf("Nested"); - // - // Number of members - //printf(" Members: %d", Info.Info.sUdtClassInfo.NumVariables); - } - } - } - } -} + PPV_SYMBOL_NODE symbol; -BOOLEAN GetFileParamsSize( - _In_ PWSTR FileName, - _Out_ ULONG *FileLength - ) -{ - HANDLE hFile = CreateFile( - FileName, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - 0, - NULL - ); + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); - if (hFile == INVALID_HANDLE_VALUE) - { - return FALSE; - } + symbol->Type = PV_SYMBOL_TYPE_CLASS; + //symbol->Address = VarInfo.sDataInfo.Address; + //symbol->Size = (ULONG)Info->Offset; + symbol->Name = SymbolInfo_GetTypeName(Context, index, info.sUdtUnionInfo.Name); + // PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); + symbol->Data = SymbolInfo_GetTypeName(Context, index, info.sUdtUnionInfo.Name); - *FileLength = GetFileSize(hFile, NULL); - NtClose(hFile); + if (PhEqualString2(symbol->Name, L"STRUCT", TRUE)) + symbol->Type = PV_SYMBOL_TYPE_STRUCT; - return (*FileLength != INVALID_FILE_SIZE); -} - -BOOLEAN GetFileParams( - _In_ PWSTR FileName, - _Out_ ULONG64 *BaseAddress, - _Out_ ULONG *FileLength - ) -{ - TCHAR szFileExt[_MAX_EXT] = { 0 }; - - _wsplitpath(FileName, NULL, NULL, NULL, szFileExt); - - if (_wcsicmp(szFileExt, L".PDB") == 0) - { - *BaseAddress = 0x10000000; - - if (!GetFileParamsSize(FileName, FileLength)) - return FALSE; - } - else - { - *BaseAddress = 0; - *FileLength = 0; - } - - return TRUE; -} - -// -// TODO: Move to pdbprp.c -// - -VOID PdbLoadDbgHelpFromPath( - _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"); + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - dbghelpFolder.Buffer = fullDbghelpPath->Buffer; - dbghelpFolder.Length = indexOfFileName * sizeof(WCHAR); + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); - symsrvPath = PhConcatStringRef2(&dbghelpFolder, &symsrvString); + // Print information about the union + for (ULONG i = 0; i < info.sUdtUnionInfo.NumMembers; i++) + { + TypeInfo baseInfo; - LoadLibrary(symsrvPath->Buffer); + if (!SymbolInfo_DumpType(Context, info.sUdtUnionInfo.Members[i], &baseInfo)) + { + // Continue + } + else if (baseInfo.Tag != SymTagBaseClass) + { + // Continue + } + else + { - PhDereferenceObject(symsrvPath); + } + } + } } - - PhDereferenceObject(fullDbghelpPath); } } - else - { - dbghelpModule = LoadLibrary(L"dbghelp.dll"); - } - - PhSymbolProviderCompleteInitialization(dbghelpModule); } -VOID ShowSymbolInfo( +VOID ShowModuleSymbolInfo( _In_ ULONG64 BaseAddress ) { - IMAGEHLP_MODULE64 info; - - memset(&info, 0, sizeof(IMAGEHLP_MODULE64)); - info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + IMAGEHLP_MODULE64 info = { sizeof(IMAGEHLP_MODULE64) }; if (!SymGetModuleInfoW64_I(NtCurrentProcess(), BaseAddress, &info)) return; @@ -2417,86 +2338,130 @@ VOID ShowSymbolInfo( switch (info.SymType) { case SymNone: - OutputDebugString(L"No symbols available for the module.\r\n"); + //PhCreateString(L"No symbols available for the module."); break; case SymExport: - //OutputDebugString(L"Loaded symbols: Exports\r\n"); + //PhFormatString(L"Loaded Exports symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymCoff: - //OutputDebugString(L"Loaded symbols: COFF\r\n"); + //PhFormatString(L"Loaded COFF symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymCv: - //OutputDebugString(L"Loaded symbols: CodeView\r\n"); + //PhFormatString(L"Loaded CodeView symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymSym: - //OutputDebugString(L"Loaded symbols: SYM\r\n"); + //PhFormatString(L"Loaded SYM symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymVirtual: - //OutputDebugString(L"Loaded symbols: Virtual\r\n"); + //PhFormatString(L"Loaded Virtual symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymPdb: - //OutputDebugString(L"Loaded symbols: PDB\r\n"); + //PhFormatString(L"Loaded PDB symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymDia: - //OutputDebugString(L"Loaded symbols: DIA\r\n"); + //PhFormatString(L"Loaded DIA symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); break; case SymDeferred: - //OutputDebugString(L"Loaded symbols: Deferred\r\n"); // not actually loaded + //PhCreateString(L"Loaded Deferred symbols"); // not actually loaded break; default: - //OutputDebugString(L"Loaded symbols: Unknown format.\r\n"); + //PhCreateString(L"Error: Unknown symbol format."); break; } - // Image name - if (wcslen(info.ImageName) > 0) - { - //OutputDebugString(PhaFormatString(L"Image name: %s\n", info.ImageName)->Buffer); - } + //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 +} - // Loaded image name - if (wcslen(info.LoadedImageName) > 0) +PPH_STRING PvFindDbghelpPath( + _In_ ULONG Type + ) +{ + static struct { - //OutputDebugString(PhaFormatString(L"Loaded image name: %s\n", info.LoadedImageName)->Buffer); - } - - // Loaded PDB name - if (wcslen(info.LoadedPdbName) > 0) + BOOLEAN Type; + ULONG Folder; + PWSTR AppendPath; + } locations[] = { - //OutputDebugString(PhaFormatString(L"PDB file name: %s\n", info.LoadedPdbName)->Buffer); - } +#ifdef _WIN64 + { FALSE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.1\\Debuggers\\x64\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.0\\Debuggers\\x64\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x64)\\dbghelp.dll" }, + { TRUE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.1\\Debuggers\\x64\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.0\\Debuggers\\x64\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x64)\\symsrv.dll" } +#else + { FALSE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\10\\Debuggers\\x86\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.1\\Debuggers\\x86\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.0\\Debuggers\\x86\\dbghelp.dll" }, + { FALSE, CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x86)\\dbghelp.dll" }, + { TRUE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\10\\Debuggers\\x86\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.1\\Debuggers\\x86\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.0\\Debuggers\\x86\\symsrv.dll" }, + { TRUE, CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x86)\\symsrv.dll" } +#endif + }; + + PPH_STRING path; + ULONG i; - // Is debug information unmatched ? - // (It can only happen if the debug information is contained in a separate file (.DBG or .PDB) - if (info.PdbUnmatched || info.DbgUnmatched) + for (i = 0; i < sizeof(locations) / sizeof(locations[0]); i++) { - OutputDebugString(L"Warning: Unmatched symbols. \n"); + if (locations[i].Type != Type) + continue; + + path = PhGetKnownLocation(locations[i].Folder, locations[i].AppendPath); + + if (path) + { + if (RtlDoesFileExists_U(path->Buffer)) + return path; + + PhDereferenceObject(path); + } } - // Load address: BaseAddress - //OutputDebugString(PhaFormatString(L"Line numbers: %s\n", info.LineNumbers ? L"Available" : L"Not available")->Buffer); - //OutputDebugString(PhaFormatString(L"Global symbols: %s\n", info.GlobalSymbols ? L"Available" : L"Not available")->Buffer); - //OutputDebugString(PhaFormatString(L"Type information: %s\n", info.TypeInfo ? L"Available" : L"Not available")->Buffer); - //OutputDebugString(PhaFormatString(L"Source indexing: %s\n", info.SourceIndexed ? L"Yes" : L"No")->Buffer); - //OutputDebugString(PhaFormatString(L"Public symbols: %s\n", info.Publics ? L"Available" : L"Not available")->Buffer); + return NULL; } NTSTATUS PeDumpFileSymbols( _In_ PPDB_SYMBOL_CONTEXT Context ) { + NTSTATUS status; + HANDLE fileHandle; HMODULE dbghelpHandle; HMODULE symsrvHandle; ULONG64 symbolBaseAddress; ULONG64 baseAddress = 0; - ULONG fileLength = 0; + LARGE_INTEGER fileSize; + PPH_STRING dbghelpPath; + PPH_STRING symsrvPath; - PdbLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); + dbghelpPath = PvFindDbghelpPath(FALSE); + symsrvPath = PvFindDbghelpPath(TRUE); + dbghelpHandle = LoadLibrary(dbghelpPath->Buffer); + symsrvHandle = LoadLibrary(symsrvPath->Buffer); if (!(dbghelpHandle = GetModuleHandle(L"dbghelp.dll"))) return 1; - - symsrvHandle = GetModuleHandle(L"symsrv.dll"); + SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); SymCleanup_I = PhGetProcedureAddress(dbghelpHandle, "SymCleanup", 0); SymEnumSymbolsW_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); @@ -2516,34 +2481,56 @@ NTSTATUS PeDumpFileSymbols( if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*http://msdl.microsoft.com/download/symbols")) goto CleanupExit; - if (GetFileParams(PvFileName->Buffer, &baseAddress, &fileLength)) + 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 + ); + + if (!NT_SUCCESS(status)) + return status; + + if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) { - if ((symbolBaseAddress = SymLoadModuleExW_I( - NtCurrentProcess(), - NULL, - PvFileName->Buffer, - NULL, - baseAddress, - fileLength, - NULL, - 0 - ))) - { - Context->BaseAddress = symbolBaseAddress; - Context->UdtList = PhCreateList(0x100); + NtClose(fileHandle); + return status; + } - ShowSymbolInfo(symbolBaseAddress); + if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) + baseAddress = 0x10000000; - SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, Context); + if ((symbolBaseAddress = SymLoadModuleExW_I( + NtCurrentProcess(), + NULL, + PhGetString(PvFileName), + NULL, + baseAddress, + (ULONG)fileSize.QuadPart, + NULL, + 0 + ))) + { + Context->UdtList = PhCreateList(0x100); + Context->BaseAddress = symbolBaseAddress; - // Print information about used defined types - PrintUserDefinedTypes(Context); - } + //ShowModuleSymbolInfo(symbolBaseAddress); + SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, Context); + + // Enumerate user defined types + PrintUserDefinedTypes(Context); } CleanupExit: SymCleanup_I(NtCurrentProcess()); + NtClose(fileHandle); + + PostMessage(Context->DialogHandle, WM_PV_SEARCH_FINISHED, 0, 0); + return 0; } diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 93459540ebf7..4b04ccfd2605 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -22,8 +22,232 @@ #include #include +#include #include "colmgr.h" +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, @@ -132,18 +356,18 @@ VOID PhApplyTreeNewFilters( TreeNew_NodesStructured(Support->TreeNewHandle); } -BOOLEAN PluginsNodeHashtableCompareFunction( +BOOLEAN SymbolNodeHashtableCompareFunction( _In_ PVOID Entry1, _In_ PVOID Entry2 ); -ULONG PluginsNodeHashtableHashFunction( +ULONG SymbolNodeHashtableHashFunction( _In_ PVOID Entry ); -VOID DestroyPluginsNode( +VOID PvDestroySymbolNode( _In_ PPV_SYMBOL_NODE Node ); -VOID DeletePluginsTree( +VOID PvDeleteSymbolTree( _In_ PPDB_SYMBOL_CONTEXT Context ) { @@ -153,21 +377,21 @@ VOID DeletePluginsTree( for (ULONG i = 0; i < Context->NodeList->Count; i++) { - DestroyPluginsNode(Context->NodeList->Items[i]); + PvDestroySymbolNode(Context->NodeList->Items[i]); } PhDereferenceObject(Context->NodeHashtable); PhDereferenceObject(Context->NodeList); } -struct _PH_TN_FILTER_SUPPORT* GetPluginListFilterSupport( +struct _PH_TN_FILTER_SUPPORT* GetSymbolListFilterSupport( _In_ PPDB_SYMBOL_CONTEXT Context ) { return &Context->FilterSupport; } -BOOLEAN PluginsNodeHashtableCompareFunction( +BOOLEAN SymbolNodeHashtableCompareFunction( _In_ PVOID Entry1, _In_ PVOID Entry2 ) @@ -178,20 +402,23 @@ BOOLEAN PluginsNodeHashtableCompareFunction( return PhEqualString(windowNode1->Name, windowNode2->Name, TRUE); } -ULONG PluginsNodeHashtableHashFunction( +ULONG SymbolNodeHashtableHashFunction( _In_ PVOID Entry ) { - return PhHashStringRef(&(*(PPV_SYMBOL_NODE*)Entry)->Name->sr, TRUE); + return PhHashInt64((*(PPV_SYMBOL_NODE*)Entry)->Index); } -VOID PluginsAddTreeNode( +VOID PvSymbolAddTreeNode( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ PPV_SYMBOL_NODE Entry ) { + static ULONG64 index = 0; + PhInitializeTreeNewNode(&Entry->Node); + Entry->Index = index++; memset(Entry->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM); Entry->Node.TextCache = Entry->TextCache; Entry->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; @@ -199,17 +426,13 @@ VOID PluginsAddTreeNode( PhAddEntryHashtable(Context->NodeHashtable, &Entry); PhAddItemList(Context->NodeList, Entry); - TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); - TreeNew_NodesStructured(Context->TreeNewHandle); - TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); - if (Context->FilterSupport.NodeList) { Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); } } -PPV_SYMBOL_NODE FindTreeNode( +PPV_SYMBOL_NODE PvFindSymbolNode( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ PPH_STRING Name ) @@ -225,25 +448,22 @@ PPV_SYMBOL_NODE FindTreeNode( return NULL; } -VOID WeRemoveWindowNode( +VOID PvRemoveSymbolNode( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ PPV_SYMBOL_NODE 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); - } - DestroyPluginsNode(Node); + PvDestroySymbolNode(Node); } -VOID DestroyPluginsNode( +VOID PvDestroySymbolNode( _In_ PPV_SYMBOL_NODE Node ) { @@ -276,7 +496,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(VA) { - sortResult = PhCompareString(node1->Name, node2->Name, FALSE); + sortResult = uintptrcmp(node1->Address, node2->Address); } END_SORT_FUNCTION @@ -286,7 +506,7 @@ BEGIN_SORT_FUNCTION(Name) } END_SORT_FUNCTION -BOOLEAN NTAPI PluginsTreeNewCallback( +BOOLEAN NTAPI PvSymbolTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, __in_opt PVOID Parameter1, @@ -374,8 +594,8 @@ BOOLEAN NTAPI PluginsTreeNewCallback( case PV_SYMBOL_TYPE_GLOBAL_VAR: PhInitializeStringRef(&getCellText->Text, L"GLOBAL_VAR"); break; - case PV_SYMBOL_TYPE_MEMBER: - PhInitializeStringRef(&getCellText->Text, L"MEMBER"); + case PV_SYMBOL_TYPE_STRUCT: + PhInitializeStringRef(&getCellText->Text, L"STRUCT"); break; case PV_SYMBOL_TYPE_STATIC_MEMBER: PhInitializeStringRef(&getCellText->Text, L"STATIC_MEMBER"); @@ -383,6 +603,9 @@ BOOLEAN NTAPI PluginsTreeNewCallback( case PV_SYMBOL_TYPE_CONSTANT: PhInitializeStringRef(&getCellText->Text, L"CONSTANT"); break; + case PV_SYMBOL_TYPE_CLASS: + PhInitializeStringRef(&getCellText->Text, L"CLASS"); + break; } } break; @@ -433,7 +656,7 @@ BOOLEAN NTAPI PluginsTreeNewCallback( return TRUE; case TreeNewHeaderRightClick: { - /*PH_TN_COLUMN_MENU_DATA data; + PH_TN_COLUMN_MENU_DATA data; data.TreeNewHandle = hwnd; data.MouseEvent = Parameter1; @@ -444,7 +667,7 @@ BOOLEAN NTAPI PluginsTreeNewCallback( 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);*/ + PhDeleteTreeNewColumnMenu(&data); } return TRUE; } @@ -452,18 +675,18 @@ BOOLEAN NTAPI PluginsTreeNewCallback( return FALSE; } -VOID PluginsClearTree( +VOID PvSymbolClearTree( _In_ PPDB_SYMBOL_CONTEXT Context ) { for (ULONG i = 0; i < Context->NodeList->Count; i++) - DestroyPluginsNode(Context->NodeList->Items[i]); + PvDestroySymbolNode(Context->NodeList->Items[i]); PhClearHashtable(Context->NodeHashtable); PhClearList(Context->NodeList); } -PPV_SYMBOL_NODE WeGetSelectedWindowNode( +PPV_SYMBOL_NODE PvGetSelectedSymbolNode( _In_ PPDB_SYMBOL_CONTEXT Context ) { @@ -478,7 +701,7 @@ PPV_SYMBOL_NODE WeGetSelectedWindowNode( return NULL; } -VOID WeGetSelectedWindowNodes( +VOID PvGetSelectedSymbolNodes( _In_ PPDB_SYMBOL_CONTEXT Context, _Out_ PPV_SYMBOL_NODE **Windows, _Out_ PULONG NumberOfWindows @@ -500,7 +723,7 @@ VOID WeGetSelectedWindowNodes( PhDereferenceObject(list); } -VOID InitializePluginsTree( +VOID PvInitializeSymbolTree( _In_ PPDB_SYMBOL_CONTEXT Context, _In_ HWND ParentWindowHandle, _In_ HWND TreeNewHandle @@ -508,8 +731,8 @@ VOID InitializePluginsTree( { Context->NodeHashtable = PhCreateHashtable( sizeof(PPV_SYMBOL_NODE), - PluginsNodeHashtableCompareFunction, - PluginsNodeHashtableHashFunction, + SymbolNodeHashtableCompareFunction, + SymbolNodeHashtableHashFunction, 100 ); Context->NodeList = PhCreateList(100); @@ -518,19 +741,14 @@ VOID InitializePluginsTree( Context->TreeNewHandle = TreeNewHandle; PhSetControlTheme(TreeNewHandle, L"explorer"); - TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, Context); + 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); - /* - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"Symbol"); - 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, 40, L"Size"); - PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Type"); - */ + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_SIZE, FALSE, L"Size", 40, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_SIZE, 0, 0); + TreeNew_SetSort(TreeNewHandle, 0, NoSortOrder); PPH_STRING settings = PhGetStringSetting(L"PdbTreeListColumns"); @@ -575,29 +793,107 @@ BOOLEAN WordMatchStringZ( return WordMatchStringRef(Context, &text); } -BOOLEAN TreeFilterCallback( +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; + // 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; + } + return FALSE; } +VOID CALLBACK PvSymbolTreeUpdateCallback( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ BOOLEAN TimerOrWaitFired + ) +{ + ULONG i; + + 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); + + ChangeTimerQueueTimer(NULL, Context->UpdateTimer, 1000, INFINITE); +} INT_PTR CALLBACK PvpSymbolsDlgProc( _In_ HWND hwndDlg, @@ -623,6 +919,8 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( { case WM_INITDIALOG: { + HANDLE treeNewTimer = NULL; + context = propPageContext->Context = PhAllocate(sizeof(PDB_SYMBOL_CONTEXT)); memset(context, 0, sizeof(PDB_SYMBOL_CONTEXT)); @@ -631,17 +929,41 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( context->SearchHandle = GetDlgItem(hwndDlg, IDC_SYMSEARCH); context->SearchboxText = PhReferenceEmptyString(); - InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); - //PhAddTreeNewFilter(GetPluginListFilterSupport(context), TreeFilterCallback, context); + PvCreateSearchControl(context->SearchHandle, L"Search Symbols (Ctrl+K)"); + + PvInitializeSymbolTree(context, hwndDlg, context->TreeNewHandle); + PhAddTreeNewFilter(GetSymbolListFilterSupport(context), PvSymbolTreeFilterCallback, context); + + PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); + SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); + + SearchResults = PhCreateList(0x1000); + context->SearchThreadHandle = PhCreateThread(0, PeDumpFileSymbols, context); + + if (CreateTimerQueueTimer( + &treeNewTimer, + NULL, + PvSymbolTreeUpdateCallback, + context, + 1000, + 1000, + 0 + )) + { + context->UpdateTimer = treeNewTimer; + } - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PeDumpFileSymbols, context); - - //SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_OPTIONS), TRUE); + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; case WM_DESTROY: { - DeletePluginsTree(context); + if (context->UpdateTimer) + DeleteTimerQueueTimer(NULL, context->UpdateTimer, NULL); + + NtClose(context->SearchThreadHandle); + + PvDeleteSymbolTree(context); } break; case WM_SHOWWINDOW: @@ -652,7 +974,7 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_SYMSEARCH), + PvAddPropPageLayoutItem(hwndDlg, context->SearchHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PvAddPropPageLayoutItem(hwndDlg, context->TreeNewHandle, dialogItem, PH_ANCHOR_ALL); @@ -662,23 +984,59 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( } } break; - case WM_NOTIFY: + case WM_COMMAND: { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) + switch (GET_WM_COMMAND_CMD(wParam, lParam)) { - case NM_DBLCLK: + case EN_CHANGE: { - if (header->hwndFrom == context->TreeNewHandle) + 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; + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + LPPSHNOTIFY pageNotify = (LPPSHNOTIFY)header; + + switch (pageNotify->hdr.code) + { + case PSN_SETACTIVE: + PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(GetParent(hwndDlg), IDCANCEL), TRUE); // HACK + break; + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(GetParent(hwndDlg), IDCANCEL)); + return TRUE; + } + } + break; + } return FALSE; diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 7ca0f9265d2d..4a1db839071e 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -63,26 +63,26 @@ BEGIN IDD_PEIMPORTS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END IDD_PEEXPORTS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END IDD_LIBEXPORTS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END IDD_PECLR, DIALOG @@ -95,18 +95,18 @@ BEGIN IDD_PELOADCONFIG, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END IDD_PECFG, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 292 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 + LEFTMARGIN, 3 + RIGHTMARGIN, 296 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 END IDD_PESYMBOLS, DIALOG @@ -162,7 +162,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -170,7 +170,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -178,7 +178,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -202,7 +202,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -210,7 +210,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -218,8 +218,8 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "Symbols" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "",IDC_SYMBOLTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,3,20,295,257,WS_EX_CLIENTEDGE - EDITTEXT IDC_SYMSEARCH,166,3,132,14,ES_AUTOHSCROLL + 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 @@ -233,6 +233,51 @@ BEGIN 0 END +IDD_PELOADCONFIG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_PEIMPORTS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_PEEXPORTS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_PECFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_LIBEXPORTS AFX_DIALOG_LAYOUT +BEGIN + 0 +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" + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index c3d2df1f85af..1ce6cc5d5354 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -96,7 +96,7 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) @@ -121,7 +121,7 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) @@ -149,7 +149,7 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) @@ -179,7 +179,7 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) @@ -206,6 +206,7 @@ + @@ -228,6 +229,13 @@ + + + + + + + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index 7395ed945c8f..95f381deb897 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -57,6 +57,9 @@ Source Files + + Source Files + @@ -88,4 +91,21 @@ Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + \ No newline at end of file diff --git a/tools/peview/resource.h b/tools/peview/resource.h index 56994a3e0af7..010f9d43fd37 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -2,19 +2,19 @@ // 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 IDD_PESYMBOLS 109 -#define IDC_SYMBOLTREE 110 -#define IDB_SEARCH_ACTIVE 111 -#define IDB_SEARCH_INACTIVE 112 -#define IDB_SEARCH_ACTIVE_BMP 113 -#define IDB_SEARCH_INACTIVE_BMP 114 +#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 IDB_SEARCH_ACTIVE 110 +#define IDB_SEARCH_INACTIVE 111 +#define IDB_SEARCH_ACTIVE_BMP 112 +#define IDB_SEARCH_INACTIVE_BMP 113 +#define IDC_SYMBOLTREE 119 #define IDC_TARGETMACHINE 1003 #define IDC_CHECKSUM 1004 #define IDC_SUBSYSTEM 1005 @@ -33,16 +33,19 @@ #define IDC_IMAGEBASE 1015 #define IDC_ENTRYPOINT 1016 #define IDC_SYMSEARCH 1017 -#define IDC_NAME 1044 -#define IDC_COMPANYNAME_LINK 1279 +#define IDC_NAME 1019 +#define IDC_COMPANYNAME_LINK 1020 +#define IDC_BUTTON1 1021 +#define IDC_STOP 1021 +#define IDC_PROGRESS 1022 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 116 +#define _APS_NEXT_RESOURCE_VALUE 119 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1019 -#define _APS_NEXT_SYMED_VALUE 116 +#define _APS_NEXT_CONTROL_VALUE 1023 +#define _APS_NEXT_SYMED_VALUE 115 #endif #endif diff --git a/tools/peview/resources/ProcessHacker.ico b/tools/peview/resources/ProcessHacker.ico new file mode 100644 index 0000000000000000000000000000000000000000..5225637d781f268469054fe92a7d4980ecb7fff8 GIT binary patch literal 97449 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 AX8-^I literal 0 HcmV?d00001 diff --git a/tools/peview/resources/active_search.bmp b/tools/peview/resources/active_search.bmp new file mode 100644 index 0000000000000000000000000000000000000000..61b13de9a1152694de72dd81c01c47282773298e GIT binary patch literal 1494 zcmZ?ry~fG_24+A~1Bk_eSOka}8650^YlUPk4#vGuUW6N6R zWNHFQJcaSx|X%2hHw&(QFzR`wykGxlOd1cH4UFR%aZTy0vQ4UK=!p| e$uJ%|jmI2xrFi+H=HLw;0_g|8USwG+hBE*$K{!VM literal 0 HcmV?d00001 diff --git a/tools/peview/resources/active_search.png b/tools/peview/resources/active_search.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf29576b0b50ed5ee88c1ea8565fe2e9adb9b69 GIT binary patch literal 569 zcmV-90>=G`P)sM}kty8!R#X_B;6=p#3k05!yiVY9d zlP4*H^pRy55Oc`+@ds70n9+mE9;(NrCYe{xn9wV|9VBsU+d(|PXf{(L44hUrH>@-V-11oc);or!AQP^ zGA=1rIynoT>*g-{55qdy^B+mtM4?zjp=AgQmBSj+v@_=Xhhe3lstAxc%pz)8#;6wA zW~r4l^}SZ|wEr*+mw^5uN{4!`dE+8h9B2g+;~9Laf!o+nq2=@QC~4{5 zzNMIgKr``}1C-rcX?bp5HnP>n`l5h>$RZFnesj((%HG>#cy3wt`|HP^U*3CeUNVq- zZ5@_$h2I>YUDuXnAL}xNqidV67y?vA&>V=f-`}3|{{B)38!JhqIau`~3l2ha0LuUq A!~g&Q literal 0 HcmV?d00001 diff --git a/tools/peview/resources/inactive_search.png b/tools/peview/resources/inactive_search.png new file mode 100644 index 0000000000000000000000000000000000000000..29eaa91305b3de1ce3854f19d86780a552bff734 GIT binary patch literal 755 zcmV;^k2Oj%`vu0L~h9Ndt{Z?B0N*TK5C5_$jUJj2-f3Mf; z=Q4%>h(lx3%*O9D(}M#pG|-AZmB~Y^5Kn3OGXm_qV;y8!p2Ycr{A~&Vh&eaG$uG|C z6Av0_h0HQDGp8TB=MgF`eoUj*zR2Kp69(V&kV6d|aG`-#)NJOFTW(>}f=4uY!z&#( zy!b0FYT$qi4YZzi z!U4p{@mX8WMy*7RX54w@=Gkk?VfutyFI>F1**tDmn1AarCNdTRhz$lqT^w#vqiIjf z++4ek*{XCNwS|Dwx6J)v#I?d`27J0X0K~|N-h&;@?7SR0m@dn1EiEQs_o;`$Fyb>) z{!a}>%}S)Dt6pr_b2g-Y=aI^?%}2xWn%Jm@kdeTRQJW3s_yq*>%`QLV|Bc0r1Qr_O lJMbXX`SgwQ{nsmReE`jH|7l`4W6l5o002ovPDHLkV1naFZkYf8 literal 0 HcmV?d00001 diff --git a/tools/peview/searchbox.c b/tools/peview/searchbox.c new file mode 100644 index 000000000000..17d89f752726 --- /dev/null +++ b/tools/peview/searchbox.c @@ -0,0 +1,728 @@ +/* + * 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 +#define CINTERFACE +#define COBJMACROS +#include +#include +#include +#include + +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; + +HICON PhpSearchBitmapToIcon( + _In_ HBITMAP BitmapHandle, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhpSearchFreeTheme( + _Inout_ PEDIT_CONTEXT Context + ) +{ + if (Context->BrushNormal) + DeleteObject(Context->BrushNormal); + + if (Context->BrushHot) + DeleteObject(Context->BrushHot); + + if (Context->BrushPushed) + DeleteObject(Context->BrushPushed); +} + +VOID PhpSearchInitializeFont( + _Inout_ PEDIT_CONTEXT Context + ) +{ + LOGFONT logFont; + + if (Context->WindowFont) + DeleteObject(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 + ); + + SendMessage(Context->WindowHandle, WM_SETFONT, (WPARAM)Context->WindowFont, TRUE); +} + +VOID PhpSearchInitializeTheme( + _Inout_ PEDIT_CONTEXT Context + ) +{ + Context->CXWidth = 20;// PH_SCALE_DPI(20); + Context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); + Context->BrushHot = CreateSolidBrush(RGB(205, 232, 255)); + Context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); + + 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; + } +} + +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; + 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; +} + +VOID PhpSearchInitializeImages( + _Inout_ PEDIT_CONTEXT Context + ) +{ + HBITMAP bitmap; + + Context->ImageWidth = GetSystemMetrics(SM_CXSMICON) + 4; + Context->ImageHeight = GetSystemMetrics(SM_CYSMICON) + 4; + + if (bitmap = PhLoadPngImageFromResource(PhLibImageBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) + { + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteObject(bitmap); + } + else if (bitmap = LoadImage(PhLibImageBase, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) + { + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteObject(bitmap); + } + + if (bitmap = PhLoadPngImageFromResource(PhLibImageBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) + { + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteObject(bitmap); + } + else if (bitmap = LoadImage(PhLibImageBase, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) + { + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteObject(bitmap); + } +} + +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 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 = SelectObject(bufferDc, bufferBitmap); + + if (Context->Pushed) + { + FillRect(bufferDc, &bufferRect, Context->BrushPushed); + //FrameRect(bufferDc, &bufferRect, CreateSolidBrush(RGB(0xff, 0, 0))); + } + else if (Context->Hot) + { + FillRect(bufferDc, &bufferRect, Context->BrushHot); + //FrameRect(bufferDc, &bufferRect, CreateSolidBrush(RGB(38, 160, 218))); + } + else + { + FillRect(bufferDc, &bufferRect, Context->BrushNormal); + } + + if (Edit_GetTextLength(Context->WindowHandle) > 0) + { + DrawIconEx( + bufferDc, + bufferRect.left + 1, // offset + bufferRect.top, + Context->BitmapActive, + Context->ImageWidth, + Context->ImageHeight, + 0, + NULL, + DI_NORMAL + ); + } + else + { + DrawIconEx( + bufferDc, + bufferRect.left + 2, // offset + bufferRect.top + 1, // offset + Context->BitmapInactive, + Context->ImageWidth, + Context->ImageHeight, + 0, + NULL, + DI_NORMAL + ); + } + + BitBlt(hdc, ButtonRect.left, ButtonRect.top, ButtonRect.right, ButtonRect.bottom, bufferDc, 0, 0, SRCCOPY); + SelectObject(bufferDc, oldBufferBitmap); + DeleteObject(bufferBitmap); + DeleteDC(bufferDc); + + ReleaseDC(Context->WindowHandle, hdc); +} + +LRESULT CALLBACK PhpSearchWndSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ UINT_PTR uIdSubclass, + _In_ ULONG_PTR dwRefData + ) +{ + PEDIT_CONTEXT context; + + context = (PEDIT_CONTEXT)GetProp(hWnd, L"SearchBoxContext"); + + switch (uMsg) + { + case WM_NCDESTROY: + { + PhpSearchFreeTheme(context); + + if (context->WindowFont) + DeleteObject(context->WindowFont); + + RemoveWindowSubclass(hWnd, PhpSearchWndSubclassProc, uIdSubclass); + RemoveProp(hWnd, L"SearchBoxContext"); + PhFree(context); + } + break; + case WM_ERASEBKGND: + return 1; + case WM_NCCALCSIZE: + { + LPNCCALCSIZE_PARAMS ncCalcSize = (NCCALCSIZE_PARAMS*)lParam; + + // Let Windows handle the non-client defaults. + DefSubclassProc(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; + + // Let Windows handle the non-client defaults. + DefSubclassProc(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); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Draw the button. + PhpSearchDrawButton(context, windowRect); + } + 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 DefSubclassProc(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 = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); + + iconInfo.fIcon = TRUE; + iconInfo.hbmColor = BitmapHandle; + iconInfo.hbmMask = screenBitmap; + + icon = CreateIconIndirect(&iconInfo); + + DeleteObject(screenBitmap); + DeleteDC(screenDc); + + return icon; +} + +VOID PvCreateSearchControl( + _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; + + //PhpSearchInitializeTheme(context); + PhpSearchInitializeImages(context); + + // Set initial text + if (BannerText) + Edit_SetCueBannerText(context->WindowHandle, BannerText); + + // Set our window context data. + SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); + + // Subclass the Edit control window procedure. + SetWindowSubclass(context->WindowHandle, PhpSearchWndSubclassProc, 0, (ULONG_PTR)context); + + // Initialize the theme parameters. + SendMessage(context->WindowHandle, WM_THEMECHANGED, 0, 0); +} From 9f7caa491adcfe34f9cca6e62092283ffd09978e Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 23:47:08 +1000 Subject: [PATCH 108/839] peview: Fix symbol filter --- tools/peview/include/peview.h | 1 + tools/peview/pdb.c | 173 ++++++++++------------------------ tools/peview/pdbprp.c | 5 + 3 files changed, 55 insertions(+), 124 deletions(-) diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index f626710a2439..91d189acc697 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -97,6 +97,7 @@ typedef enum _PV_SYMBOL_TYPE PV_SYMBOL_TYPE_STRUCT, PV_SYMBOL_TYPE_STATIC_MEMBER, PV_SYMBOL_TYPE_CONSTANT, + PV_SYMBOL_TYPE_MEMBER, PV_SYMBOL_TYPE_CLASS, } PV_SYMBOL_TYPE; diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 12ebe46f59bd..af7357660b51 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -478,15 +478,7 @@ BOOLEAN SymbolInfo_DumpFunctionType( ULONG Children[cMaxChildren]; ULONG NumChildren = 0; - if( !GetChildren( m_hProcess, BaseAddress, Index, Children, NumChildren, cMaxChildren ) ) - { - ULONG ErrCode = 0; - _ASSERTE( !_T("GetChildren() failed.") ); - } - else - { - ULONG ErrCode = 0; - } + GetChildren( m_hProcess, BaseAddress, Index, Children, NumChildren, cMaxChildren) */ // Dump function arguments @@ -1922,8 +1914,6 @@ BOOL CALLBACK EnumCallbackProc( ); SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex PhAcquireQueuedLockExclusive(&SearchResultsLock); PhAddItemList(SearchResults, symbol); PhReleaseQueuedLockExclusive(&SearchResultsLock); @@ -2045,30 +2035,21 @@ VOID PrintClassInfo( _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; + //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->Name = SymbolInfo_GetTypeName(Context, Index, Info->Name);// PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); //symbol->Data = SymbolInfo_GetTypeName(Context, Index, Info->Name); - - if (PhEqualString2(symbol->Name, L"STRUCT", TRUE)) - symbol->Type = PV_SYMBOL_TYPE_STRUCT; - //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - - PhAcquireQueuedLockExclusive(&SearchResultsLock); - PhAddItemList(SearchResults, symbol); - PhReleaseQueuedLockExclusive(&SearchResultsLock); - - // Info.Nested - // Info->NumVariables - // Info->NumFunctions - // Info->NumBaseClasses + //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++) { @@ -2083,62 +2064,46 @@ VOID PrintClassInfo( // Unexpected symbol tag. } else - { - // Location - //switch (VarInfo.sDataInfo.dataKind) - //{ - //case DataIsGlobal: - //case DataIsStaticLocal: - //case DataIsFileStatic: - //case DataIsStaticMember: - // { - // // Use Address - // // " Address: %16I64x" VarInfo.sDataInfo.Address - // } - // break; - //case DataIsLocal: - //case DataIsParam: - //case DataIsObjectPtr: - //case DataIsMember: - // { - // // Use Offset - // // " Offset: %8d" (long)VarInfo.sDataInfo.Offset - // } - // break; - //default: - // break; // TODO Add support for constants - //} - // + { //PPV_SYMBOL_NODE symbol; // //symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); //memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); // //symbol->Type = PV_SYMBOL_TYPE_MEMBER; - //symbol->Address = VarInfo.sDataInfo.Address; + // + // Location + switch (VarInfo.sDataInfo.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); - // - // Data - //if (SymbolInfo_GetTypeName(Context, &typeName, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name)) - // symbol->Data = PhFinalStringBuilderString(&typeName); - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex - // Nested - //if (Info.Info.sUdtClassInfo.Nested) - // printf("Nested"); - // - // Number of members - //printf(" Members: %d", Info.Info.sUdtClassInfo.NumVariables); - // + //Info.Info.sUdtClassInfo.Nested + //Info.Info.sUdtClassInfo.NumVariables //PhAcquireQueuedLockExclusive(&SearchResultsLock); //PhAddItemList(SearchResults, symbol); //PhReleaseQueuedLockExclusive(&SearchResultsLock); - // - // Update the search results in batches of 2000. - //if (SearchResults->Count % 2000 == 0) - // PostMessage(Context->DialogHandle, WM_PV_SEARCH_UPDATE, 0, 0); } } @@ -2161,7 +2126,7 @@ VOID PrintClassInfo( symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); - symbol->Type = PV_SYMBOL_TYPE_STRUCT; + symbol->Type = PV_SYMBOL_TYPE_FUNCTION; symbol->Address = VarInfo.sDataInfo.Address; symbol->Size = (ULONG)VarInfo.sDataInfo.Offset; symbol->Name = PhCreateString(VarInfo.sDataInfo.Name); @@ -2281,45 +2246,9 @@ VOID PrintUserDefinedTypes( } else { - 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.sUdtUnionInfo.Name); - // PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); - symbol->Data = SymbolInfo_GetTypeName(Context, index, info.sUdtUnionInfo.Name); - - if (PhEqualString2(symbol->Name, L"STRUCT", TRUE)) - symbol->Type = PV_SYMBOL_TYPE_STRUCT; - - //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - - PhAcquireQueuedLockExclusive(&SearchResultsLock); - PhAddItemList(SearchResults, symbol); - PhReleaseQueuedLockExclusive(&SearchResultsLock); - - // Print information about the union - for (ULONG i = 0; i < info.sUdtUnionInfo.NumMembers; i++) - { - TypeInfo baseInfo; - - if (!SymbolInfo_DumpType(Context, info.sUdtUnionInfo.Members[i], &baseInfo)) - { - // Continue - } - else if (baseInfo.Tag != SymTagBaseClass) - { - // Continue - } - else - { - - } - } + //info.sUdtUnionInfo.Name + //PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); + //SymbolInfo_GetTypeName(Context, index, info.sUdtUnionInfo.Name); } } } @@ -2448,7 +2377,6 @@ NTSTATUS PeDumpFileSymbols( HANDLE fileHandle; HMODULE dbghelpHandle; HMODULE symsrvHandle; - ULONG64 symbolBaseAddress; ULONG64 baseAddress = 0; LARGE_INTEGER fileSize; PPH_STRING dbghelpPath; @@ -2473,7 +2401,7 @@ NTSTATUS PeDumpFileSymbols( SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); SymSetContext_I = PhGetProcedureAddress(dbghelpHandle, "SymSetContext", 0); - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME | SYMOPT_ALLOW_ZERO_ADDRESS); // SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED + SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); if (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) return 1; @@ -2503,22 +2431,19 @@ NTSTATUS PeDumpFileSymbols( if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) baseAddress = 0x10000000; - if ((symbolBaseAddress = SymLoadModuleExW_I( + if (Context->BaseAddress = SymLoadModuleExW_I( NtCurrentProcess(), - NULL, + fileHandle, PhGetString(PvFileName), NULL, baseAddress, (ULONG)fileSize.QuadPart, NULL, 0 - ))) + )) { - Context->UdtList = PhCreateList(0x100); - Context->BaseAddress = symbolBaseAddress; - //ShowModuleSymbolInfo(symbolBaseAddress); - SymEnumSymbolsW_I(NtCurrentProcess(), symbolBaseAddress, NULL, EnumCallbackProc, Context); + SymEnumSymbolsW_I(NtCurrentProcess(), Context->BaseAddress, NULL, EnumCallbackProc, Context); // Enumerate user defined types PrintUserDefinedTypes(Context); diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 4b04ccfd2605..64f8ffdb0742 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -606,6 +606,9 @@ BOOLEAN NTAPI PvSymbolTreeNewCallback( case PV_SYMBOL_TYPE_CLASS: PhInitializeStringRef(&getCellText->Text, L"CLASS"); break; + case PV_SYMBOL_TYPE_MEMBER: + PhInitializeStringRef(&getCellText->Text, L"MEMBER"); + break; } } break; @@ -938,6 +941,8 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); SearchResults = PhCreateList(0x1000); + context->UdtList = PhCreateList(0x100); + context->SearchThreadHandle = PhCreateThread(0, PeDumpFileSymbols, context); if (CreateTimerQueueTimer( From 9f672ba0ac23a6f18cc49c28b36bf59f4e1b4cb7 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 17 May 2017 23:52:34 +1000 Subject: [PATCH 109/839] peview: Fix symbol size column --- tools/peview/include/peview.h | 1 + tools/peview/pdbprp.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 91d189acc697..253606b17cdf 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -111,6 +111,7 @@ typedef struct _PV_SYMBOL_NODE 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]; diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 64f8ffdb0742..f067dff423c8 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -621,6 +621,19 @@ BOOLEAN NTAPI PvSymbolTreeNewCallback( 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; } From 496a629b6681d5b73009377d9cd4cd759ef59fa6 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 01:45:46 +1000 Subject: [PATCH 110/839] Fix modules tab LoadCount for x64 (1/2) --- phlib/native.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/phlib/native.c b/phlib/native.c index 6a70c8fe1400..056c2a60e809 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -3136,6 +3136,24 @@ BOOLEAN NTAPI PhpEnumProcessModulesCallback( } } + if (WindowsVersion >= WINDOWS_8) + { + LDR_DDAG_NODE ldrDagNode = { 0 }; + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + Entry->DdagNode, + &ldrDagNode, + sizeof(LDR_DDAG_NODE), + NULL + ))) + { + // HACK: Fixup the module load count (64bit only). + // Temp fix until PhpModuleQueryWorker can be updated with Stage2. + Entry->ObsoleteLoadCount = (USHORT)ldrDagNode.LoadCount; + } + } + // Execute the callback. cont = parameters->Callback(Entry, parameters->Context); From dc6a8a94f7e4b381090b46eb8e1b9fd7de052dbe Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 02:00:14 +1000 Subject: [PATCH 111/839] Fix build warnings --- ProcessHacker.sln | 2 +- ProcessHacker/ProcessHacker.vcxproj | 2 +- phlib/phlib.vcxproj | 2 +- phlib/phlib.vcxproj.filters | 734 ++++++++++++++-------------- tools/peview/pdb.c | 20 +- tools/peview/pdbprp.c | 6 +- 6 files changed, 381 insertions(+), 385 deletions(-) diff --git a/ProcessHacker.sln b/ProcessHacker.sln index b9f1116bcef8..c2fa6bcc9b31 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 15.0.26228.4 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{2758DC86-368B-430C-9D29-F1EF20032A71}" EndProject diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 6273867a710a..f212a670d21b 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index f9b2a7f82826..cf67c6719a83 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index aef40882c991..cae851a27dce 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -1,368 +1,368 @@ - - - - - {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} - - - - - 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 - - - - - 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 - - + + + + + {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} + + + + + 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 + + + + + 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 + + \ No newline at end of file diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index af7357660b51..b1efe29dedc2 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -2374,7 +2374,7 @@ NTSTATUS PeDumpFileSymbols( ) { NTSTATUS status; - HANDLE fileHandle; + HANDLE fileHandle = NULL; HMODULE dbghelpHandle; HMODULE symsrvHandle; ULONG64 baseAddress = 0; @@ -2407,9 +2407,9 @@ NTSTATUS PeDumpFileSymbols( return 1; if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*http://msdl.microsoft.com/download/symbols")) - goto CleanupExit; + return 1; - status = PhCreateFileWin32( + if (!NT_SUCCESS(status = PhCreateFileWin32( &fileHandle, PhGetString(PvFileName), FILE_GENERIC_READ, @@ -2417,16 +2417,11 @@ NTSTATUS PeDumpFileSymbols( FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - return status; + ))) + return 1; if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) - { - NtClose(fileHandle); - return status; - } + goto CleanupExit; if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) baseAddress = 0x10000000; @@ -2453,7 +2448,8 @@ NTSTATUS PeDumpFileSymbols( SymCleanup_I(NtCurrentProcess()); - NtClose(fileHandle); + if (fileHandle) + NtClose(fileHandle); PostMessage(Context->DialogHandle, WM_PV_SEARCH_FINISHED, 0, 0); diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index f067dff423c8..05e833a0d16c 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -40,8 +40,8 @@ VOID PhInitializeTreeNewColumnMenuEx( PPH_EMENU_ITEM resetSortMenuItem = NULL; PPH_EMENU_ITEM sizeColumnToFitMenuItem; PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem; - PPH_EMENU_ITEM hideColumnMenuItem; - PPH_EMENU_ITEM chooseColumnsMenuItem; + PPH_EMENU_ITEM hideColumnMenuItem = NULL; + PPH_EMENU_ITEM chooseColumnsMenuItem = NULL; ULONG minimumNumberOfColumns; Data->Menu = PhCreateEMenu(); @@ -496,7 +496,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(VA) { - sortResult = uintptrcmp(node1->Address, node2->Address); + sortResult = uintptrcmp((ULONG_PTR)node1->Address, (ULONG_PTR)node2->Address); } END_SORT_FUNCTION From 7195ce76c0d35bb4cbc3b9661440b0cbb7f7ae1e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 08:08:19 +1000 Subject: [PATCH 112/839] peview: Re-add some filters --- tools/peview/pdb.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index b1efe29dedc2..6b12b042e63a 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -1935,17 +1935,24 @@ BOOL CALLBACK EnumCallbackProc( if (!SymbolInfo->Address) break; - if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + SymbolInfo->ModBase, + SymbolInfo->Index, + TI_GET_DATAKIND, + &dataKindType + )) + { break; + } symDataKind = SymbolInfo_DataKindStr(dataKindType); - //if (dataKindType == DataIsLocal || - // dataKindType == DataIsObjectPtr || - // dataKindType == DataIsParam) - //{ - // break; - //} + if ( + dataKindType == DataIsLocal || + dataKindType == DataIsParam + ) // || dataKindType == DataIsObjectPtr) + break; symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); @@ -1990,8 +1997,6 @@ BOOL CALLBACK EnumCallbackProc( symbol->Data = SymbolInfo_GetTypeName(context, SymbolInfo->TypeIndex, SymbolInfo->Name); SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex PhAcquireQueuedLockExclusive(&SearchResultsLock); PhAddItemList(SearchResults, symbol); PhReleaseQueuedLockExclusive(&SearchResultsLock); @@ -2016,8 +2021,6 @@ BOOL CALLBACK EnumCallbackProc( symbol->Data = SymbolInfo_GetTypeName(context, SymbolInfo->TypeIndex, SymbolInfo->Name); SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); - // Flags: %x, SymbolInfo->Flags - // Index: %8u, TypeIndex: %8u, SymbolInfo->Index, SymbolInfo->TypeIndex PhAcquireQueuedLockExclusive(&SearchResultsLock); PhAddItemList(SearchResults, symbol); PhReleaseQueuedLockExclusive(&SearchResultsLock); From 71f40f7a34357e4d18e05f34368db493479d0eed Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 08:13:44 +1000 Subject: [PATCH 113/839] BuildTool: Update binaries to latest revision --- build/build_changelog.cmd | 7 + tools/CustomBuildTool/Source Files/Build.cs | 395 ++++++++---------- .../Source Files/NativeMethods.cs | 35 +- tools/CustomBuildTool/Source Files/Program.cs | 227 +++++----- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 160256 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 73216 bytes .../CustomSetupTool/CustomSetupTool/extract.c | 4 +- 7 files changed, 305 insertions(+), 363 deletions(-) create mode 100644 build/build_changelog.cmd 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/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index ddacc7449635..f0073edf10fd 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -16,6 +16,7 @@ public static class Build private static string BuildOutputFolder = "build"; private static string BuildVersion; private static string BuildLongVersion; + private static string BuildCount; private static string BuildRevision; private static string BuildMessage; private static long BuildSetupFileLength; @@ -223,63 +224,51 @@ public static void CleanupBuildEnvironment() } } - public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo) + public static string GetBuildLogString() { - if (ShowBuildInfo) - { - Program.PrintColorMessage("Build: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(Platform, ConsoleColor.White, false); - } - - string currentGitTag = Win32.ShellExecute(GitExePath, "describe --abbrev=0 --tags --always"); - string latestGitRevision = Win32.ShellExecute(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\""); + 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(); + } - if (string.IsNullOrEmpty(latestGitRevision)) + public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) + { + string currentBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); + string currentCommitTag = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD + Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(currentBranch, ConsoleColor.White); + Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(currentCommitTag, ConsoleColor.White); + + 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"; - else - BuildRevision = latestGitRevision.Trim(); + if (string.IsNullOrEmpty(BuildCount)) + BuildCount = "0"; BuildVersion = "3.0." + BuildRevision; - BuildLongVersion = "3.0.0." + BuildRevision; - BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); - + BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; + if (ShowBuildInfo) { - string buildMessage = string.Empty; + Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(BuildLongVersion + Environment.NewLine, ConsoleColor.White); - if (BuildNightly) + if (ShowLogInfo) { - buildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); - } - else - { - 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(yellow)%h%Creset)\" --abbrev-commit"); - } - - string currentBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); - string currentCommitTag = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD - //string latestGitCount = Win32.GitExecCommand("rev-list --count " + BuildBranch); - - if (!string.IsNullOrEmpty(currentBranch)) - { - Program.PrintColorMessage(Environment.NewLine + "Branch: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(currentBranch, ConsoleColor.White); - } - - if (!string.IsNullOrEmpty(BuildVersion)) - { - Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildVersion, ConsoleColor.White); - } + if (BuildNightly) + BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); + else + { + 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); - Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(currentCommitTag + Environment.NewLine, ConsoleColor.White); + //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 5 --date=format:%Y-%m-%d --pretty=format:\"%C(green)[%cd]%Creset %C(bold blue)%an%Creset %<(65,trunc)%s%Creset (%C(yellow)%h%Creset)\" --abbrev-commit"); + } - if (!string.IsNullOrEmpty(buildMessage)) - { - Console.WriteLine(buildMessage + Environment.NewLine); + Console.WriteLine(BuildMessage + Environment.NewLine); } } } @@ -288,21 +277,20 @@ public static void ShowBuildStats() { TimeSpan buildTime = DateTime.Now - TimeStart; - Console.WriteLine(); - Console.WriteLine("Build Time: " + buildTime.Minutes + " minute(s), " + buildTime.Seconds + " second(s)"); - Console.WriteLine("Build complete."); + Console.WriteLine( + Environment.NewLine + "Build Time: " + + buildTime.Minutes + " minute(s), " + + buildTime.Seconds + " second(s)"); } public static bool CopyTextFiles() { - Program.PrintColorMessage("Copying text files...", ConsoleColor.Cyan); - try { - File.Copy("README.md", "bin\\README.txt", true); - File.Copy("CHANGELOG.txt", "bin\\CHANGELOG.txt", true); - File.Copy("COPYRIGHT.txt", "bin\\COPYRIGHT.txt", true); - File.Copy("LICENSE.txt", "bin\\LICENSE.txt", true); + Win32.CopyIfNewer("README.md", "bin\\README.txt"); + Win32.CopyIfNewer("CHANGELOG.txt", "bin\\CHANGELOG.txt"); + Win32.CopyIfNewer("COPYRIGHT.txt", "bin\\COPYRIGHT.txt"); + Win32.CopyIfNewer("LICENSE.txt", "bin\\LICENSE.txt"); } catch (Exception ex) { @@ -313,53 +301,45 @@ public static bool CopyTextFiles() return true; } - public static bool CopyKProcessHacker(bool DebugBuild) - { - Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); - - try - { - if (DebugBuild) - { - File.Copy("KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", "bin\\Debug32\\kprocesshacker.sys", true); - File.Copy("KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", "bin\\Debug64\\kprocesshacker.sys", true); - } - else - { - File.Copy("KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", "bin\\Release32\\kprocesshacker.sys", true); - File.Copy("KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", "bin\\Release64\\kprocesshacker.sys", true); - } - } - catch (Exception ex) - { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); - return false; - } - - return true; - } - public static bool CopyLibFiles(BuildFlags Flags) { - Program.PrintColorMessage("Copying Plugin SDK linker files...", ConsoleColor.Cyan); - try { if (Flags.HasFlag(BuildFlags.BuildDebug)) { if (Flags.HasFlag(BuildFlags.Build32bit)) - File.Copy("bin\\Debug32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + { + Win32.CopyIfNewer( + "bin\\Debug32\\ProcessHacker.lib", + "sdk\\lib\\i386\\ProcessHacker.lib" + ); + } if (Flags.HasFlag(BuildFlags.Build64bit)) - File.Copy("bin\\Debug64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + { + Win32.CopyIfNewer( + "bin\\Debug64\\ProcessHacker.lib", + "sdk\\lib\\amd64\\ProcessHacker.lib" + ); + } } else { if (Flags.HasFlag(BuildFlags.Build32bit)) - File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); + { + Win32.CopyIfNewer( + "bin\\Release32\\ProcessHacker.lib", + "sdk\\lib\\i386\\ProcessHacker.lib" + ); + } if (Flags.HasFlag(BuildFlags.Build64bit)) - File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + { + Win32.CopyIfNewer( + "bin\\Release64\\ProcessHacker.lib", + "sdk\\lib\\amd64\\ProcessHacker.lib" + ); + } } } catch (Exception ex) @@ -384,10 +364,10 @@ public static bool CopyWow64Files(BuildFlags Flags) if (!Directory.Exists("bin\\Debug64\\x86\\plugins")) Directory.CreateDirectory("bin\\Debug64\\x86\\plugins"); - File.Copy("bin\\Debug32\\ProcessHacker.exe", "bin\\Debug64\\x86\\ProcessHacker.exe", true); - File.Copy("bin\\Debug32\\ProcessHacker.pdb", "bin\\Debug64\\x86\\ProcessHacker.pdb", true); - File.Copy("bin\\Debug32\\plugins\\DotNetTools.dll", "bin\\Debug64\\x86\\plugins\\DotNetTools.dll", true); - File.Copy("bin\\Debug32\\plugins\\DotNetTools.pdb", "bin\\Debug64\\x86\\plugins\\DotNetTools.pdb", true); + 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"); } else { @@ -396,14 +376,22 @@ public static bool CopyWow64Files(BuildFlags Flags) if (!Directory.Exists("bin\\Release64\\x86\\plugins")) Directory.CreateDirectory("bin\\Release64\\x86\\plugins"); - if (File.Exists("bin\\Release32\\ProcessHacker.exe")) - File.Copy("bin\\Release32\\ProcessHacker.exe", "bin\\Release64\\x86\\ProcessHacker.exe", true); - if (File.Exists("bin\\Release32\\ProcessHacker.pdb")) - File.Copy("bin\\Release32\\ProcessHacker.pdb", "bin\\Release64\\x86\\ProcessHacker.pdb", true); - if (File.Exists("bin\\Release32\\plugins\\DotNetTools.dll")) - File.Copy("bin\\Release32\\plugins\\DotNetTools.dll", "bin\\Release64\\x86\\plugins\\DotNetTools.dll", true); - if (File.Exists("bin\\Release32\\plugins\\DotNetTools.pdb")) - File.Copy("bin\\Release32\\plugins\\DotNetTools.pdb", "bin\\Release64\\x86\\plugins\\DotNetTools.pdb", true); + 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" + ); } } catch (Exception ex) @@ -417,8 +405,6 @@ public static bool CopyWow64Files(BuildFlags Flags) public static bool CopyPluginSdkHeaders() { - Program.PrintColorMessage("Copying Plugin SDK headers...", ConsoleColor.Cyan); - try { foreach (string folder in sdk_directories) @@ -438,27 +424,28 @@ public static bool CopyPluginSdkHeaders() // Copy the plugin SDK headers foreach (string file in phnt_headers) - File.Copy("phnt\\include\\" + file, "sdk\\include\\" + file, true); + Win32.CopyIfNewer("phnt\\include\\" + file, "sdk\\include\\" + file); foreach (string file in phlib_headers) - File.Copy("phlib\\include\\" + file, "sdk\\include\\" + file, true); - File.Copy("phlib\\mxml\\mxml.h", "sdk\\include\\mxml.h", true); + Win32.CopyIfNewer("phlib\\include\\" + file, "sdk\\include\\" + file); + + Win32.CopyIfNewer("phlib\\mxml\\mxml.h", "sdk\\include\\mxml.h"); // Copy readme - File.Copy("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt", true); + Win32.CopyIfNewer("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt"); // Copy symbols - File.Copy("bin\\Release32\\ProcessHacker.pdb", "sdk\\dbg\\i386\\ProcessHacker.pdb", true); - File.Copy("bin\\Release64\\ProcessHacker.pdb", "sdk\\dbg\\amd64\\ProcessHacker.pdb", true); - File.Copy("KProcessHacker\\bin\\i386\\kprocesshacker.pdb", "sdk\\dbg\\i386\\kprocesshacker.pdb", true); - File.Copy("KProcessHacker\\bin\\amd64\\kprocesshacker.pdb", "sdk\\dbg\\amd64\\kprocesshacker.pdb", true); + 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 - File.Copy("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib", true); - File.Copy("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib", true); + 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 - File.Copy("plugins\\SamplePlugin\\main.c", "sdk\\samples\\SamplePlugin\\main.c", true); - File.Copy("plugins\\SamplePlugin\\SamplePlugin.sln", "sdk\\samples\\SamplePlugin\\SamplePlugin.sln", true); - File.Copy("plugins\\SamplePlugin\\SamplePlugin.vcxproj", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj", true); - File.Copy("plugins\\SamplePlugin\\SamplePlugin.vcxproj.filters", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj.filters", true); - File.Copy("plugins\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll", "sdk\\samples\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll", true); + 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) { @@ -471,77 +458,14 @@ public static bool CopyPluginSdkHeaders() public static bool CopyVersionHeader() { - Program.PrintColorMessage("Copying Plugin SDK version header...", ConsoleColor.Cyan); - try { HeaderGen gen = new HeaderGen(); gen.Execute(); - File.Copy("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", true); - File.Copy("ProcessHacker\\sdk\\phdk.h", "sdk\\include\\phdk.h", true); - File.Copy("ProcessHacker\\resource.h", "sdk\\include\\phappresource.h", true); - } - catch (Exception ex) - { - Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); - return false; - } - - return true; - } - - public static bool CopyRedistFiles(BuildFlags Flags) - { - string dbghelp32RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x86\\dbghelp.dll"); - string dbghelp64RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); - string symsrv32RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x86\\symsrv.dll"); - string symsrv64RedistDll = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\Debuggers\\x64\\symsrv.dll"); - - Program.PrintColorMessage("Copying redist files...", ConsoleColor.Cyan); - - try - { - if (Flags.HasFlag(BuildFlags.BuildDebug)) - { - if (Flags.HasFlag(BuildFlags.Build32bit)) - { - if (File.Exists(dbghelp32RedistDll)) - File.Copy(dbghelp32RedistDll, "bin\\Debug32\\dbghelp.dll", true); - - if (File.Exists(symsrv32RedistDll)) - File.Copy(symsrv32RedistDll, "bin\\Debug32\\symsrv.dll", true); - } - - if (Flags.HasFlag(BuildFlags.Build64bit)) - { - if (File.Exists(dbghelp64RedistDll)) - File.Copy(dbghelp64RedistDll, "bin\\Debug64\\dbghelp.dll", true); - - if (File.Exists(symsrv64RedistDll)) - File.Copy(symsrv64RedistDll, "bin\\Debug64\\symsrv.dll", true); - } - } - else - { - if (Flags.HasFlag(BuildFlags.Build32bit)) - { - if (File.Exists(dbghelp32RedistDll)) - File.Copy(dbghelp32RedistDll, "bin\\Release32\\dbghelp.dll", true); - - if (File.Exists(symsrv32RedistDll)) - File.Copy(symsrv32RedistDll, "bin\\Release32\\symsrv.dll", true); - } - - if (Flags.HasFlag(BuildFlags.Build64bit)) - { - if (File.Exists(dbghelp64RedistDll)) - File.Copy(dbghelp64RedistDll, "bin\\Release64\\dbghelp.dll", true); - - if (File.Exists(symsrv64RedistDll)) - File.Copy(symsrv64RedistDll, "bin\\Release64\\symsrv.dll", true); - } - } + 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) { @@ -554,8 +478,6 @@ public static bool CopyRedistFiles(BuildFlags Flags) public static bool FixupResourceHeader() { - Program.PrintColorMessage("Building Plugin SDK resource header...", ConsoleColor.Cyan); - try { string phappContent = File.ReadAllText("sdk\\include\\phappresource.h"); @@ -576,31 +498,6 @@ public static bool FixupResourceHeader() return true; } - public static bool UpdateHeaderFileVersion() - { - try - { - if (File.Exists("ProcessHacker\\include\\phapprev.h")) - File.Delete("ProcessHacker\\include\\phapprev.h"); - - File.WriteAllText("ProcessHacker\\include\\phapprev.h", -@"#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION " + BuildRevision + @" - -#endif // PHAPPREV_H -"); - } - catch (Exception ex) - { - Program.PrintColorMessage("[phapprev] " + ex.ToString(), ConsoleColor.Yellow); - return false; - } - - return true; - } - public static bool BuildPublicHeaderFiles() { Program.PrintColorMessage("Building public SDK headers...", ConsoleColor.Cyan); @@ -619,11 +516,9 @@ public static bool BuildPublicHeaderFiles() return true; } - public static bool BuildKphSignatureFile(bool DebugBuild) + public static bool CopyKProcessHacker(bool DebugBuild) { - string output; - - Program.PrintColorMessage("Building KPH signature...", ConsoleColor.Cyan); + Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); if (!File.Exists(CustomSignToolPath)) { @@ -659,13 +554,15 @@ public static bool BuildKphSignatureFile(bool DebugBuild) File.Create("bin\\Debug32\\ProcessHacker.sig").Dispose(); File.Create("bin\\Debug64\\ProcessHacker.sig").Dispose(); - if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"))) + 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); return false; } - if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"))) + 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); return false; @@ -693,23 +590,56 @@ public static bool BuildKphSignatureFile(bool DebugBuild) File.Create("bin\\Release32\\ProcessHacker.sig").Dispose(); File.Create("bin\\Release64\\ProcessHacker.sig").Dispose(); - if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"))) + 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("[ERROR] (Release32) " + output, ConsoleColor.Red); return false; } - if (!string.IsNullOrEmpty(output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"))) + output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"); + if (!string.IsNullOrEmpty(output)) { Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red); return false; } } + try + { + if (DebugBuild) + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", + "bin\\Debug32\\kprocesshacker.sys" + ); + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", + "bin\\Debug64\\kprocesshacker.sys" + ); + } + else + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", + "bin\\Release32\\kprocesshacker.sys" + ); + 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 BuildSecureFiles() + 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); @@ -753,7 +683,7 @@ public static bool BuildSetupExe() { Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan, true, BuildFlags.BuildVerbose); - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildVerbose)) + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit)) return false; try @@ -864,6 +794,8 @@ public static bool BuildSrcZip() public static bool BuildPdbZip() { + Program.PrintColorMessage("Building build-pdb.zip...", ConsoleColor.Cyan); + try { Zip.CreateCompressedPdbFromFolder(".\\", BuildOutputFolder + "\\processhacker-build-pdb.zip"); @@ -962,7 +894,7 @@ public static void WebServiceUpdateConfig() Updated = TimeStart.ToString("o"), Version = BuildVersion, FileLength = BuildSetupFileLength.ToString(), - ForumUrl = "https://wj32.org/processhacker/forums/viewtopic.php?t=2315", + ForumUrl = "https://wj32.org/processhacker/nightly.php", Setupurl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-setup.exe", Binurl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-bin.zip", HashSetup = BuildSetupHash, @@ -1076,11 +1008,26 @@ public static bool AppveyorUploadBuildFiles() } } + if (File.Exists(releaseFileArray[0])) + { + try + { + Win32.ShellExecute("appveyor", "UpdateBuild -Version \"1.0 -$version\" "); + } + catch (Exception ex) + { + Program.PrintColorMessage("[WebServicePushArtifact] " + ex, ConsoleColor.Red); + return false; + } + } + return true; } public static bool BuildSolution(string Solution, BuildFlags Flags) { + //string buildParams = "/p:DefineConstants=\"PH_BUILD_API=1;PHAPP_VERSION_REVISION=" + BuildRevision + "\" "; + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); @@ -1091,7 +1038,9 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + - "/p:Platform=Win32" + (BuildNightly ? " /p:ExternalCompilerOptions=PH_BUILD_API" : string.Empty) + " " + Solution + "/p:Platform=Win32 " + + //buildParams + + Solution ); if (!string.IsNullOrEmpty(error32)) @@ -1111,7 +1060,9 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + - "/p:Platform=x64" + (BuildNightly ? " /p:ExternalCompilerOptions=PH_BUILD_API" : string.Empty) + " " + Solution + "/p:Platform=x64 " + + //buildParams + + Solution ); if (!string.IsNullOrEmpty(error64)) diff --git a/tools/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs index 3f16de01e59d..6478cdda836d 100644 --- a/tools/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; namespace CustomBuildTool @@ -45,23 +46,33 @@ public static void ImageResizeFile(int size, string FileName, string OutName) } } - public const int STD_OUTPUT_HANDLE = -11; - public const int STD_INPUT_HANDLE = -10; - public const int STD_ERROR_HANDLE = -12; - public const int SW_HIDE = 0; - public const int SW_SHOW = 5; + public static void CopyIfNewer(string CurrentFile, string NewFile) + { + if (!File.Exists(CurrentFile)) + return; - static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + if (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) + { + File.Copy(CurrentFile, NewFile, true); + } + } - [DllImport("kernel32.dll")] - public static extern IntPtr GetStdHandle(int nStdHandle); - [DllImport("kernel32.dll")] + public const int SW_HIDE = 0; + public const int SW_SHOW = 5; + public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + public static readonly IntPtr STD_OUTPUT_HANDLE = new IntPtr(-11); + public static readonly IntPtr STD_INPUT_HANDLE = new IntPtr(-10); + public static readonly IntPtr STD_ERROR_HANDLE = new IntPtr(-12); + + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern IntPtr GetStdHandle(IntPtr StdHandle); + [DllImport("kernel32.dll", ExactSpelling = true)] public static extern bool GetConsoleMode(IntPtr ConsoleHandle, out ConsoleMode Mode); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", ExactSpelling = true)] public static extern bool SetConsoleMode(IntPtr ConsoleHandle, ConsoleMode Mode); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GetConsoleWindow(); - [DllImport("user32.dll")] + [DllImport("user32.dll", ExactSpelling = true)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index dfaa12b29ea7..960cad65053f 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -8,6 +8,24 @@ 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); @@ -32,24 +50,33 @@ public static void Main(string[] args) 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("-sign")) { - Build.BuildKphSignatureFile(false); + Build.CopyKProcessHacker(false); } else if (ProgramArgs.ContainsKey("-sdk")) { if (!Build.InitializeBuildEnvironment(false)) return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - - if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) - return; + BuildSdk( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + ); Build.ShowBuildStats(); } @@ -59,18 +86,14 @@ public static void Main(string[] args) return; if (!Build.BuildSolution("ProcessHacker.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose + )) + { return; + } - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) - return; + BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); Build.ShowBuildStats(); } @@ -79,29 +102,17 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - Build.ShowBuildEnvironment("bin", false); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); + Build.ShowBuildEnvironment("bin", false, true); + Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildKphSignatureFile(false)) - return; - if (!Build.CopyTextFiles()) - return; if (!Build.CopyKProcessHacker(false)) return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.BuildSolution("plugins\\Plugins.sln", @@ -121,118 +132,56 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment("debug", true); - Build.BuildSecureFiles(); + Build.ShowBuildEnvironment("debug", true, true); + Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; - if (!Build.BuildKphSignatureFile(true)) - return; - if (!Build.CopyTextFiles()) - return; if (!Build.CopyKProcessHacker(true)) return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles( + + if (!BuildSdk( BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + )) + { return; + } if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + )) + { return; + } if (!Build.CopyWow64Files(BuildFlags.BuildDebug)) return; Build.ShowBuildStats(); } - else if (ProgramArgs.ContainsKey("-release")) - { - if (!Build.InitializeBuildEnvironment(true)) - return; - - Build.ShowBuildEnvironment("release", true); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); - - if (!Build.BuildSolution("ProcessHacker.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) - return; - - if (!Build.BuildKphSignatureFile(false)) - return; - if (!Build.CopyTextFiles()) - return; - if (!Build.CopyKProcessHacker(false)) - return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) - return; - - if (!Build.BuildSolution("plugins\\Plugins.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) - return; - - if (!Build.CopyWow64Files(BuildFlags.None)) - return; - - if (!Build.BuildBinZip()) - return; - if (!Build.BuildSetupExe()) - return; - Build.BuildPdbZip(); - Build.BuildSdkZip(); - Build.BuildSrcZip(); - - Build.ShowBuildStats(); - } else if (ProgramArgs.ContainsKey("-appveyor")) { if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment("nightly", true); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); + Build.ShowBuildEnvironment("nightly", true, true); + Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildKphSignatureFile(false)) - return; - if (!Build.CopyTextFiles()) - return; if (!Build.CopyKProcessHacker(false)) return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles( - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.CopyWow64Files(BuildFlags.None)) @@ -255,22 +204,13 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(true)) return; - Build.ShowBuildEnvironment("appx", true); - Build.BuildSecureFiles(); - Build.UpdateHeaderFileVersion(); + Build.ShowBuildEnvironment("appx", true, true); + Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyTextFiles()) - return; - if (!Build.CopyPluginSdkHeaders()) - return; - if (!Build.CopyVersionHeader()) - return; - if (!Build.FixupResourceHeader()) - return; - if (!Build.CopyLibFiles(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) @@ -291,7 +231,7 @@ public static void Main(string[] args) if (!Build.InitializeBuildEnvironment(false)) return; - Build.ShowBuildEnvironment("appxcert", true); + Build.ShowBuildEnvironment("appxcert", true, true); Build.BuildAppxSignature(); @@ -299,7 +239,38 @@ public static void Main(string[] args) } else { - Console.WriteLine("Invalid arguments.\n"); + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment("release", true, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.CopyKProcessHacker(false)) + return; + + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.CopyWow64Files(BuildFlags.None)) + return; + + if (!Build.BuildBinZip()) + return; + if (!Build.BuildSetupExe()) + return; + Build.BuildPdbZip(); + Build.BuildSdkZip(); + Build.BuildSrcZip(); + + Build.ShowBuildStats(); } } @@ -369,6 +340,8 @@ private static bool Restart(string Arguments) } catch (Exception) { } + Environment.Exit(Environment.ExitCode); + return true; } } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 3ee36479e5975a4aa2752484c2f7ebc0eea2e8a4..bf9224ba705bdcae546ae48bdc28a10b30dc3005 100644 GIT binary patch delta 21275 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 09ff85a2e3d99f1d62ce3fc84e1fe604264cdf55..c9b211b16d3120a19e8d603bc851328efeb954d4 100644 GIT binary patch literal 73216 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 literal 75264 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

}?v?5%#^m}f4=9zPN@TR)5a_#6aN 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 literal 0 HcmV?d00001 diff --git a/tools/delaylib/bin/Release32/delaylib.lib b/tools/delaylib/bin/Release32/delaylib.lib new file mode 100644 index 0000000000000000000000000000000000000000..7e4fb20980bc276c072964297217d7e476865aac GIT binary patch literal 226114 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 +UJBUq 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�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

    6&j9S# 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%0A*$>*bS@O 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

    yV;NxiP3t8@?ORrTnliSwGmJ&F+WnK7_A_bs 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 literal 0 HcmV?d00001 diff --git a/tools/delaylib/delaylib.sln b/tools/delaylib/delaylib.sln new file mode 100644 index 000000000000..e94606b52622 --- /dev/null +++ b/tools/delaylib/delaylib.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delaylib", "delaylib.vcxproj", "{9648DD70-47A8-4B73-B379-12368E8EF919}" +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 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Debug|x64.ActiveCfg = Debug|x64 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Debug|x64.Build.0 = Debug|x64 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Debug|x86.ActiveCfg = Debug|Win32 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Debug|x86.Build.0 = Debug|Win32 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Release|x64.ActiveCfg = Release|x64 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Release|x64.Build.0 = Release|x64 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Release|x86.ActiveCfg = Release|Win32 + {9648DD70-47A8-4B73-B379-12368E8EF919}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/delaylib/delaylib.vcxproj b/tools/delaylib/delaylib.vcxproj new file mode 100644 index 000000000000..a06c38f4c7eb --- /dev/null +++ b/tools/delaylib/delaylib.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + 15.0 + {9648DD70-47A8-4B73-B379-12368E8EF919} + Win32Proj + delaylib + 10.0.15063.0 + delaylib + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(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)\ + + + + + + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + StdCall + false + MultiThreadedDebug + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + true + true + true + ProgramDatabase + + + Windows + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + StdCall + false + MultiThreadedDebug + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + true + true + true + ProgramDatabase + + + Windows + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + StdCall + MultiThreaded + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + true + true + true + + + Windows + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + StdCall + MultiThreaded + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + true + true + true + + + Windows + true + true + + + + + + \ No newline at end of file diff --git a/tools/delaylib/delaylib.vcxproj.filters b/tools/delaylib/delaylib.vcxproj.filters new file mode 100644 index 000000000000..6827613af933 --- /dev/null +++ b/tools/delaylib/delaylib.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tools/delaylib/main.c b/tools/delaylib/main.c new file mode 100644 index 000000000000..1907bb64f22c --- /dev/null +++ b/tools/delaylib/main.c @@ -0,0 +1,125 @@ +/* + * Process Hacker Toolchain - + * Image DelayLoad Helper + * + * 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 + +extern const IMAGE_DOS_HEADER __ImageBase; + +PVOID WINAPI __delayLoadHelper2( + _In_ PIMAGE_DELAYLOAD_DESCRIPTOR Entry, + _Out_ PVOID *ImportAddress + ) +{ + BOOLEAN needsFree = FALSE; + PSTR importName; + PVOID procedureAddress; + PVOID moduleHandle; + PVOID* importHandle; + PIMAGE_THUNK_DATA entry; + PIMAGE_THUNK_DATA importTable; + PIMAGE_THUNK_DATA importNameTable; + + importName = PTR_ADD_OFFSET(&__ImageBase, Entry->DllNameRVA); + importHandle = PTR_ADD_OFFSET(&__ImageBase, Entry->ModuleHandleRVA); + importTable = PTR_ADD_OFFSET(&__ImageBase, Entry->ImportAddressTableRVA); + importNameTable = PTR_ADD_OFFSET(&__ImageBase, Entry->ImportNameTableRVA); + + if (!strcmp(importName, "ProcessHacker.exe")) + { + moduleHandle = NtCurrentPeb()->ImageBaseAddress; + } + else + { + if (moduleHandle = LoadLibraryA(importName)) + { + needsFree = TRUE; + } + else + { + DelayLoadInfo dli = + { + sizeof(DelayLoadInfo), + (PCImgDelayDescr)Entry, + (FARPROC*)ImportAddress, + importName + }; + + RaiseException( + VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND), + 0, + 1, + (PULONG_PTR)&dli + ); + + return NULL; + } + } + + entry = PTR_ADD_OFFSET(importNameTable, (ULONG)((PBYTE)ImportAddress - (PBYTE)importTable)); + + if (IMAGE_SNAP_BY_ORDINAL(entry->u1.Ordinal)) + { + ULONG procedureOrdinal = IMAGE_ORDINAL(entry->u1.Ordinal); + procedureAddress = PhGetProcedureAddress(moduleHandle, NULL, procedureOrdinal); + } + else + { + PSTR procedureName = ((PIMAGE_IMPORT_BY_NAME)PTR_ADD_OFFSET(&__ImageBase, entry->u1.AddressOfData))->Name; + procedureAddress = PhGetProcedureAddress(moduleHandle, procedureName, 0); + } + + if (!procedureAddress) + { + PSTR procedureName = ((PIMAGE_IMPORT_BY_NAME)PTR_ADD_OFFSET(&__ImageBase, entry->u1.AddressOfData))->Name; + DelayLoadInfo dli = + { + sizeof(DelayLoadInfo), + (PCImgDelayDescr)Entry, + (FARPROC*)ImportAddress, + procedureName + }; + + RaiseException( + VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND), + 0, + 1, + (PULONG_PTR)&dli + ); + + return NULL; + } + + // Cache the procedure address (Fixes CRT delayload bug). + if (InterlockedExchangePointer(ImportAddress, procedureAddress)) + { + NOTHING; + } + + // Cache the module handle in the IAT entry (required) (Fixes CRT use-after-free bug). + if (InterlockedExchangePointer(importHandle, moduleHandle) == moduleHandle && needsFree) + { + FreeLibrary(moduleHandle); // A different thread has already updated the cache. + } + + return procedureAddress; +} From e0dd4c16826895dd4aa5479a1adb1937be8eba1a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 06:59:30 +1000 Subject: [PATCH 138/839] Improve plugin abstraction (see below notes) Improve plugin callbacks Improve plugin resource sharing Improve plugin error handling Add delayload support --- .gitignore | 1 + ProcessHacker.sln | 5 + ProcessHacker/ProcessHacker.def | 20 ++-- ProcessHacker/ProcessHacker.vcxproj | 21 ++-- ProcessHacker/ProcessHacker.vcxproj.filters | 3 + ProcessHacker/exports.c | 97 +++++++++++++++++++ ProcessHacker/include/appsup.h | 4 +- ProcessHacker/include/mainwnd.h | 2 +- ProcessHacker/include/netprv.h | 4 - ProcessHacker/include/phapp.h | 88 ++++++++++++++++- ProcessHacker/include/phplug.h | 13 +++ ProcessHacker/include/procprv.h | 5 - ProcessHacker/include/srvprv.h | 5 - ProcessHacker/log.c | 5 +- ProcessHacker/logwnd.c | 5 +- ProcessHacker/main.c | 2 +- ProcessHacker/mainwnd.c | 6 +- ProcessHacker/memlists.c | 5 +- ProcessHacker/miniinfo.c | 12 +-- ProcessHacker/mwpgnet.c | 8 +- ProcessHacker/mwpgproc.c | 8 +- ProcessHacker/mwpgsrv.c | 8 +- ProcessHacker/netprv.c | 14 +-- ProcessHacker/notifico.c | 2 +- ProcessHacker/plugin.c | 20 ++-- ProcessHacker/procprv.c | 15 +-- ProcessHacker/procrec.c | 2 +- ProcessHacker/prpggen.c | 4 +- ProcessHacker/prpgperf.c | 5 +- ProcessHacker/prpgstat.c | 5 +- ProcessHacker/prpgthrd.c | 2 +- ProcessHacker/srvctl.c | 5 +- ProcessHacker/srvlist.c | 8 +- ProcessHacker/srvprv.c | 14 +-- ProcessHacker/sysinfo.c | 8 +- ProcessHacker/thrdprv.c | 5 +- phlib/global.c | 34 ++++--- phlib/include/phconfig.h | 71 ++++++-------- phlib/include/phsup.h | 8 ++ phlib/phlib.vcxproj.filters | 2 +- phnt/include/phnt.h | 10 ++ plugins/DotNetTools/clrsup.c | 2 +- plugins/DotNetTools/perfpage.c | 2 +- plugins/DotNetTools/stackext.c | 2 +- plugins/ExtendedNotifications/filelog.c | 2 +- plugins/ExtendedServices/main.c | 20 ++-- plugins/ExtendedServices/other.c | 2 +- plugins/ExtendedServices/recovery.c | 4 +- plugins/ExtendedTools/disktab.c | 34 +++---- plugins/ExtendedTools/etwdisk.c | 2 +- plugins/ExtendedTools/etwmon.c | 8 +- plugins/ExtendedTools/etwprprp.c | 14 +-- plugins/ExtendedTools/etwstat.c | 8 +- plugins/ExtendedTools/exttools.h | 4 - plugins/ExtendedTools/gpumon.c | 10 +- plugins/ExtendedTools/gpunodes.c | 10 +- plugins/ExtendedTools/gpuprprp.c | 16 +-- plugins/ExtendedTools/main.c | 6 +- plugins/ExtendedTools/unldll.c | 6 +- plugins/ExtendedTools/wswatch.c | 2 +- plugins/ExtraPlugins/cloud.c | 2 +- plugins/ExtraPlugins/dialog.c | 2 +- plugins/ExtraPlugins/setup/page5.c | 8 +- plugins/ExtraPlugins/setup/updater.c | 28 ++---- plugins/HardwareDevices/disknotify.c | 6 +- plugins/HardwareDevices/main.c | 4 +- plugins/HardwareDevices/netdetails.c | 4 +- plugins/HardwareDevices/netoptions.c | 4 +- plugins/HardwareDevices/storage.c | 6 +- plugins/NetworkTools/pages.c | 6 +- plugins/NetworkTools/ping.c | 8 +- plugins/NetworkTools/tracert.c | 7 +- plugins/NetworkTools/update.c | 63 ++++-------- plugins/NetworkTools/whois.c | 5 +- plugins/OnlineChecks/main.c | 14 +-- plugins/OnlineChecks/page1.c | 2 +- plugins/OnlineChecks/page2.c | 2 +- plugins/OnlineChecks/page3.c | 2 +- plugins/OnlineChecks/page4.c | 4 +- plugins/OnlineChecks/upload.c | 40 ++------ plugins/OnlineChecks/virustotal.c | 8 +- plugins/Plugins.props | 8 +- plugins/ToolStatus/customizesb.c | 4 +- plugins/ToolStatus/customizetb.c | 10 +- plugins/ToolStatus/graph.c | 14 +-- plugins/ToolStatus/main.c | 58 +++++------ plugins/ToolStatus/options.c | 6 +- plugins/ToolStatus/statusbar.c | 8 +- plugins/ToolStatus/toolbar.c | 6 +- plugins/Updater/options.c | 4 +- plugins/Updater/page5.c | 8 +- plugins/Updater/updater.c | 24 ++--- plugins/Updater/updater.h | 3 - plugins/UserNotes/main.c | 15 +-- plugins/WindowExplorer/WindowExplorer.vcxproj | 4 +- plugins/WindowExplorer/main.c | 2 +- plugins/WindowExplorer/wndexp.h | 4 +- plugins/WindowExplorer/wndprp.c | 2 +- tools/peview/peview.vcxproj | 20 ++-- 99 files changed, 634 insertions(+), 506 deletions(-) create mode 100644 ProcessHacker/exports.c diff --git a/.gitignore b/.gitignore index 53afad1c8da7..230b4fd6ceab 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ tools/GenerateZw/GenerateZw/bin/Release/* !tools/GenerateZw/GenerateZw/bin/Release/GenerateZw.exe !tools/delaylib/bin/ +!*pdb* diff --git a/ProcessHacker.sln b/ProcessHacker.sln index 4e87a04af677..241b7a6e05d2 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -12,6 +12,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phlib", "phlib\phlib.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peview", "tools\peview\peview.vcxproj", "{72C124A2-3C80-41C6-ABA1-C4948B713204}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9963724D-4A1D-4442-AD5A-1CE899D30D96}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -48,4 +50,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {72C124A2-3C80-41C6-ABA1-C4948B713204} = {9963724D-4A1D-4442-AD5A-1CE899D30D96} + EndGlobalSection EndGlobal diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 97692aee76bf..99e444c9bc65 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -37,18 +37,16 @@ EXPORTS PhfWakeForReleaseQueuedLock ; phconfig - PhGlobalDpi DATA - PhHeapHandle DATA + PhGetSystemBasicInformation + PhWindowsVersion + PhGetGlobalDpi + PhGetImageBase PhIsExecutingInWow64 - PhLibImageBase DATA - PhOsVersion DATA - PhSystemBasicInformation DATA - ProcessAllAccess DATA - ProcessQueryAccess DATA - ThreadAllAccess DATA - ThreadQueryAccess DATA - ThreadSetAccess DATA - WindowsVersion DATA + PhProcessAllAccess + PhProcessQueryAccess + PhThreadAllAccess + PhThreadQueryAccess + PhThreadSetAccess ; phbasesup PhAddElementAvlTree diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 5e3e2d24f2de..9b9b462afac4 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -95,14 +95,15 @@ true - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX86 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -126,14 +127,15 @@ true - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX64 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -162,7 +164,7 @@ StreamingSIMDExtensions - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -170,9 +172,10 @@ MachineX86 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -196,7 +199,7 @@ true - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -204,9 +207,10 @@ MachineX64 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -225,6 +229,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index e4fac5fddc0e..d8d900f22ea6 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -381,6 +381,9 @@ Process Hacker + + Process Hacker + diff --git a/ProcessHacker/exports.c b/ProcessHacker/exports.c new file mode 100644 index 000000000000..c20fa19ae156 --- /dev/null +++ b/ProcessHacker/exports.c @@ -0,0 +1,97 @@ +/* + * Process Hacker - + * exported variables + * + * 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 + +HFONT PhGetApplicationFont( + VOID + ) +{ + return PhApplicationFont; +} + +HWND PhGetMainWndHandle( + VOID + ) +{ + return PhMainWndHandle; +} + +PVOID PhGetImageBase( + VOID + ) +{ + return PhLibImageBase; +} + +ULONG PhGetGlobalDpi( + VOID + ) +{ + return PhGlobalDpi; +} + +SYSTEM_BASIC_INFORMATION PhGetSystemBasicInformation( + VOID + ) +{ + return PhSystemBasicInformation; +} + +ACCESS_MASK PhProcessQueryAccess( + VOID + ) +{ + return ProcessQueryAccess; +} + +ACCESS_MASK PhProcessAllAccess( + VOID + ) +{ + return ProcessAllAccess; +} + +ACCESS_MASK PhThreadQueryAccess( + VOID + ) +{ + return ThreadQueryAccess; +} + +ACCESS_MASK PhThreadSetAccess( + VOID + ) +{ + return ThreadSetAccess; +} + +ACCESS_MASK PhThreadAllAccess( + VOID + ) +{ + return ThreadAllAccess; +} \ No newline at end of file diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index fe34c8977206..275273f6d4e8 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -416,8 +416,8 @@ 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) +#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, diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index d7607624acd1..d93cc50263ac 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -3,7 +3,7 @@ #define PH_MAINWND_CLASSNAME L"ProcessHacker" // phapppub -PHAPPAPI extern HWND PhMainWndHandle; // phapppub +extern HWND PhMainWndHandle; extern BOOLEAN PhMainWndExiting; #define WM_PH_FIRST (WM_APP + 99) diff --git a/ProcessHacker/include/netprv.h b/ProcessHacker/include/netprv.h index beaecea2b4a1..b4b34835f35f 100644 --- a/ProcessHacker/include/netprv.h +++ b/ProcessHacker/include/netprv.h @@ -2,10 +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; diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 5a9914f8a514..d0160089cf5b 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -74,7 +74,7 @@ typedef struct _PH_STARTUP_PARAMETERS extern PPH_STRING PhApplicationDirectory; extern PPH_STRING PhApplicationFileName; -PHAPPAPI extern HFONT PhApplicationFont; // phapppub +extern HFONT PhApplicationFont; extern PPH_STRING PhCurrentUserName; extern HINSTANCE PhInstanceHandle; extern PPH_STRING PhLocalSystemName; @@ -87,8 +87,6 @@ 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 @@ -135,6 +133,87 @@ VOID PhInitializeFont( _In_ HWND hWnd ); +// begin_phapppub +PHAPPAPI +HFONT +NTAPI +PhGetApplicationFont( + VOID + ); + +PHAPPAPI +HWND +NTAPI +PhGetMainWndHandle( + VOID + ); + +#define PhMainWindowHandle \ + PhGetMainWndHandle() + +PHLIBAPI +PVOID +NTAPI +PhGetImageBase( + VOID + ); + +#define PhImageBaseAddress \ + PhGetImageBase() + +PHLIBAPI +ULONG +NTAPI +PhGetGlobalDpi( + VOID + ); + +#define PH_SCALE_DPI(Value) \ + PhMultiplyDivide(Value, PhGetGlobalDpi(), 96) + +PHLIBAPI +SYSTEM_BASIC_INFORMATION +NTAPI +PhGetSystemBasicInformation( + VOID + ); + +PHLIBAPI +ACCESS_MASK +NTAPI +PhProcessQueryAccess( + VOID + ); + +PHLIBAPI +ACCESS_MASK +NTAPI +PhProcessAllAccess( + VOID + ); + +PHLIBAPI +ACCESS_MASK +NTAPI +PhThreadQueryAccess( + VOID + ); + +PHLIBAPI +ACCESS_MASK +NTAPI +PhThreadSetAccess( + VOID + ); + +PHLIBAPI +ACCESS_MASK +NTAPI +PhThreadAllAccess( + VOID + ); +// end_phapppub + // plugin extern PH_AVL_TREE PhPluginsByName; @@ -211,7 +290,6 @@ typedef struct _PH_LOG_ENTRY } PH_LOG_ENTRY, *PPH_LOG_ENTRY; extern PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; -PHAPPAPI extern PH_CALLBACK PhLoggedCallback; // phapppub VOID PhLogInitialization( VOID @@ -643,7 +721,7 @@ PhCreateCommonFont( return NULL; fontHandle = CreateFont( - -PhMultiplyDivideSigned(Size, PhGlobalDpi, 72), + -PhMultiplyDivideSigned(Size, PhGetGlobalDpi(), 72), 0, 0, 0, diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 68ea5602ede1..cc2de419a98c 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -44,6 +44,19 @@ typedef enum _PH_GENERAL_CALLBACK 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] + GeneralCallbackLoggedEvent = 34, // PPH_LOG_ENTRY data [provider thread] + GeneralCallbackProcessProviderAdded = 35, // PPH_PROCESS_ITEM data [provider thread] + GeneralCallbackProcessProviderModified = 36, // PPH_PROCESS_ITEM data [provider thread] + GeneralCallbackProcessProviderRemoved = 37, // PPH_PROCESS_ITEM data [provider thread] + GeneralCallbackProcessProviderUpdated = 38, // [provider thread] + GeneralCallbackServiceProviderAdded = 39, // PPH_SERVICE_ITEM data [provider thread] + GeneralCallbackServiceProviderModified = 40, // PPH_SERVICE_ITEM data [provider thread] + GeneralCallbackServiceProviderRemoved = 41, // PPH_SERVICE_ITEM data [provider thread] + GeneralCallbackServiceProviderUpdated = 42, // [provider thread] + GeneralCallbackNetworkProviderAdded = 43, // PPH_NETWORK_ITEM data [provider thread] + GeneralCallbackNetworkProviderModified = 44, // PPH_NETWORK_ITEM data [provider thread] + GeneralCallbackNetworkProviderRemoved = 45, // PPH_NETWORK_ITEM data [provider thread] + GeneralCallbackNetworkProviderUpdated = 46, // [provider thread] GeneralCallbackMaximum } PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index f79546f534fd..169268452962 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -6,11 +6,6 @@ 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; diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index a01349d8005e..d511db7cdf69 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -3,11 +3,6 @@ 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 diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index a57862b1c614..b5334c064109 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -21,11 +21,10 @@ */ #include - +#include #include PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; -PHAPPAPI PH_CALLBACK_DECLARE(PhLoggedCallback); VOID PhLogInitialization( VOID @@ -155,7 +154,7 @@ VOID PhpLogEntry( if (oldEntry) PhpFreeLogEntry(oldEntry); - PhInvokeCallback(&PhLoggedCallback, Entry); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), Entry); } VOID PhClearLogEntries( diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 0a9b71a94b62..2be84a749e1e 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -187,7 +188,7 @@ INT_PTR CALLBACK PhpLogDlgProc( Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOSCROLL), BST_CHECKED); - PhRegisterCallback(&PhLoggedCallback, LoggedCallback, NULL, &LoggedRegistration); + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), LoggedCallback, NULL, &LoggedRegistration); PhpUpdateLogList(); ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); } @@ -199,7 +200,7 @@ INT_PTR CALLBACK PhpLogDlgProc( PhDeleteLayoutManager(&WindowLayoutManager); - PhUnregisterCallback(&PhLoggedCallback, &LoggedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), &LoggedRegistration); PhUnregisterDialog(PhLogWindowHandle); PhLogWindowHandle = NULL; } diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 3d8b82255a96..f25656fa2284 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -77,7 +77,7 @@ VOID PhpEnablePrivileges( PPH_STRING PhApplicationDirectory; PPH_STRING PhApplicationFileName; -PHAPPAPI HFONT PhApplicationFont; +HFONT PhApplicationFont; PPH_STRING PhCurrentUserName = NULL; HINSTANCE PhInstanceHandle; PPH_STRING PhLocalSystemName = NULL; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 3d4c8539dd18..b6b7a5d9c4c6 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -57,7 +57,7 @@ #define RUNAS_MODE_ADMIN 1 #define RUNAS_MODE_LIMITED 2 -PHAPPAPI HWND PhMainWndHandle; +HWND PhMainWndHandle; BOOLEAN PhMainWndExiting = FALSE; HMENU PhMainWndMenuHandle; @@ -342,12 +342,12 @@ BOOLEAN PhMwpInitializeWindowClass( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, 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 = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); if (!RegisterClassEx(&wcex)) return FALSE; diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 6f73a3f3d918..093db01c7a5b 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -171,7 +172,7 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( { case WM_INITDIALOG: { - PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); PhpUpdateMemoryListInfo(hwndDlg); PhLoadWindowPlacementFromSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); @@ -183,7 +184,7 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( PhUnregisterDialog(hwndDlg); PhSaveWindowPlacementToSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); - PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedRegistration); UnregisterDialogFunction(hwndDlg); PhMemoryListsWindowHandle = NULL; diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 01d139a083e8..b3e2f4105b47 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -110,11 +110,11 @@ VOID PhPinMiniInformation( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; - wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( @@ -337,7 +337,7 @@ VOID PhMipContainerOnShowWindow( PhMipMessageLoopFilterEntry = PhRegisterMessageLoopFilter(PhMipMessageLoopFilter, NULL); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhMipUpdateHandler, NULL, &ProcessesUpdatedRegistration @@ -357,7 +357,7 @@ VOID PhMipContainerOnShowWindow( PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE); PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedRegistration ); @@ -448,10 +448,10 @@ VOID PhMipOnInitDialog( HICON cog; HICON pin; - cog = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COG)); + cog = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); - pin = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PIN)); + pin = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PIN)); SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); diff --git a/ProcessHacker/mwpgnet.c b/ProcessHacker/mwpgnet.c index c8fa1d5cd18f..4229dd858ed3 100644 --- a/ProcessHacker/mwpgnet.c +++ b/ProcessHacker/mwpgnet.c @@ -62,25 +62,25 @@ BOOLEAN PhMwpNetworkPageCallback( PhMwpNetworkPage = Page; PhRegisterCallback( - &PhNetworkItemAddedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderAdded), PhMwpNetworkItemAddedHandler, NULL, &NetworkItemAddedRegistration ); PhRegisterCallback( - &PhNetworkItemModifiedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderModified), PhMwpNetworkItemModifiedHandler, NULL, &NetworkItemModifiedRegistration ); PhRegisterCallback( - &PhNetworkItemRemovedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderRemoved), PhMwpNetworkItemRemovedHandler, NULL, &NetworkItemRemovedRegistration ); PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), PhMwpNetworkItemsUpdatedHandler, NULL, &NetworkItemsUpdatedRegistration diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 11307c937294..6d0fbad59d2b 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -70,25 +70,25 @@ BOOLEAN PhMwpProcessesPageCallback( PhMwpProcessesPage = Page; PhRegisterCallback( - &PhProcessAddedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderAdded), PhMwpProcessAddedHandler, NULL, &ProcessAddedRegistration ); PhRegisterCallback( - &PhProcessModifiedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderModified), PhMwpProcessModifiedHandler, NULL, &ProcessModifiedRegistration ); PhRegisterCallback( - &PhProcessRemovedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderRemoved), PhMwpProcessRemovedHandler, NULL, &ProcessRemovedRegistration ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhMwpProcessesUpdatedHandler, NULL, &ProcessesUpdatedRegistration diff --git a/ProcessHacker/mwpgsrv.c b/ProcessHacker/mwpgsrv.c index 866ca49dc2e3..f9e1ff1832a7 100644 --- a/ProcessHacker/mwpgsrv.c +++ b/ProcessHacker/mwpgsrv.c @@ -63,25 +63,25 @@ BOOLEAN PhMwpServicesPageCallback( PhMwpServicesPage = Page; PhRegisterCallback( - &PhServiceAddedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderAdded), PhMwpServiceAddedHandler, NULL, &ServiceAddedRegistration ); PhRegisterCallback( - &PhServiceModifiedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderModified), PhMwpServiceModifiedHandler, NULL, &ServiceModifiedRegistration ); PhRegisterCallback( - &PhServiceRemovedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderRemoved), PhMwpServiceRemovedHandler, NULL, &ServiceRemovedRegistration ); PhRegisterCallback( - &PhServicesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderUpdated), PhMwpServicesUpdatedHandler, NULL, &ServicesUpdatedRegistration diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index d3cc1187db6a..1247c49721d1 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -94,11 +95,6 @@ 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; @@ -503,7 +499,7 @@ VOID PhNetworkProviderUpdate( if (!found) { - PhInvokeCallback(&PhNetworkItemRemovedEvent, *networkItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderRemoved), *networkItem); if (!connectionsToRemove) connectionsToRemove = PhCreateList(2); @@ -666,7 +662,7 @@ VOID PhNetworkProviderUpdate( PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); // Raise the network item added event. - PhInvokeCallback(&PhNetworkItemAddedEvent, networkItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderAdded), networkItem); } else { @@ -710,7 +706,7 @@ VOID PhNetworkProviderUpdate( if (modified) { // Raise the network item modified event. - PhInvokeCallback(&PhNetworkItemModifiedEvent, networkItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderModified), networkItem); } PhDereferenceObject(networkItem); @@ -719,7 +715,7 @@ VOID PhNetworkProviderUpdate( PhFree(connections); - PhInvokeCallback(&PhNetworkItemsUpdatedEvent, NULL); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), NULL); } PWSTR PhGetProtocolTypeName( diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 729f81412e78..7ad86bbb1486 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -113,7 +113,7 @@ VOID PhNfLoadStage2( } PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhNfpProcessesUpdatedHandler, NULL, &PhNfpProcessesUpdatedRegistration diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 8f30f4e9da33..efb22a49c654 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -264,30 +264,30 @@ VOID PhLoadPlugins( // In certain startup modes we want to ignore all plugin load errors. if (LoadErrors && LoadErrors->Count != 0 && !PhStartupParameters.PhSvc) { - PH_STRING_BUILDER sb; + PH_STRING_BUILDER stringBuilder; 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"); + PhInitializeStringBuilder(&stringBuilder, 100); for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; baseName = PhGetBaseName(loadError->FileName); - PhAppendFormatStringBuilder(&sb, L"%s: %s\n", + PhAppendFormatStringBuilder(&stringBuilder, 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 (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); - if (PhShowMessage( + if (PhShowError2( NULL, - MB_ICONERROR | MB_YESNO, - sb.String->Buffer - ) == IDYES) + L"Unable to load the following plugin(s):", + stringBuilder.String->Buffer + )) { ULONG i; @@ -300,7 +300,7 @@ VOID PhLoadPlugins( } } - PhDeleteStringBuilder(&sb); + PhDeleteStringBuilder(&stringBuilder); } // When we loaded settings before, we didn't know about plugin settings, so they diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 9445050002f7..06ba7e4c6a69 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -179,11 +179,6 @@ 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; @@ -1901,7 +1896,7 @@ VOID PhFlushProcessQueryData( // which will lead to very bad things happening. PhAcquireQueuedLockExclusive(&data->ProcessItem->RemoveLock); if (!(data->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)) - PhInvokeCallback(&PhProcessModifiedEvent, data->ProcessItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderModified), data->ProcessItem); PhReleaseQueuedLockExclusive(&data->ProcessItem->RemoveLock); } else @@ -2176,7 +2171,7 @@ VOID PhProcessProviderUpdate( // Raise the process removed event. // See PhFlushProcessQueryData for why we need to lock here. PhAcquireQueuedLockExclusive(&processItem->RemoveLock); - PhInvokeCallback(&PhProcessRemovedEvent, processItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderRemoved), processItem); PhReleaseQueuedLockExclusive(&processItem->RemoveLock); if (!processesToRemove) @@ -2298,7 +2293,7 @@ VOID PhProcessProviderUpdate( PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); // Raise the process added event. - PhInvokeCallback(&PhProcessAddedEvent, processItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderAdded), processItem); processItem->AddedEventSent = TRUE; // (Ref: for the process item being in the hashtable.) @@ -2461,7 +2456,7 @@ VOID PhProcessProviderUpdate( if (modified) { - PhInvokeCallback(&PhProcessModifiedEvent, processItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderModified), processItem); } // No reference added by PhpLookupProcessItem. @@ -2562,7 +2557,7 @@ VOID PhProcessProviderUpdate( } } - PhInvokeCallback(&PhProcessesUpdatedEvent, NULL); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), NULL); runCount++; } diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 486a3db8841c..dbcb2c98019a 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -186,7 +186,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( 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))); + (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)context->FileIcon, 0); diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 30b2115d2695..76318e4492e0 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -147,8 +147,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(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER)); + magnifier = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_MAGNIFIER)); SET_BUTTON_ICON(IDC_INSPECT, magnifier); SET_BUTTON_ICON(IDC_OPENFILENAME, folder); diff --git a/ProcessHacker/prpgperf.c b/ProcessHacker/prpgperf.c index 9efb80e93072..d525919ab666 100644 --- a/ProcessHacker/prpgperf.c +++ b/ProcessHacker/prpgperf.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -74,7 +75,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( performanceContext->WindowHandle = hwndDlg; PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PerformanceUpdateHandler, performanceContext, &performanceContext->ProcessesUpdatedRegistration @@ -113,7 +114,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhDeleteGraphState(&performanceContext->IoGraphState); PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &performanceContext->ProcessesUpdatedRegistration ); PhFree(performanceContext); diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 97b0127b4ea9..4953d401b699 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -246,7 +247,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), StatisticsUpdateHandler, statisticsContext, &statisticsContext->ProcessesUpdatedRegistration @@ -258,7 +259,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( case WM_DESTROY: { PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &statisticsContext->ProcessesUpdatedRegistration ); diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 53e2cf7797e5..cf23dd2cccc2 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -639,7 +639,7 @@ 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))); + SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); } break; case WM_DESTROY: diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index a53d1f769498..3596d3176853 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -229,7 +230,7 @@ INT_PTR CALLBACK PhpServicesPageProc( ULONG i; PhRegisterCallback( - &PhServiceModifiedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderModified), ServiceModifiedHandler, servicesContext, &servicesContext->ModifiedEventRegistration @@ -280,7 +281,7 @@ INT_PTR CALLBACK PhpServicesPageProc( PhFree(servicesContext->Services); PhUnregisterCallback( - &PhServiceModifiedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderModified), &servicesContext->ModifiedEventRegistration ); diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 003e86f51e31..1e2eaeb655df 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -689,10 +689,10 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( 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)); + ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATION)); + ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); + ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); + ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COGGO)); ServiceIconsLoaded = TRUE; } diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 18f258c7aa3d..b716fa37a141 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -93,11 +94,6 @@ 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; @@ -579,7 +575,7 @@ VOID PhServiceProviderUpdate( } // Raise the service removed event. - PhInvokeCallback(&PhServiceRemovedEvent, *serviceItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderRemoved), *serviceItem); if (!servicesToRemove) servicesToRemove = PhCreateList(2); @@ -654,7 +650,7 @@ VOID PhServiceProviderUpdate( PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); // Raise the service added event. - PhInvokeCallback(&PhServiceAddedEvent, serviceItem); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderAdded), serviceItem); } else { @@ -758,7 +754,7 @@ VOID PhServiceProviderUpdate( } // Raise the service modified event. - PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderModified), &serviceModifiedData); } } } @@ -767,7 +763,7 @@ VOID PhServiceProviderUpdate( PhFree(services); UpdateEnd: - PhInvokeCallback(&PhServicesUpdatedEvent, NULL); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderUpdated), NULL); runCount++; } diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 5dcbb236ea54..ee2feb3038ad 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -281,8 +281,8 @@ VOID PhSipOnInitDialog( VOID ) { - SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); @@ -291,7 +291,7 @@ VOID PhSipOnInitDialog( PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhSipSysInfoUpdateHandler, NULL, &ProcessesUpdatedRegistration @@ -313,7 +313,7 @@ VOID PhSipOnDestroy( ) { PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedRegistration ); diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 8f7a60b85b6a..ac616f887e03 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -212,7 +213,7 @@ VOID PhRegisterThreadProvider( ) { PhReferenceObject(ThreadProvider); - PhRegisterCallback(&PhProcessesUpdatedEvent, PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); } VOID PhUnregisterThreadProvider( @@ -220,7 +221,7 @@ VOID PhUnregisterThreadProvider( _In_ PPH_CALLBACK_REGISTRATION CallbackRegistration ) { - PhUnregisterCallback(&PhProcessesUpdatedEvent, CallbackRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), CallbackRegistration); PhDereferenceObject(ThreadProvider); } diff --git a/phlib/global.c b/phlib/global.c index fb8a44453ab0..34c6ddf1eaf4 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -38,20 +38,19 @@ 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; +PVOID PhLibImageBase; +PWSTR PhApplicationName = L"Application"; +ULONG PhGlobalDpi = 96; +PVOID PhHeapHandle; +RTL_OSVERSIONINFOEXW PhOsVersion; +SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; +ULONG WindowsVersion; + +ACCESS_MASK ProcessQueryAccess; +ACCESS_MASK ProcessAllAccess; +ACCESS_MASK ThreadQueryAccess; +ACCESS_MASK ThreadSetAccess; +ACCESS_MASK ThreadAllAccess; // Internal data #ifdef DEBUG @@ -237,3 +236,10 @@ static VOID PhInitializeWindowsVersion( ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; } } + +ULONG PhWindowsVersion( + VOID + ) +{ + return WindowsVersion; +} \ No newline at end of file diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 75892b856fe8..d163c60ecaed 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -7,47 +7,19 @@ extern "C" { #define _User_set_ -PHLIBAPI extern _User_set_ PVOID PhLibImageBase; - -PHLIBAPI extern _User_set_ PWSTR PhApplicationName; -PHLIBAPI extern _User_set_ ULONG PhGlobalDpi; -PHLIBAPI extern PVOID PhHeapHandle; -PHLIBAPI extern RTL_OSVERSIONINFOEXW PhOsVersion; -PHLIBAPI extern SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; -PHLIBAPI extern ULONG WindowsVersion; - -PHLIBAPI extern ACCESS_MASK ProcessQueryAccess; -PHLIBAPI extern ACCESS_MASK ProcessAllAccess; -PHLIBAPI extern ACCESS_MASK ThreadQueryAccess; -PHLIBAPI extern ACCESS_MASK ThreadSetAccess; -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_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 - -#ifdef DEBUG -#define dprintf(format, ...) DbgPrint(format, __VA_ARGS__) -#else -#define dprintf(format, ...) -#endif +extern _User_set_ PVOID PhLibImageBase; +extern _User_set_ PWSTR PhApplicationName; +extern _User_set_ ULONG PhGlobalDpi; +extern PVOID PhHeapHandle; +extern RTL_OSVERSIONINFOEXW PhOsVersion; +extern SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; +extern ULONG WindowsVersion; + +extern ACCESS_MASK ProcessQueryAccess; +extern ACCESS_MASK ProcessAllAccess; +extern ACCESS_MASK ThreadQueryAccess; +extern ACCESS_MASK ThreadSetAccess; +extern ACCESS_MASK ThreadAllAccess; // global @@ -102,6 +74,23 @@ PhIsExecutingInWow64( ); #endif + +PHLIBAPI +ULONG +NTAPI +PhWindowsVersion( + VOID + ); + +#define WINDOWS_HAS_CONSOLE_HOST (PhWindowsVersion() >= WINDOWS_7) +#define WINDOWS_HAS_CYCLE_TIME (PhWindowsVersion() >= WINDOWS_VISTA) +#define WINDOWS_HAS_IFILEDIALOG (PhWindowsVersion() >= WINDOWS_VISTA) +#define WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID (PhWindowsVersion() >= WINDOWS_VISTA) +#define WINDOWS_HAS_IMMERSIVE (PhWindowsVersion() >= WINDOWS_8) +#define WINDOWS_HAS_LIMITED_ACCESS (PhWindowsVersion() >= WINDOWS_VISTA) +#define WINDOWS_HAS_SERVICE_TAGS (PhWindowsVersion() >= WINDOWS_VISTA) +#define WINDOWS_HAS_UAC (PhWindowsVersion() >= WINDOWS_VISTA) + #ifdef __cplusplus } #endif diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 55e36cf5160f..82e4b306303e 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -8,6 +8,14 @@ #include #include +// Debugging + +#ifdef DEBUG +#define dprintf(format, ...) DbgPrint(format, __VA_ARGS__) +#else +#define dprintf(format, ...) +#endif + // Memory #define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index cae851a27dce..dee92e1b386b 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/phnt/include/phnt.h b/phnt/include/phnt.h index 42bf2a9d8007..1d7dffcca31e 100644 --- a/phnt/include/phnt.h +++ b/phnt/include/phnt.h @@ -24,6 +24,16 @@ #define PHNT_MODE_USER 1 // Version +#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 PHNT_WIN2K 50 #define PHNT_WINXP 51 #define PHNT_WS03 52 diff --git a/plugins/DotNetTools/clrsup.c b/plugins/DotNetTools/clrsup.c index 3fca660cb6ce..afc00b24cbf5 100644 --- a/plugins/DotNetTools/clrsup.c +++ b/plugins/DotNetTools/clrsup.c @@ -283,7 +283,7 @@ ICLRDataTarget *DnCLRDataTarget_Create( HANDLE processHandle; BOOLEAN isWow64; - if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId))) + if (!NT_SUCCESS(PhOpenProcess(&processHandle, PhProcessQueryAccess() | PROCESS_VM_READ, ProcessId))) return NULL; #ifdef _WIN64 diff --git a/plugins/DotNetTools/perfpage.c b/plugins/DotNetTools/perfpage.c index c5cf54c72bcf..1b3094966b8c 100644 --- a/plugins/DotNetTools/perfpage.c +++ b/plugins/DotNetTools/perfpage.c @@ -765,7 +765,7 @@ INT_PTR CALLBACK DotNetPerfPageDlgProc( if (NT_SUCCESS(PhOpenProcess( &context->ProcessHandle, - PROCESS_VM_READ | ProcessQueryAccess | PROCESS_DUP_HANDLE | SYNCHRONIZE, + PROCESS_VM_READ | PhProcessQueryAccess() | PROCESS_DUP_HANDLE | SYNCHRONIZE, context->ProcessItem->ProcessId ))) { diff --git a/plugins/DotNetTools/stackext.c b/plugins/DotNetTools/stackext.c index 13ef6a3aca5c..30528fb5b856 100644 --- a/plugins/DotNetTools/stackext.c +++ b/plugins/DotNetTools/stackext.c @@ -93,7 +93,7 @@ VOID ProcessThreadStackControl( context->ThreadHandle = Control->u.Initializing.ThreadHandle; #if _WIN64 - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, Control->u.Initializing.ProcessId))) + if (NT_SUCCESS(PhOpenProcess(&processHandle, PhProcessQueryAccess(), Control->u.Initializing.ProcessId))) { PhGetProcessIsWow64(processHandle, &context->IsWow64); NtClose(processHandle); diff --git a/plugins/ExtendedNotifications/filelog.c b/plugins/ExtendedNotifications/filelog.c index e1cc06587719..e0c3d10163b8 100644 --- a/plugins/ExtendedNotifications/filelog.c +++ b/plugins/ExtendedNotifications/filelog.c @@ -55,7 +55,7 @@ VOID FileLogInitialization( if (NT_SUCCESS(status)) { PhRegisterCallback( - &PhLoggedCallback, + PhGetGeneralCallback(GeneralCallbackLoggedEvent), LoggedCallback, NULL, &LoggedCallbackRegistration diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index c36e3b8f38f7..335c99fff0f1 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -57,28 +57,28 @@ VOID NTAPI MenuItemCallback( { case ID_SERVICE_GOTOSERVICE: { - ProcessHacker_SelectTabPage(PhMainWndHandle, 1); - ProcessHacker_SelectServiceItem(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + ProcessHacker_SelectTabPage(PhMainWindowHandle, 1); + ProcessHacker_SelectServiceItem(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_START: { - PhUiStartService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiStartService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_CONTINUE: { - PhUiContinueService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiContinueService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_PAUSE: { - PhUiPauseService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiPauseService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_STOP: { - PhUiStopService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiStopService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_RESTART: @@ -89,7 +89,7 @@ VOID NTAPI MenuItemCallback( if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS)) { - EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle); + EsRestartServiceWithProgress(PhMainWindowHandle, serviceItem, serviceHandle); CloseServiceHandle(serviceHandle); } else @@ -100,7 +100,7 @@ VOID NTAPI MenuItemCallback( if (win32Result != 0) { PhShowStatus( - PhMainWndHandle, + PhMainWindowHandle, PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer, 0, win32Result @@ -348,7 +348,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (PhWindowsVersion() >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); @@ -362,7 +362,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (PhWindowsVersion() >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); diff --git a/plugins/ExtendedServices/other.c b/plugins/ExtendedServices/other.c index 5b7957afa653..7c94a31beacd 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -365,7 +365,7 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_PROTECTION), EspServiceLaunchProtectedStrings, sizeof(EspServiceLaunchProtectedStrings) / sizeof(PWSTR)); - if (WindowsVersion < WINDOWS_8_1) + if (PhWindowsVersion() < WINDOWS_8_1) EnableWindow(GetDlgItem(hwndDlg, IDC_PROTECTION), FALSE); SetDlgItemText(hwndDlg, IDC_SERVICESID, diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index b917b0334498..245bce8a6372 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -214,7 +214,7 @@ NTSTATUS EspLoadRecoveryInfo( // Enable actions for stops with errors // This is Vista and above only. - if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2( + if (PhWindowsVersion() >= WINDOWS_VISTA && QueryServiceConfig2( serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (BYTE *)&failureActionsFlag, @@ -320,7 +320,7 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( { SetDlgItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0"); - if (WindowsVersion >= WINDOWS_VISTA) + if (PhWindowsVersion() >= WINDOWS_VISTA) { context->EnableFlagCheckBox = TRUE; EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE); diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index b8b1ae72e953..daf90924b520 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -62,7 +62,7 @@ VOID EtInitializeDiskTab( memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); PhInitializeStringRef(&page.Name, L"Disk"); page.Callback = EtpDiskPageCallback; - DiskPage = ProcessHacker_CreateTabPage(PhMainWndHandle, &page); + DiskPage = ProcessHacker_CreateTabPage(PhMainWindowHandle, &page); if (ToolStatusInterface) { @@ -101,7 +101,7 @@ BOOLEAN EtpDiskPageCallback( 0, 3, 3, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -115,7 +115,7 @@ BOOLEAN EtpDiskPageCallback( *(HWND *)Parameter1 = CreateDialog( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_DISKTABERROR), - PhMainWndHandle, + PhMainWindowHandle, EtpDiskTabErrorDialogProc ); return TRUE; @@ -829,17 +829,17 @@ VOID EtHandleDiskCommand( if ((processNode = PhFindProcessNode(diskItem->ProcessId)) && processNode->ProcessItem->CreateTime.QuadPart == diskItem->ProcessRecord->CreateTime.QuadPart) { - ProcessHacker_SelectTabPage(PhMainWndHandle, 0); + ProcessHacker_SelectTabPage(PhMainWindowHandle, 0); PhSelectAndEnsureVisibleProcessNode(processNode); } else { - PhShowProcessRecordDialog(PhMainWndHandle, diskItem->ProcessRecord); + PhShowProcessRecordDialog(PhMainWindowHandle, diskItem->ProcessRecord); } } else { - PhShowError(PhMainWndHandle, L"The process does not exist."); + PhShowError(PhMainWindowHandle, L"The process does not exist."); } PhDereferenceObject(diskItem); @@ -852,7 +852,7 @@ VOID EtHandleDiskCommand( if (diskItem) { - PhShellExploreFile(PhMainWndHandle, diskItem->FileNameWin32->Buffer); + PhShellExploreFile(PhMainWindowHandle, diskItem->FileNameWin32->Buffer); } } break; @@ -867,7 +867,7 @@ VOID EtHandleDiskCommand( if (diskItem) { - PhShellProperties(PhMainWndHandle, diskItem->FileNameWin32->Buffer); + PhShellProperties(PhMainWindowHandle, diskItem->FileNameWin32->Buffer); } } break; @@ -937,7 +937,7 @@ VOID EtShowDiskContextMenu( item = PhShowEMenu( menu, - PhMainWndHandle, + PhMainWindowHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, Location.x, @@ -963,7 +963,7 @@ VOID NTAPI EtpDiskItemAddedHandler( PET_DISK_ITEM diskItem = (PET_DISK_ITEM)Parameter; PhReferenceObject(diskItem); - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemAdded, diskItem); + ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemAdded, diskItem); } VOID NTAPI EtpDiskItemModifiedHandler( @@ -971,7 +971,7 @@ VOID NTAPI EtpDiskItemModifiedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemModified, (PET_DISK_ITEM)Parameter); + ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemModified, (PET_DISK_ITEM)Parameter); } VOID NTAPI EtpDiskItemRemovedHandler( @@ -979,7 +979,7 @@ VOID NTAPI EtpDiskItemRemovedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemRemoved, (PET_DISK_ITEM)Parameter); + ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemRemoved, (PET_DISK_ITEM)Parameter); } VOID NTAPI EtpDiskItemsUpdatedHandler( @@ -987,7 +987,7 @@ VOID NTAPI EtpDiskItemsUpdatedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemsUpdated, NULL); + ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemsUpdated, NULL); } VOID NTAPI EtpOnDiskItemAdded( @@ -1136,10 +1136,10 @@ INT_PTR CALLBACK EtpDiskTabErrorDialogProc( switch (LOWORD(wParam)) { case IDC_RESTART: - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); if (PhShellProcessHacker( - PhMainWndHandle, + PhMainWindowHandle, L"-v -selecttab Disk", SW_SHOW, PH_SHELL_EXECUTE_ADMIN, @@ -1148,11 +1148,11 @@ INT_PTR CALLBACK EtpDiskTabErrorDialogProc( NULL )) { - ProcessHacker_Destroy(PhMainWndHandle); + ProcessHacker_Destroy(PhMainWindowHandle); } else { - ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); + ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); } break; diff --git a/plugins/ExtendedTools/etwdisk.c b/plugins/ExtendedTools/etwdisk.c index c8512f20a01d..237e34715d71 100644 --- a/plugins/ExtendedTools/etwdisk.c +++ b/plugins/ExtendedTools/etwdisk.c @@ -95,7 +95,7 @@ VOID EtInitializeDiskInformation( EtStartEtwRundown(); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), EtpDiskProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration diff --git a/plugins/ExtendedTools/etwmon.c b/plugins/ExtendedTools/etwmon.c index 7c4823ae21ba..402b3417a27e 100644 --- a/plugins/ExtendedTools/etwmon.c +++ b/plugins/ExtendedTools/etwmon.c @@ -117,7 +117,7 @@ VOID EtStartEtwSession( ULONG result; ULONG bufferSize; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { EtpActualKernelLoggerName = &EtpPrivateKernelLoggerName; EtpActualSessionGuid = &ProcessHackerGuid; @@ -146,7 +146,7 @@ VOID EtStartEtwSession( EtpTraceProperties->LogFileNameOffset = 0; EtpTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) EtpTraceProperties->LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE; result = StartTrace(&EtpSessionHandle, EtpActualKernelLoggerName->Buffer, EtpTraceProperties); @@ -240,7 +240,7 @@ VOID NTAPI EtpEtwEventCallback( { DiskIo_TypeGroup1 *data = EventRecord->UserData; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { diskEvent.ClientId.UniqueThread = UlongToHandle(data->IssuingThreadId); diskEvent.ClientId.UniqueProcess = EtThreadIdToProcessId(diskEvent.ClientId.UniqueThread); @@ -387,7 +387,7 @@ NTSTATUS EtpEtwMonitorThreadStart( TRACEHANDLE traceHandle; // See comment in EtEtwProcessesUpdatedCallback. - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) EtUpdateProcessInformation(); memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); diff --git a/plugins/ExtendedTools/etwprprp.c b/plugins/ExtendedTools/etwprprp.c index 1e87ffbd4672..ba8059a07b8e 100644 --- a/plugins/ExtendedTools/etwprprp.c +++ b/plugins/ExtendedTools/etwprprp.c @@ -148,9 +148,9 @@ VOID EtwDiskNetworkLayoutGraphs( 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); + 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; @@ -326,7 +326,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( EtwDiskNetworkUpdatePanel(context); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), EtwDiskNetworkUpdateHandler, context, &context->ProcessesUpdatedRegistration @@ -352,7 +352,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( if (context->PanelHandle) DestroyWindow(context->PanelHandle); - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); PhFree(context); PhPropPageDlgProcDestroy(hwndDlg); @@ -394,7 +394,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( )); hdc = Graph_GetBufferedContext(context->DiskGraphHandle); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText(hdc, drawInfo, &context->DiskGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } @@ -462,7 +462,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( )); hdc = Graph_GetBufferedContext(context->NetworkGraphHandle); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText(hdc, drawInfo, &context->NetworkGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/ExtendedTools/etwstat.c b/plugins/ExtendedTools/etwstat.c index 33ee65dd33ee..663613de863e 100644 --- a/plugins/ExtendedTools/etwstat.c +++ b/plugins/ExtendedTools/etwstat.c @@ -85,13 +85,13 @@ VOID EtEtwStatisticsInitialization( PhInitializeCircularBuffer_ULONG(&EtMaxNetworkHistory, sampleCount); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), EtEtwProcessesUpdatedCallback, NULL, &EtpProcessesUpdatedCallbackRegistration ); PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), EtEtwNetworkItemsUpdatedCallback, NULL, &EtpNetworkItemsUpdatedCallbackRegistration @@ -224,7 +224,7 @@ VOID NTAPI EtEtwProcessesUpdatedCallback( // 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) + if (PhWindowsVersion() >= WINDOWS_8) EtUpdateProcessInformation(); // ETW is extremely lazy when it comes to flushing buffers, so we must do it @@ -357,7 +357,7 @@ VOID NTAPI EtEtwNetworkItemsUpdatedCallback( { // Values have changed. Invalidate the network node. PhReferenceObject(block->NetworkItem); - ProcessHacker_Invoke(PhMainWndHandle, EtpInvalidateNetworkNode, block->NetworkItem); + ProcessHacker_Invoke(PhMainWindowHandle, EtpInvalidateNetworkNode, block->NetworkItem); } listEntry = listEntry->Flink; diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index 967f6d30f356..c5201b3fabc5 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -27,10 +27,6 @@ extern HWND NetworkTreeNewHandle; #define SETTING_NAME_UNLOADED_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") #define SETTING_NAME_UNLOADED_COLUMNS (PLUGIN_NAME L".UnloadedListColumns") -#define ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) -#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) - // Graph update message #define UPDATE_MSG (WM_APP + 1) diff --git a/plugins/ExtendedTools/gpumon.c b/plugins/ExtendedTools/gpumon.c index ca608783baa3..4905e6fe2374 100644 --- a/plugins/ExtendedTools/gpumon.c +++ b/plugins/ExtendedTools/gpumon.c @@ -97,7 +97,7 @@ VOID EtGpuMonitorInitialization( } PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), EtGpuProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration @@ -244,7 +244,7 @@ BOOLEAN EtpInitializeD3DStatistics( ULONG64 commitLimit; ULONG aperture; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit; aperture = queryStatistics.QueryResult.SegmentInformation.Aperture; @@ -418,7 +418,7 @@ VOID EtpUpdateSegmentInformation( { ULONG64 bytesCommitted; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } @@ -436,7 +436,7 @@ VOID EtpUpdateSegmentInformation( { ULONG64 bytesCommitted; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident; } @@ -777,7 +777,7 @@ VOID EtQueryProcessGpuStatistics( { ULONG64 bytesCommitted; - if (WindowsVersion >= WINDOWS_8) + if (PhWindowsVersion() >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index fe9467dda387..6b1e083f01e4 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -121,15 +121,13 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( WindowHandle = hwndDlg; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); 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); @@ -206,6 +204,8 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); EtpLoadNodeBitMap(); + + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration); } break; case WM_DESTROY: @@ -214,7 +214,7 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( EtpSaveNodeBitMap(); - PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedCallbackRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedCallbackRegistration); for (i = 0; i < EtGpuTotalNodeCount; i++) { diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index 882b3c453d75..6661ee3e747c 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -263,9 +263,9 @@ VOID GpuPropLayoutGraphs( 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); + 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; @@ -474,7 +474,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( GpuPropUpdatePanel(context); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedHandler, context, &context->ProcessesUpdatedRegistration @@ -502,7 +502,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( if (context->PanelHandle) DestroyWindow(context->PanelHandle); - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); PhFree(context); PhPropPageDlgProcDestroy(hwndDlg); @@ -543,7 +543,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->GpuGraphHandle); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText(hdc, drawInfo, &context->GpuGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } @@ -574,7 +574,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->MemGraphHandle); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText( hdc, drawInfo, @@ -630,7 +630,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->SharedGraphHandle); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText(hdc, drawInfo, &context->MemorySharedGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 22c279729158..542438278ebd 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -91,7 +91,7 @@ VOID NTAPI MenuItemCallback( break; case ID_PROCESS_WSWATCH: { - EtShowWsWatchDialog(PhMainWndHandle, menuItem->Context); + EtShowWsWatchDialog(PhMainWindowHandle, menuItem->Context); } break; case ID_THREAD_CANCELIO: @@ -565,13 +565,13 @@ LOGICAL DllMain( ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration ); PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), NetworkItemsUpdatedCallback, NULL, &NetworkItemsUpdatedCallbackRegistration diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 54d9f4b8ae27..04fd28abfce5 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -305,8 +305,8 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); @@ -337,7 +337,7 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( 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); + PhCenterWindow(hwndDlg, PhMainWindowHandle); if (!EtpRefreshUnloadedDlls(hwndDlg, context)) { diff --git a/plugins/ExtendedTools/wswatch.c b/plugins/ExtendedTools/wswatch.c index 470092cb3b0d..29094695fd0d 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -389,7 +389,7 @@ static BOOLEAN NTAPI EnumGenericModulesCallback( // in Windows 7. if ( context->LoadingSymbolsForProcessId == SYSTEM_PROCESS_ID && - (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress + (ULONG_PTR)Module->BaseAddress <= PhGetSystemBasicInformation().MaximumUserModeAddress ) return TRUE; diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index 57509cc969c0..ac286ad2b0be 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -58,7 +58,7 @@ NTSTATUS QueryPluginsCallbackThread( if (!(httpSessionHandle = WinHttpOpen( L"ExtraPlugins_1.0", - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index b924fe09d2d9..ab5c5ea929d2 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -243,7 +243,7 @@ INT_PTR CALLBACK CloudPluginsDlgProc( context->NormalFontHandle = PhCreateCommonFont(-14, FW_NORMAL, NULL); context->BoldFontHandle = PhCreateCommonFont(-16, FW_BOLD, NULL); - PhCenterWindow(hwndDlg, PhMainWndHandle); + PhCenterWindow(hwndDlg, PhMainWindowHandle); InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); PhAddTreeNewFilter(GetPluginListFilterSupport(context), ProcessTreeFilterCallback, context); diff --git a/plugins/ExtraPlugins/setup/page5.c b/plugins/ExtraPlugins/setup/page5.c index e703258e37b6..256c6a8d52dc 100644 --- a/plugins/ExtraPlugins/setup/page5.c +++ b/plugins/ExtraPlugins/setup/page5.c @@ -45,9 +45,9 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( { if ((INT)wParam == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); PhShellProcessHacker( - PhMainWndHandle, + PhMainWindowHandle, L"-v", SW_SHOW, 0, @@ -56,7 +56,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( NULL ); //PhShellProcessHacker( - // PhMainWndHandle, + // PhMainWindowHandle, // L"-plugin " PLUGIN_NAME L":INSTALL -plugin " PLUGIN_NAME L":hex64value", // SW_SHOW, // 0, @@ -64,7 +64,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( // 0, // NULL // ); - ProcessHacker_Destroy(PhMainWndHandle); + ProcessHacker_Destroy(PhMainWindowHandle); } } break; diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 77662132c054..02e2e37eddb7 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -60,22 +60,8 @@ VOID TaskDialogCreateIcons( ) { // 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 - ); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); // Set the TaskDialog window icons SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); @@ -421,7 +407,7 @@ NTSTATUS UpdateDownloadThread( // Open the HTTP session with the system proxy configuration if available if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -430,7 +416,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -465,7 +451,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) + if (PhWindowsVersion() >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -738,11 +724,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( // break; //case WM_NCACTIVATE: // { - // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) + // if (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) // { // if (!context->FixedWindowStyles) // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); + // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWindowHandle); // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); // context->FixedWindowStyles = TRUE; // } diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index f212a04d456a..a68605ffbbd5 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -94,7 +94,7 @@ VOID AddRemoveDeviceChangeCallback( ) { // We get called during the plugin LoadCallback, don't do anything. - if (!PhMainWndHandle) + if (!PhMainWindowHandle) return; // Add the subclass only when disks are being monitored, remove when no longer needed. @@ -103,7 +103,7 @@ VOID AddRemoveDeviceChangeCallback( if (!SubclassActive) { // We have a disk device, subclass the main window to detect drive letter changes. - SetWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0, 0); + SetWindowSubclass(PhMainWindowHandle, MainWndDevicesSubclassProc, 0, 0); SubclassActive = TRUE; } } @@ -112,7 +112,7 @@ VOID AddRemoveDeviceChangeCallback( if (SubclassActive) { // The user has removed the last disk device, remove the subclass. - RemoveWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0); + RemoveWindowSubclass(PhMainWindowHandle, MainWndDevicesSubclassProc, 0); SubclassActive = FALSE; } } diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index 9b2e7c4a39bd..fd212f6d7e64 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -259,7 +259,7 @@ VOID ShowDeviceMenu( selectedItem = PhShowEMenu( menu, - PhMainWndHandle, + PhMainWindowHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, @@ -364,7 +364,7 @@ LOGICAL DllMain( ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration diff --git a/plugins/HardwareDevices/netdetails.c b/plugins/HardwareDevices/netdetails.c index 983a7ca071db..f703b9cd89d7 100644 --- a/plugins/HardwareDevices/netdetails.c +++ b/plugins/HardwareDevices/netdetails.c @@ -536,7 +536,7 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( NetAdapterAddListViewItemGroups(context->ListViewHandle); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), NetAdapterProcessesUpdatedHandler, context, &context->ProcessesUpdatedRegistration @@ -556,7 +556,7 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( break; case WM_DESTROY: { - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); if (context->NotifyHandle) CancelMibChangeNotify2(context->NotifyHandle); diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index 0807ba9a9e3b..b82b52fd44e7 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -301,7 +301,7 @@ BOOLEAN QueryNetworkDeviceInterfaceDescription( if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? deviceInstanceHandle, - WindowsVersion >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, + PhWindowsVersion() >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)deviceDescription->Buffer, &bufferSize, @@ -313,7 +313,7 @@ BOOLEAN QueryNetworkDeviceInterfaceDescription( result = CM_Get_DevNode_Property( deviceInstanceHandle, - WindowsVersion >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, + PhWindowsVersion() >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)deviceDescription->Buffer, &bufferSize, diff --git a/plugins/HardwareDevices/storage.c b/plugins/HardwareDevices/storage.c index 2be01224b59c..5c7a4472b533 100644 --- a/plugins/HardwareDevices/storage.c +++ b/plugins/HardwareDevices/storage.c @@ -918,21 +918,21 @@ BOOLEAN DiskDriveQueryFileSystemInfo( case FILESYSTEM_STATISTICS_TYPE_NTFS: case FILESYSTEM_STATISTICS_TYPE_REFS: // ReFS uses the same statistics as NTFS. { - bufferLength = sizeof(NTFS_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; + bufferLength = sizeof(NTFS_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } break; case FILESYSTEM_STATISTICS_TYPE_FAT: { - bufferLength = sizeof(FAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; + bufferLength = sizeof(FAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } break; case FILESYSTEM_STATISTICS_TYPE_EXFAT: { - bufferLength = sizeof(EXFAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; + bufferLength = sizeof(EXFAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index f2bce861c8ca..887789488e94 100644 --- a/plugins/NetworkTools/pages.c +++ b/plugins/NetworkTools/pages.c @@ -107,9 +107,9 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( { if ((INT)wParam == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); PhShellProcessHacker( - PhMainWndHandle, + PhMainWindowHandle, NULL, SW_SHOW, 0, @@ -117,7 +117,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( 0, NULL ); - ProcessHacker_Destroy(PhMainWndHandle); + ProcessHacker_Destroy(PhMainWindowHandle); } } break; diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 0ccb364a7005..bbed74977946 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -330,7 +330,7 @@ INT_PTR CALLBACK NetworkPingWndProc( 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); + PhCenterWindow(hwndDlg, PhMainWindowHandle); SetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer); SetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with %lu bytes of data...", @@ -339,7 +339,7 @@ INT_PTR CALLBACK NetworkPingWndProc( ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), NetworkPingUpdateHandler, context, &context->ProcessesUpdatedRegistration @@ -362,7 +362,7 @@ INT_PTR CALLBACK NetworkPingWndProc( case WM_DESTROY: { PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration ); @@ -447,7 +447,7 @@ INT_PTR CALLBACK NetworkPingWndProc( PhFormatString(L"%lu ms", context->CurrentPingMs) ); - SelectObject(hdc, PhApplicationFont); + SelectObject(hdc, PhGetApplicationFont()); PhSetGraphText(hdc, drawInfo, &context->PingGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 41550a1f123b..519bbf6b47f1 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -568,7 +568,10 @@ INT_PTR CALLBACK TracertDlgProc( { HANDLE tracertThread; - PhCenterWindow(hwndDlg, PhMainWndHandle); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, PhMainWindowHandle); Static_SetText(hwndDlg, PhaFormatString(L"Tracing %s...", context->IpAddressString)->Buffer @@ -592,7 +595,7 @@ INT_PTR CALLBACK TracertDlgProc( 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); + PhCenterWindow(hwndDlg, PhMainWindowHandle); PhReferenceObject(context); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 988029df021f..c7ceba20e69f 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -35,21 +35,14 @@ VOID FreeUpdateContext( ) { //PhClearReference(&Context->Version); - // PhClearReference(&Context->RevVersion); + //PhClearReference(&Context->RevVersion); //PhClearReference(&Context->RelDate); - // PhClearReference(&Context->Size); + //PhClearReference(&Context->Size); //PhClearReference(&Context->Hash); - // PhClearReference(&Context->Signature); - // PhClearReference(&Context->ReleaseNotesUrl); + //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->SetupFileDownloadUrl); //PhClearReference(&Context); } @@ -57,29 +50,11 @@ 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; + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconLargeHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconSmallHandle); } VOID TaskDialogLinkClicked( @@ -159,7 +134,7 @@ PPH_STRING QueryFwLinkUrl( if (!(httpSessionHandle = WinHttpOpen( NULL, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -168,7 +143,7 @@ PPH_STRING QueryFwLinkUrl( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -223,7 +198,7 @@ PPH_STRING QueryFwLinkUrl( ); } - if (WindowsVersion >= WINDOWS_7) + if (PhWindowsVersion() >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_REDIRECTS; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); @@ -415,7 +390,7 @@ NTSTATUS GeoIPUpdateThread( if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -424,7 +399,7 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); @@ -453,7 +428,7 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) + if (PhWindowsVersion() >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); @@ -735,7 +710,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( 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); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); @@ -787,7 +762,7 @@ NTSTATUS GeoIPUpdateDialogThread( //info.hwnd = Parameter; //info.lpVerb = L"runas"; // - //ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + //ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); // //if (ShellExecuteEx(&info)) //{ @@ -810,7 +785,7 @@ NTSTATUS GeoIPUpdateDialogThread( // NULL // ); // - // ProcessHacker_Destroy(PhMainWndHandle); + // ProcessHacker_Destroy(PhMainWindowHandle); // } // } // @@ -818,7 +793,7 @@ NTSTATUS GeoIPUpdateDialogThread( //} //else //{ - // ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); + // ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); //} } diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index bf6e602d0270..b7ea2d5a38d5 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -396,6 +396,9 @@ INT_PTR CALLBACK NetworkOutputDlgProc( { HANDLE dialogThread; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); context->WindowHandle = hwndDlg; @@ -416,7 +419,7 @@ INT_PTR CALLBACK NetworkOutputDlgProc( 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); + PhCenterWindow(hwndDlg, PhMainWindowHandle); if (dialogThread = PhCreateThread(0, NetworkWhoisThreadStart, (PVOID)context)) NtClose(dialogThread); diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 35ef25384a98..687ba5defef1 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -178,13 +178,7 @@ VOID NTAPI MenuItemCallback( 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.hMainIcon = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); config.cxWidth = 180; config.pszWindowTitle = L"Process Hacker - VirusTotal"; config.pszMainInstruction = L"VirusTotal scanning requires a restart of Process Hacker."; @@ -192,9 +186,9 @@ VOID NTAPI MenuItemCallback( if (SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); PhShellProcessHacker( - PhMainWndHandle, + PhMainWindowHandle, L"-v", SW_SHOW, 0, @@ -202,7 +196,7 @@ VOID NTAPI MenuItemCallback( 0, NULL ); - ProcessHacker_Destroy(PhMainWndHandle); + ProcessHacker_Destroy(PhMainWindowHandle); } DestroyIcon(config.hMainIcon); diff --git a/plugins/OnlineChecks/page1.c b/plugins/OnlineChecks/page1.c index 7b8489b72c95..2ce418db265f 100644 --- a/plugins/OnlineChecks/page1.c +++ b/plugins/OnlineChecks/page1.c @@ -40,7 +40,7 @@ HRESULT CALLBACK TaskDialogProcessingCallbackProc( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); if (context->TaskbarListClass) - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_INDETERMINATE); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_INDETERMINATE); PhReferenceObject(context); PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UploadCheckThreadStart, context); diff --git a/plugins/OnlineChecks/page2.c b/plugins/OnlineChecks/page2.c index aff040808c29..b6b972258f19 100644 --- a/plugins/OnlineChecks/page2.c +++ b/plugins/OnlineChecks/page2.c @@ -45,7 +45,7 @@ HRESULT CALLBACK TaskDialogResultFoundProc( { if (context->TaskbarListClass) { - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); } } break; diff --git a/plugins/OnlineChecks/page3.c b/plugins/OnlineChecks/page3.c index c35e7309c7ec..9d6fac0bc773 100644 --- a/plugins/OnlineChecks/page3.c +++ b/plugins/OnlineChecks/page3.c @@ -40,7 +40,7 @@ HRESULT CALLBACK TaskDialogProgressCallbackProc( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); if (context->TaskbarListClass) - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_INDETERMINATE); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_INDETERMINATE); PhReferenceObject(context); context->UploadThreadHandle = PhCreateThread(0, UploadFileThreadStart, context); diff --git a/plugins/OnlineChecks/page4.c b/plugins/OnlineChecks/page4.c index 849bdcd1e0cf..e174cb39f63c 100644 --- a/plugins/OnlineChecks/page4.c +++ b/plugins/OnlineChecks/page4.c @@ -38,8 +38,8 @@ HRESULT CALLBACK TaskDialogErrorProc( { if (context->TaskbarListClass) { - ITaskbarList3_SetProgressValue(context->TaskbarListClass, PhMainWndHandle, 1, 1); - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_ERROR); + ITaskbarList3_SetProgressValue(context->TaskbarListClass, PhMainWindowHandle, 1, 1); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_ERROR); } } break; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 803689827131..d0736ff8414e 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -172,18 +172,6 @@ VOID UploadContextDeleteProcedure( 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->FileName); PhClearReference(&context->BaseFileName); @@ -205,7 +193,7 @@ VOID TaskDialogFreeContext( { // Reset Taskbar progress state(s) if (Context->TaskbarListClass) - ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); if (Context->TaskbarListClass) ITaskbarList3_SetProgressState(Context->TaskbarListClass, Context->DialogHandle, TBPF_NOPROGRESS); @@ -217,20 +205,8 @@ 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) - ); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); @@ -710,7 +686,7 @@ NTSTATUS UploadFileThreadStart( { ITaskbarList3_SetProgressState( context->TaskbarListClass, - PhMainWndHandle, + PhMainWindowHandle, TBPF_NORMAL ); } @@ -914,7 +890,7 @@ NTSTATUS UploadFileThreadStart( // Reset Taskbar progress state(s) if (context->TaskbarListClass) { - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); ITaskbarList3_SetProgressState(context->TaskbarListClass, context->DialogHandle, TBPF_NOPROGRESS); } @@ -1014,7 +990,7 @@ NTSTATUS UploadCheckThreadStart( if (!(context->HttpHandle = WinHttpOpen( userAgent->Buffer, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -1023,7 +999,7 @@ NTSTATUS UploadCheckThreadStart( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; WinHttpSetOption(context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); @@ -1252,7 +1228,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( 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); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 642ce0ddd9f3..bc93f9df57bd 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -317,7 +317,7 @@ PSTR VirusTotalSendHttpRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -326,7 +326,7 @@ PSTR VirusTotalSendHttpRequest( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); @@ -461,7 +461,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -470,7 +470,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); diff --git a/plugins/Plugins.props b/plugins/Plugins.props index 0f074ebbd868..73e6bd500644 100644 --- a/plugins/Plugins.props +++ b/plugins/Plugins.props @@ -30,7 +30,9 @@ true - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + delaylib.lib;ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ProcessHacker.exe;%(DelayLoadDLLs) + __delayLoadHelper2 true 6.01 Windows @@ -75,7 +77,7 @@ StreamingSIMDExtensions - ..\..\sdk\lib\i386;%(AdditionalLibraryDirectories) + ..\..\sdk\lib\i386;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) MachineX86 @@ -83,7 +85,7 @@ - ..\..\sdk\lib\amd64;%(AdditionalLibraryDirectories) + ..\..\sdk\lib\amd64;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) MachineX64 diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index cd4dbd29034d..9b5ca897757d 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -310,7 +310,7 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWndHandle); + PhCenterWindow(hwndDlg, PhMainWindowHandle); context->DialogHandle = hwndDlg; context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); @@ -627,7 +627,7 @@ VOID StatusBarShowCustomizeDialog( DialogBox( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_CUSTOMIZE_SB), - PhMainWndHandle, + PhMainWindowHandle, CustomizeStatusBarDialogProc ); } \ No newline at end of file diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index cef8fdffbb33..c457964c4ffc 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -509,7 +509,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWndHandle); + PhCenterWindow(hwndDlg, PhMainWindowHandle); context->DialogHandle = hwndDlg; context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); @@ -788,12 +788,12 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } else { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); + SetMenu(PhMainWindowHandle, MainMenu); + DrawMenuBar(PhMainWindowHandle); } } } @@ -937,7 +937,7 @@ VOID ToolBarShowCustomizeDialog( DialogBox( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_CUSTOMIZE_TB), - PhMainWndHandle, + PhMainWindowHandle, CustomizeToolbarDialogProc ); } \ No newline at end of file diff --git a/plugins/ToolStatus/graph.c b/plugins/ToolStatus/graph.c index c8effcd4396e..97a62b7b1ae2 100644 --- a/plugins/ToolStatus/graph.c +++ b/plugins/ToolStatus/graph.c @@ -45,7 +45,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -65,7 +65,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -85,7 +85,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -105,7 +105,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -449,7 +449,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) PhDivideSinglesBySingle( MemGraphState.Data1, - (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, + (FLOAT)PhGetSystemBasicInformation().NumberOfPhysicalPages, drawInfo->LineDataCount ); @@ -617,7 +617,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) if (record) { - PhShowProcessRecordDialog(PhMainWndHandle, record); + PhShowProcessRecordDialog(PhMainWindowHandle, record); PhDereferenceProcessRecord(record); } } @@ -651,7 +651,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) if (record) { - PhShowProcessRecordDialog(PhMainWndHandle, record); + PhShowProcessRecordDialog(PhMainWindowHandle, record); PhDereferenceProcessRecord(record); } } diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 2c81fa57babb..29f4ad589e31 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -252,7 +252,7 @@ VOID ShowCustomizeMenu( selectedItem = PhShowEMenu( menu, - PhMainWndHandle, + PhMainWindowHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, @@ -271,12 +271,12 @@ VOID ShowCustomizeMenu( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } else { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); + SetMenu(PhMainWindowHandle, MainMenu); + DrawMenuBar(PhMainWindowHandle); } } break; @@ -293,7 +293,7 @@ VOID ShowCustomizeMenu( { // Adding the Searchbox makes it focused, // reset the focus back to the main window. - SetFocus(PhMainWndHandle); + SetFocus(PhMainWindowHandle); } } break; @@ -452,8 +452,8 @@ VOID NTAPI LayoutPaddingCallback( SendMessage(RebarHandle, WM_SIZE, 0, 0); - // TODO: GetClientRect with PhMainWndHandle causes crash. - //GetClientRect(PhMainWndHandle, &clientRect); + // TODO: GetClientRect with PhMainWindowHandle causes crash. + //GetClientRect(PhMainWindowHandle, &clientRect); GetClientRect(RebarHandle, &rebarRect); // Adjust the PH client area and exclude the rebar width. @@ -556,22 +556,22 @@ BOOLEAN NTAPI MessageLoopFilter( ) { if ( - Message->hwnd == PhMainWndHandle || - IsChild(PhMainWndHandle, Message->hwnd) + Message->hwnd == PhMainWindowHandle || + IsChild(PhMainWindowHandle, Message->hwnd) ) { - if (TranslateAccelerator(PhMainWndHandle, AcceleratorTable, Message)) + if (TranslateAccelerator(PhMainWindowHandle, AcceleratorTable, Message)) return TRUE; - if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWndHandle)) + if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWindowHandle)) { 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); + SetMenu(PhMainWindowHandle, MainMenu); + DrawMenuBar(PhMainWindowHandle); + SendMessage(PhMainWindowHandle, WM_SYSCHAR, Message->wParam, Message->lParam); return TRUE; } } @@ -774,7 +774,7 @@ LRESULT CALLBACK MainWndSubclassProc( case RBN_HEIGHTCHANGE: { // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); + SendMessage(PhMainWindowHandle, WM_SIZE, 0, 0); } break; case RBN_CHEVRONPUSHED: @@ -886,7 +886,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (selectedItem && selectedItem->Id != -1) { - SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + SendMessage(PhMainWindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); } PhDestroyEMenu(menu); @@ -997,7 +997,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (selectedItem && selectedItem->Id != -1) { - SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + SendMessage(PhMainWindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); } PhDestroyEMenu(menu); @@ -1123,7 +1123,7 @@ LRESULT CALLBACK MainWndSubclassProc( 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, + SetWindowPos(PhMainWindowHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); TargetingWindow = FALSE; @@ -1236,7 +1236,7 @@ LRESULT CALLBACK MainWndSubclassProc( } } - SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, + SetWindowPos(PhMainWindowHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); TargetingCompleted = TRUE; @@ -1263,14 +1263,14 @@ LRESULT CALLBACK MainWndSubclassProc( if (!ToolStatusConfig.AutoHideMenu) break; - if (GetMenu(PhMainWndHandle)) + if (GetMenu(PhMainWindowHandle)) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } else { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); + SetMenu(PhMainWindowHandle, MainMenu); + DrawMenuBar(PhMainWindowHandle); } } else if ((wParam & 0xFFF0) == SC_MINIMIZE) @@ -1288,9 +1288,9 @@ LRESULT CALLBACK MainWndSubclassProc( if (!ToolStatusConfig.AutoHideMenu) break; - if (GetMenu(PhMainWndHandle)) + if (GetMenu(PhMainWindowHandle)) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } } break; @@ -1309,21 +1309,21 @@ VOID NTAPI MainWindowShowingCallback( { PhRegisterMessageLoopFilter(MessageLoopFilter, NULL); PhRegisterCallback( - ProcessHacker_GetCallbackLayoutPadding(PhMainWndHandle), + ProcessHacker_GetCallbackLayoutPadding(PhMainWindowHandle), LayoutPaddingCallback, NULL, &LayoutPaddingCallbackRegistration ); - SetWindowSubclass(PhMainWndHandle, MainWndSubclassProc, 0, 0); + SetWindowSubclass(PhMainWindowHandle, MainWndSubclassProc, 0, 0); ToolbarLoadSettings(); ReBarLoadLayoutSettings(); StatusBarLoadSettings(); - MainMenu = GetMenu(PhMainWndHandle); + MainMenu = GetMenu(PhMainWindowHandle); if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } } diff --git a/plugins/ToolStatus/options.c b/plugins/ToolStatus/options.c index f46e5b5564ac..fed1a0811a97 100644 --- a/plugins/ToolStatus/options.c +++ b/plugins/ToolStatus/options.c @@ -66,12 +66,12 @@ INT_PTR CALLBACK OptionsDlgProc( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWndHandle, NULL); + SetMenu(PhMainWindowHandle, NULL); } else { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); + SetMenu(PhMainWindowHandle, MainMenu); + DrawMenuBar(PhMainWindowHandle); } EndDialog(hwndDlg, IDOK); diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index 92a8515eb812..550771c7c7a3 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -229,7 +229,7 @@ VOID StatusBarShowMenu( selectedItem = PhShowEMenu( menu, - PhMainWndHandle, + PhMainWindowHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, cursorPos.x, @@ -324,8 +324,8 @@ VOID StatusBarUpdate( break; case ID_STATUS_PHYSICALMEMORY: { - ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; - FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages * 100; + ULONG physicalUsage = PhGetSystemBasicInformation().NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; + FLOAT physicalFraction = (FLOAT)physicalUsage / PhGetSystemBasicInformation().NumberOfPhysicalPages * 100; text[count] = PhFormatString( L"Physical memory: %s (%.2f%%)", @@ -337,7 +337,7 @@ VOID StatusBarUpdate( case ID_STATUS_FREEMEMORY: { ULONG physicalFree = SystemStatistics.Performance->AvailablePages; - FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhSystemBasicInformation.NumberOfPhysicalPages * 100; + FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhGetSystemBasicInformation().NumberOfPhysicalPages * 100; text[count] = PhFormatString( L"Free memory: %s (%.2f%%)", diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index c7b752460c7b..6393b2771090 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -127,7 +127,7 @@ VOID RebarLoadSettings( 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, + PhMainWindowHandle, NULL, NULL, NULL @@ -188,7 +188,7 @@ VOID RebarLoadSettings( NULL, WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - PhMainWndHandle, + PhMainWindowHandle, NULL, NULL, NULL @@ -374,7 +374,7 @@ VOID ToolbarLoadSettings( } // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); + SendMessage(PhMainWindowHandle, WM_SIZE, 0, 0); } VOID ToolbarResetSettings( diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 77c0c978bda6..902ba775c308 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -88,8 +88,8 @@ INT_PTR CALLBACK TextDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhInitializeLayoutManager(&LayoutManager, hwndDlg); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 0b766debcfeb..07b6e873b18d 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -71,16 +71,16 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( info.nShow = SW_SHOW; info.hwnd = hwndDlg; - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); if (ShellExecuteEx(&info)) { - ProcessHacker_Destroy(PhMainWndHandle); + ProcessHacker_Destroy(PhMainWindowHandle); } else { // Install failed, cancel the shutdown. - ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); + ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); // Set button text for next action //Button_SetText(GetDlgItem(hwndDlg, IDOK), L"Retry"); @@ -144,7 +144,7 @@ VOID ShowLatestVersionDialog( config.lpCallbackData = (LONG_PTR)Context; // HACK - imageDosHeader = (PIMAGE_DOS_HEADER)NtCurrentPeb()->ImageBaseAddress; + imageDosHeader = (PIMAGE_DOS_HEADER)PhImageBaseAddress; imageNtHeader = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(imageDosHeader, (ULONG)imageDosHeader->e_lfanew); RtlSecondsSince1970ToTime(imageNtHeader->FileHeader.TimeDateStamp, &time); PhLargeIntegerToLocalSystemTime(&systemTime, &time); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 6cc3cdc1facd..328bd6f35862 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -70,8 +70,8 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); @@ -317,7 +317,7 @@ BOOLEAN QueryUpdateData( if (!(httpSessionHandle = WinHttpOpen( NULL, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -327,7 +327,7 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -364,7 +364,7 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) + if (PhWindowsVersion() >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -760,7 +760,7 @@ NTSTATUS UpdateDownloadThread( // Open the HTTP session with the system proxy configuration if available if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -770,7 +770,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) + if (PhWindowsVersion() >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); @@ -801,7 +801,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) + if (PhWindowsVersion() >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -1093,11 +1093,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( // break; //case WM_NCACTIVATE: // { - // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) + // if (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) // { // if (!context->FixedWindowStyles) // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); + // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWindowHandle); // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); // context->FixedWindowStyles = TRUE; // } @@ -1126,7 +1126,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( 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); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); @@ -1193,7 +1193,7 @@ VOID ShowUpdateDialog( { if (!(UpdateDialogThreadHandle = PhCreateThread(0, ShowUpdateDialogThread, Context))) { - PhShowStatus(PhMainWndHandle, L"Unable to create the updater window.", 0, GetLastError()); + PhShowStatus(PhMainWindowHandle, L"Unable to create the updater window.", 0, GetLastError()); return; } diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index ab8b850a635c..5afc567627b0 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -55,9 +55,6 @@ ((ULONGLONG)(build) << 16) | \ ((ULONGLONG)(revision) << 0)) -#define UT_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define UT_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) - #ifdef _DEBUG // Force update checks to succeed (most of the below flags require this to be defined). //#define FORCE_UPDATE_CHECK diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 88a55cfa438b..42026cb27874 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -181,7 +181,7 @@ ULONG GetProcessAffinity( if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PhProcessQueryAccess(), ProcessId ))) { @@ -208,7 +208,7 @@ IO_PRIORITY_HINT GetProcessIoPriority( if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PhProcessQueryAccess(), ProcessId ))) { @@ -425,7 +425,7 @@ VOID NTAPI MenuItemCallback( if (!highlightPresent) { CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; - chooseColor.hwndOwner = PhMainWndHandle; + chooseColor.hwndOwner = PhMainWindowHandle; chooseColor.lpCustColors = ProcessCustomColors; chooseColor.lpfnHook = ColorDlgHookProc; chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK; @@ -624,7 +624,7 @@ VOID NTAPI MenuHookCallback( affinityMask = GetProcessAffinity(processItem->ProcessId); // Show the affinity dialog (with our values). - if (PhShowProcessAffinityDialog2(PhMainWndHandle, affinityMask, &newAffinityMask)) + if (PhShowProcessAffinityDialog2(PhMainWindowHandle, affinityMask, &newAffinityMask)) { PDB_OBJECT object; @@ -1432,13 +1432,14 @@ LOGICAL DllMain( NULL, &MiListSectionMenuInitializingCallbackRegistration ); - PhRegisterCallback(&PhProcessModifiedEvent, + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderModified), ProcessModifiedCallback, NULL, &ProcessModifiedCallbackRegistration ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration @@ -1881,7 +1882,7 @@ UINT_PTR CALLBACK ColorDlgHookProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWndHandle); + PhCenterWindow(hwndDlg, PhMainWindowHandle); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj b/plugins/WindowExplorer/WindowExplorer.vcxproj index f61d6d998529..955db20f5099 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj @@ -53,12 +53,12 @@ - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) + ProcessHacker.exe;comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) + ProcessHacker.exe;comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index c31834ae619d..987bdd8e6833 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -231,7 +231,7 @@ LOGICAL DllMain( isClient = FALSE; - if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhLibImageBase")) + if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhGetImageBase")) { isClient = TRUE; } diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index dba7a96bc9a2..2f03023ad597 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -116,8 +116,8 @@ VOID WeShowWindowProperties( // utils -#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhMainWndHandle")) -#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("WindowsVersion")) +#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhGetMainWndHandle")) +#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("PhWindowsVersion")) PVOID WeGetProcedureAddress( _In_ PSTR Name diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 515b17ee9c40..7ed88dbf2a40 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -611,7 +611,7 @@ static VOID WepEnsureHookDataValid( #ifdef _WIN64 // We can't use the hook on WOW64 processes. - if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("ProcessQueryAccess"), Context->ClientId.UniqueProcess))) + if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("PhProcessQueryAccess"), Context->ClientId.UniqueProcess))) { PhGetProcessIsWow64(processHandle, &isWow64); NtClose(processHandle); diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index a5a6ae577e12..14e547b6dcc5 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -96,14 +96,15 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true Windows MachineX86 6.01 + __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -125,14 +126,15 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true Windows MachineX64 6.01 + __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -157,8 +159,8 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true @@ -168,6 +170,7 @@ MachineX86 true 6.01 + __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -191,8 +194,8 @@ true - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true @@ -202,6 +205,7 @@ MachineX64 true 6.01 + __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) From 63a688a90e4b188496143607864363a2a2fe05ce Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 11:58:14 +1000 Subject: [PATCH 139/839] Setup: Fix build, Fix licence window, Add initial web-setup --- .../CustomSetupTool/CustomSetupTool.vcxproj | 48 +- .../CustomSetupTool.vcxproj.filters | 131 ++- .../CustomSetupTool/download.c | 771 +++++++++++++++ .../CustomSetupTool/include/setup.h | 50 +- .../CustomSetupTool/json-c/AUTHORS | 5 + .../CustomSetupTool/json-c/COPYING | 42 + .../CustomSetupTool/json-c/ChangeLog | 214 +++++ .../CustomSetupTool/json-c/arraylist.c | 101 ++ .../CustomSetupTool/json-c/arraylist.h | 56 ++ .../CustomSetupTool/json-c/bits.h | 28 + .../CustomSetupTool/json-c/config.h | 89 ++ .../CustomSetupTool/json-c/config.h.in | 174 ++++ .../CustomSetupTool/json-c/debug.c | 83 ++ .../CustomSetupTool/json-c/debug.h | 71 ++ .../CustomSetupTool/json-c/json.h | 34 + .../CustomSetupTool/json-c/json_c_version.c | 20 + .../CustomSetupTool/json-c/json_c_version.h | 22 + .../CustomSetupTool/json-c/json_config.h | 3 + .../CustomSetupTool/json-c/json_inttypes.h | 28 + .../CustomSetupTool/json-c/json_object.c | 860 +++++++++++++++++ .../CustomSetupTool/json-c/json_object.h | 611 ++++++++++++ .../json-c/json_object_iterator.c | 168 ++++ .../json-c/json_object_iterator.h | 239 +++++ .../json-c/json_object_private.h | 47 + .../CustomSetupTool/json-c/json_tokener.c | 888 ++++++++++++++++++ .../CustomSetupTool/json-c/json_tokener.h | 208 ++++ .../CustomSetupTool/json-c/json_util.c | 301 ++++++ .../CustomSetupTool/json-c/json_util.h | 41 + .../CustomSetupTool/json-c/libjson.c | 26 + .../CustomSetupTool/json-c/linkhash.c | 604 ++++++++++++ .../CustomSetupTool/json-c/linkhash.h | 292 ++++++ .../CustomSetupTool/json-c/math_compat.h | 28 + .../CustomSetupTool/json-c/printbuf.c | 194 ++++ .../CustomSetupTool/json-c/printbuf.h | 81 ++ .../CustomSetupTool/json-c/random_seed.c | 238 +++++ .../CustomSetupTool/json-c/random_seed.h | 25 + tools/CustomSetupTool/CustomSetupTool/main.c | 10 + tools/CustomSetupTool/CustomSetupTool/page2.c | 16 +- tools/CustomSetupTool/CustomSetupTool/page5.c | 171 ++++ .../CustomSetupTool/resource.h | 25 +- .../CustomSetupTool/resource.rc | 117 +-- .../resources/{Licence.txt => LICENSE.txt} | 97 +- .../CustomSetupTool/resources/version.rc | 80 ++ .../CustomSetupTool/uninstall.c | 7 - .../CustomSetupTool/CustomSetupTool/update.c | 12 +- 45 files changed, 7162 insertions(+), 194 deletions(-) create mode 100644 tools/CustomSetupTool/CustomSetupTool/download.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/COPYING create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/bits.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/config.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/debug.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/debug.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h create mode 100644 tools/CustomSetupTool/CustomSetupTool/page5.c rename tools/CustomSetupTool/CustomSetupTool/resources/{Licence.txt => LICENSE.txt} (67%) create mode 100644 tools/CustomSetupTool/CustomSetupTool/resources/version.rc diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index ec82116e1157..cd939c79a92a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -69,6 +69,10 @@ Windows RequireAdministrator + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -97,16 +101,33 @@ true RequireAdministrator + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + @@ -114,14 +135,29 @@ + + + + + + + + + + + + + + + + + - - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) - + + @@ -133,10 +169,10 @@ - + - + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index fd0778f3dc06..85f331c02a3f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -13,24 +13,24 @@ {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} + @@ -66,6 +66,45 @@ Source Files + + Source Files + + + Source Files\pages + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + + + Source Files\json + @@ -80,33 +119,87 @@ Header Files\zip + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + + + Header Files\json + - 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 - + + Resource Files\Content + \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c new file mode 100644 index 000000000000..9ae9010c1566 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -0,0 +1,771 @@ +#include +#include +#include +#include + +#include "json-c\json.h" + +typedef struct _PH_SETUP_DOWNLOAD_CONTEXT +{ + HWND DialogHandle; + HICON IconSmallHandle; + HICON IconLargeHandle; +} PH_SETUP_DOWNLOAD_CONTEXT, *PPH_SETUP_DOWNLOAD_CONTEXT; + +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; +} + +BOOLEAN ParseVersionString( + _Inout_ PPH_SETUP_DOWNLOAD_CONTEXT Context + ) +{ + PH_STRINGREF remaining, majorPart, minorPart, revisionPart; + ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; + + //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; + 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; +} + +json_object_ptr json_get_object( + _In_ json_object_ptr rootObj, + _In_ const PSTR key +) +{ + json_object_ptr returnObj; + + if (json_object_object_get_ex(rootObj, key, &returnObj)) + { + return returnObj; + } + + return NULL; +} + + +BOOLEAN SetupQueryUpdateData( + _Inout_ PPH_SETUP_UNINSTALL_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(); + + 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) + { + 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 (!(httpRequestHandle = WinHttpOpenRequest( + httpConnectionHandle, + NULL, + L"/processhacker/nightly.php?latest", + 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 (!(jsonObject = json_tokener_parse(stringBuffer))) + goto CleanupExit; + + Context->Version = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "version"))); + //Context->RevVersion = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "rev"))); + Context->RelDate = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "updated"))); + Context->Size = PhFormatSize(json_object_get_int64(json_get_object(jsonObject, "size")), -1); + Context->Hash = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "hash_setup"))); + Context->Signature = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "sig"))); + Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "forum_url"))); + Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "bin_url"))); + Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "setup_url"))); + //Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "message")); + + PH_STRINGREF remaining, majorPart, minorPart, revisionPart; + ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; + + 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->LatestMajorVersion = (ULONG)majorInteger; + Context->LatestMinorVersion = (ULONG)minorInteger; + Context->LatestRevisionVersion = (ULONG)revisionInteger; + + /* 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")); + + PH_STRING_BUILDER sb; + PhInitializeStringBuilder(&sb, 0x100); + for (size_t i = 0; i < Context->BuildMessage->Length; i++) + { + if (Context->BuildMessage->Data[i] == '\n') + PhAppendFormatStringBuilder(&sb, L"\r\n"); + else + PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); + } + PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); + + CleanupJsonParser(jsonObject); + + 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 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; +//} + +BOOLEAN UpdateDownloadUpdateData( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ) +{ + 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) }; + 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 + ); + + setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); + + if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) + goto CleanupExit; + + PhGenerateGuid(&randomGuid); + + if (randomGuidString = PhFormatGuid(&randomGuid)) + { + PhMoveReference( + &randomGuidString, + PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2) + ); + } + + Context->SetupFilePath = PhFormatString( + L"%s%s\\processhacker-%lu.%lu.%lu-bin.zip", + PhGetStringOrEmpty(setupTempPath), + PhGetStringOrEmpty(randomGuidString), + Context->LatestMajorVersion, + Context->LatestMinorVersion, + Context->LatestRevisionVersion + ); + if (PhIsNullOrEmptyString(Context->SetupFilePath)) + goto CleanupExit; + + 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); + } + } + + 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; + } + + 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; + } + + downloadHostPath = PhCreateStringEx( + httpUrlComponents.lpszHostName, + httpUrlComponents.dwHostNameLength * sizeof(WCHAR) + ); + downloadUrlPath = PhCreateStringEx( + httpUrlComponents.lpszUrlPath, + httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) + ); + + SetWindowText(Context->MainHeaderHandle, PhFormatString(L"Downloading Process Hacker %lu.%lu.%lu...", + Context->LatestMajorVersion, + Context->CurrentMinorVersion, + Context->LatestRevisionVersion + )->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) + { + ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; + WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, 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) + { + ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; + WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, 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(totalLength); + PhDereferenceObject(totalDownloaded); + } + } + } + +CleanupExit: + + 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); + // } + //} + + return STATUS_SUCCESS; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index f703b2f2a180..c01ad508d6d5 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -40,9 +40,9 @@ #include #include -#include "resource.h" #include +#include "resource.h" #include "..\..\ProcessHacker\include\phappres.h" // Win32 PropertySheet Control IDs @@ -121,6 +121,13 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( _Inout_ LPARAM lParam ); +INT_PTR CALLBACK SetupPropPage5_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ); + // page4.c typedef struct _SETUP_PROGRESS_THREAD @@ -176,6 +183,47 @@ VOID SetupUpgradeSettingsFile( VOID ); +// download.c + +typedef struct _PH_SETUP_UNINSTALL_CONTEXT +{ + HWND DialogHandle; + HWND MainHeaderHandle; + HWND StatusHandle; + HWND SubStatusHandle; + HWND ProgressHandle; + HICON IconSmallHandle; + HICON IconLargeHandle; + + ULONG CurrentMajorVersion; + ULONG CurrentMinorVersion; + ULONG CurrentRevisionVersion; + ULONG LatestMajorVersion; + ULONG LatestMinorVersion; + ULONG LatestRevisionVersion; + + ULONG ErrorCode; + PPH_STRING Version; + PPH_STRING RevVersion; + PPH_STRING RelDate; + PPH_STRING Size; + PPH_STRING Hash; + PPH_STRING Signature; + PPH_STRING ReleaseNotesUrl; + + PPH_STRING BinFileDownloadUrl; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFilePath; +} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; + +BOOLEAN SetupQueryUpdateData( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); + +BOOLEAN UpdateDownloadUpdateData( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ); + // extract.c BOOLEAN SetupExtractBuild( diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS b/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS new file mode 100644 index 000000000000..b389989c452b --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS @@ -0,0 +1,5 @@ +Michael Clark +Jehiah Czebotar +Eric Haszlakiewicz +C. Watford (christopher.watford@gmail.com) + diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING b/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING new file mode 100644 index 000000000000..740d1258d425 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING @@ -0,0 +1,42 @@ + +Copyright (c) 2009-2012 Eric Haszlakiewicz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. + +---------------------------------------------------------------- + +Copyright (c) 2004, 2005 Metaparadigm Pte Ltd + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog b/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog new file mode 100644 index 000000000000..451b8f68403a --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog @@ -0,0 +1,214 @@ + +0.12 + + * Address security issues: + * CVE-2013-6371: hash collision denial of service + * CVE-2013-6370: buffer overflow if size_t is larger than int + + * Avoid potential overflow in json_object_get_double + + * Eliminate the mc_abort() function and MC_ABORT macro. + + * Make the json_tokener_errors array local. It has been deprecated for + a while, and json_tokener_error_desc() should be used instead. + + * change the floating point output format to %.17g so values with + more than 6 digits show up in the output. + + * Remove the old libjson.so name compatibility support. The library is + only created as libjson-c.so now and headers are only installed + into the ${prefix}/json-c directory. + + * When supported by the linker, add the -Bsymbolic-functions flag. + + * Various changes to fix the build on MSVC. + + * Make strict mode more strict: + * number must not start with 0 + * no single-quote strings + * no comments + * trailing char not allowed + * only allow lowercase literals + + * Added a json_object_new_double_s() convenience function to allow + an exact string representation of a double to be specified when + creating the object and use it in json_tokener_parse_ex() so + a re-serialized object more exactly matches the input. + + * Add support NaN and Infinity + + +0.11 + + * IMPORTANT: the name of the library has changed to libjson-c.so and + the header files are now in include/json-c. + The pkgconfig name has also changed from json to json-c. + You should change your build to use appropriate -I and -l options. + A compatibility shim is in place so builds using the old name will + continue to work, but that will be removed in the next release. + * Maximum recursion depth is now a runtime option. + json_tokener_new() is provided for compatibility. + json_tokener_new_ex(depth) + * Include json_object_iterator.h in the installed headers. + * Add support for building on Android. + * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. + * Make it safe to delete keys while iterating with the json_object_object_foreach macro. + * Add a json_set_serializer() function to allow the string output of a json_object to be customized. + * Make float parsing locale independent. + * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. + * Enable -Werror when building. + * speed improvements to parsing 64-bit integers on systems with working sscanf + * Add a json_object_object_length function. + * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. + +0.10 + + * Add a json_object_to_json_string_ext() function to allow output to be + formatted in a more human readable form. + * Add json_object_object_get_ex(), a NULL-safe get object method, to be able + to distinguish between a key not present and the value being NULL. + * Add an alternative iterator implementation, see json_object_iterator.h + * Make json_object_iter public to enable external use of the + json_object_object_foreachC macro. + * Add a printbuf_memset() function to provide an effecient way to set and + append things like whitespace indentation. + * Adjust json_object_is_type and json_object_get_type so they return + json_type_null for NULL objects and handle NULL passed to + json_objct_object_get(). + * Rename boolean type to json_bool. + * Fix various compile issues for Visual Studio and MinGW. + * Allow json_tokener_parse_ex() to be re-used to parse multiple object. + Also, fix some parsing issues with capitalized hexadecimal numbers and + number in E notation. + * Add json_tokener_get_error() and json_tokener_error_desc() to better + encapsulate the process of retrieving errors while parsing. + * Various improvements to the documentation of many functions. + * Add new json_object_array_sort() function. + * Fix a bug in json_object_get_int(), which would incorrectly return 0 + when called on a string type object. + Eric Haszlakiewicz + * Add a json_type_to_name() function. + Eric Haszlakiewicz + * Add a json_tokener_parse_verbose() function. + Jehiah Czebotar + * Improve support for null bytes within JSON strings. + Jehiah Czebotar + * Fix file descriptor leak if memory allocation fails in json_util + Zachary Blair, zack_blair at hotmail dot com + * Add int64 support. Two new functions json_object_net_int64 and + json_object_get_int64. Binary compatibility preserved. + Eric Haszlakiewicz, EHASZLA at transunion com + Rui Miguel Silva Seabra, rms at 1407 dot org + * Fix subtle bug in linkhash where lookup could hang after all slots + were filled then successively freed. + Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com + * Make json_object_from_file take const char *filename + Spotted by Vikram Raj V, vsagar at attinteractive dot com + * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) + Brent Miller, bdmiller at yahoo dash inc dot com + * Correction to comment describing printbuf_memappend in printbuf.h + Brent Miller, bdmiller at yahoo dash inc dot com + +0.9 + * Add README.html README-WIN32.html config.h.win32 to Makefile.am + Michael Clark, + * Add const qualifier to the json_tokener_parse functions + Eric Haszlakiewicz, EHASZLA at transunion dot com + * Rename min and max so we can never clash with C or C++ std library + Ian Atha, thatha at yahoo dash inc dot com + * Fix any noticeable spelling or grammar errors. + * Make sure every va_start has a va_end. + * Check all pointers for validity. + Erik Hovland, erik at hovland dot org + * Fix json_object_get_boolean to return false for empty string + Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com + * optimizations to json_tokener_parse_ex(), printbuf_memappend() + Brent Miller, bdmiller at yahoo dash inc dot com + * Disable REFCOUNT_DEBUG by default in json_object.c + * Don't use this as a variable, so we can compile with a C++ compiler + * Add casts from void* to type of assignment when using malloc + * Add #ifdef __cplusplus guards to all of the headers + * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table + Michael Clark, + * Null pointer dereference fix. Fix json_object_get_boolean strlen test + to not return TRUE for zero length string. Remove redundant includes. + Erik Hovland, erik at hovland dot org + * Fixed warning reported by adding -Wstrict-prototypes + -Wold-style-definition to the compilatin flags. + Dotan Barak, dotanba at gmail dot com + * Add const correctness to public interfaces + Gerard Krol, g dot c dot krol at student dot tudelft dot nl + +0.8 + * Add va_end for every va_start + Dotan Barak, dotanba at gmail dot com + * Add macros to enable compiling out debug code + Geoffrey Young, geoff at modperlcookbook dot org + * Fix bug with use of capital E in numbers with exponents + Mateusz Loskot, mateusz at loskot dot net + * Add stddef.h include + * Patch allows for json-c compile with -Werror and not fail due to + -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations + Geoffrey Young, geoff at modperlcookbook dot org + +0.7 + * Add escaping of backslash to json output + * Add escaping of foward slash on tokenizing and output + * Changes to internal tokenizer from using recursion to + using a depth state structure to allow incremental parsing + +0.6 + * Fix bug in escaping of control characters + Johan Bj�rklund, johbjo09 at kth dot se + * Remove include "config.h" from headers (should only + be included from .c files) + Michael Clark + +0.5 + * Make headers C++ compatible by change *this to *obj + * Add ifdef C++ extern "C" to headers + * Use simpler definition of min and max in bits.h + Larry Lansing, llansing at fuzzynerd dot com + + * Remove automake 1.6 requirement + * Move autogen commands into autogen.sh. Update README + * Remove error pointer special case for Windows + * Change license from LGPL to MIT + Michael Clark + +0.4 + * Fix additional error case in object parsing + * Add back sign reversal in nested object parse as error pointer + value is negative, while error value is positive. + Michael Clark + +0.3 + * fix pointer arithmetic bug for error pointer check in is_error() macro + * fix type passed to printbuf_memappend in json_tokener + * update autotools bootstrap instructions in README + Michael Clark + +0.2 + * printbuf.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of vasprintf + * debug.c - C. Watford (christopher.watford@gmail.com) + Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script + * json_object.c - C. Watford (christopher.watford@gmail.com) + Added scope operator to wrap usage of json_object_object_foreach, this + needs to be rethought to be more ANSI C friendly + * json_object.h - C. Watford (christopher.watford@gmail.com) + Added Microsoft C friendly version of json_object_object_foreach + * json_tokener.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of strndup + * json_util.c - C. Watford (christopher.watford@gmail.com) + Added cast and mask to suffice size_t v. unsigned int conversion + correctness + * json_tokener.c - sign reversal issue on error info for nested object parse + spotted by Johan Bj�rklund (johbjo09 at kth.se) + * json_object.c - escape " in json_escape_str + * Change to automake and libtool to build shared and static library + Michael Clark + +0.1 + * initial release diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c new file mode 100644 index 000000000000..97f2c921711b --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c @@ -0,0 +1,101 @@ +/* + * $Id: arraylist.c,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. + * + */ + +#include "config.h" + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +# include +#endif /* HAVE_STRINGS_H */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + int i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, int i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, int max) +{ + void *t; + int 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; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, int idx, void *data) +{ + if(array_list_expand_internal(arr, idx+1)) return -1; + if(arr->array[idx]) arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, 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); +} + +int +array_list_length(struct array_list *arr) +{ + return arr->length; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h new file mode 100644 index 000000000000..089be0bd7215 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h @@ -0,0 +1,56 @@ +/* + * $Id: arraylist.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. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(__cdecl* compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h b/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h new file mode 100644 index 000000000000..c8cbbc820d5b --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h @@ -0,0 +1,28 @@ +/* + * $Id: bits.h,v 1.10 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 _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 + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define error_description(error) (json_tokener_errors[error]) +#define is_error(ptr) (ptr == NULL) + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/config.h b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h new file mode 100644 index 000000000000..df46f0a481a7 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h @@ -0,0 +1,89 @@ +#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/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in new file mode 100644 index 000000000000..0dcab1a30015 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in @@ -0,0 +1,174 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* 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. */ +#undef HAVE_DECL_INFINITY + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#undef HAVE_DECL_ISINF + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#undef HAVE_DECL_ISNAN + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#undef HAVE_DECL_NAN + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you + don't. */ +#undef HAVE_DECL__FINITE + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. + */ +#undef HAVE_DECL__ISNAN + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* 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. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#undef HAVE_REALLOC + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* 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_CDEFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Public define for json_inttypes.h */ +#undef JSON_C_HAVE_INTTYPES_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* 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/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c new file mode 100644 index 000000000000..9dff7818bf86 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c @@ -0,0 +1,83 @@ +/* + * $Id: debug.c,v 1.5 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. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h new file mode 100644 index 000000000000..80ca3e43042e --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h @@ -0,0 +1,71 @@ +/* + * $Id: debug.h,v 1.5 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 _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); + +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json.h new file mode 100644 index 000000000000..4339b20e9060 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json.h @@ -0,0 +1,34 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 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_h_ +#define _json_h_ + +#ifdef __cplusplus +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_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c new file mode 100644 index 000000000000..13eb18855423 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c @@ -0,0 +1,20 @@ +/* + * 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. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h new file mode 100644 index 000000000000..eed98a497501 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h @@ -0,0 +1,22 @@ +/* + * 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/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h new file mode 100644 index 000000000000..405fda20dc5b --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h @@ -0,0 +1,3 @@ + +/* Define to 1 if you have the header file. */ +#define JSON_C_HAVE_INTTYPES_H 1 diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h new file mode 100644 index 000000000000..9de8d246d9dd --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h @@ -0,0 +1,28 @@ + +#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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c new file mode 100644 index 000000000000..57f3f0d237d5 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c @@ -0,0 +1,860 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h new file mode 100644 index 000000000000..c649ab7c7bfb --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h @@ -0,0 +1,611 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c new file mode 100644 index 000000000000..7066649c3bc5 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c @@ -0,0 +1,168 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 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. +* +* @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. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +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); +} + + +/** + * **************************************************************************** + */ +struct json_object* +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); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h new file mode 100644 index 000000000000..44c9fb25b6ca --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h @@ -0,0 +1,239 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 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. +* +* @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. +* +* API attributes:
    +* * Thread-safe: NO
    +* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h new file mode 100644 index 000000000000..deff7e8df48f --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h @@ -0,0 +1,47 @@ +/* + * $Id: json_object_private.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. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + char *str; + size_t len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c new file mode 100644 index 000000000000..2959cbd6aea2 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c @@ -0,0 +1,888 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 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) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +#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 */ + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +/* Use C99 NAN by default; if not available, nan("") should work too. */ +#ifndef NAN +#define NAN nan("") +#endif /* !NAN */ + +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_nan_str[] = "NaN"; +static const int json_nan_str_len = sizeof(json_nan_str) - 1; +static const char json_true_str[] = "true"; +static const int json_true_str_len = sizeof(json_true_str) - 1; +static const char json_false_str[] = "false"; +static const int json_false_str_len = sizeof(json_false_str) - 1; + +static const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", + "buffer size overflow" +}; + +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()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + 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)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +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); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * 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) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + 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"); +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + /* this interface is presently not 64-bit clean due to the int len argument + and the internal printbuf interface that takes 32-bit int len arguments + so the function limits the maximum string size to INT32_MAX (2GB). + If the function is called with len == -1 then strlen is called to check + the string length is less than INT32_MAX (2GB) */ + if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { + tok->err = json_tokener_error_size; + return NULL; + } + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'I': + case 'i': + state = json_tokener_state_inf; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case 'N': + case 'n': + state = json_tokener_state_null; // or NaN + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '\'': + if (tok->flags & JSON_TOKENER_STRICT) { + /* in STRICT mode only double-quote are allowed */ + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + case '"': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_inf: /* aka starts with 'i' */ + { + int size; + int size_inf; + int is_negative = 0; + + 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 == '-') + { + 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) + { + current = json_object_new_double(is_negative ? -INFINITY : INFINITY); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + } + break; + case json_tokener_state_null: /* aka starts with 'n' */ + { + int size; + int size_nan; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_nan = json_min(tok->st_pos+1, json_nan_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_null_str, tok->pb->buf, size) == 0) + || (strncmp(json_null_str, tok->pb->buf, size) == 0) + ) { + if (tok->st_pos == json_null_str_len) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || + (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0) + ) + { + if (tok->st_pos == json_nan_str_len) + { + current = json_object_new_double(NAN); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + 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); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* 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(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + 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) && + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + 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); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + 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 */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + { + int size1, size2; + printbuf_memappend_fast(tok->pb, &c, 1); + size1 = json_min(tok->st_pos+1, json_true_str_len); + size2 = json_min(tok->st_pos+1, json_false_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_true_str, tok->pb->buf, size1) == 0) + || (strncmp(json_true_str, tok->pb->buf, size1) == 0) + ) { + if(tok->st_pos == json_true_str_len) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_false_str, tok->pb->buf, size2) == 0) + || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { + if(tok->st_pos == json_false_str_len) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + while(c && strchr(json_number_chars, c)) { + ++case_len; + if(c == '.' || c == 'e' || c == 'E') + tok->is_double = 1; + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + + // Check for -Infinity + if (tok->pb->buf[0] == '-' && case_len == 1 && + (c == 'i' || c == 'I')) + { + state = json_tokener_state_inf; + goto redo_char; + } + } + { + 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)) { + /* in strict mode, number must not start with 0 */ + tok->err = json_tokener_error_parse_number; + goto out; + } + current = json_object_new_int64(num64); + } + else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) + { + current = json_object_new_double_s(numd, tok->pb->buf); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + 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; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_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 if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(POP_CHAR) */ + + out: + if (c && + (state == json_tokener_state_finish) && + (tok->depth == 0) && + (tok->flags & JSON_TOKENER_STRICT)) { + /* unexpected char after JSON data */ + tok->err = json_tokener_error_parse_unexpected; + } + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, oldlocale); + if (oldlocale) free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h new file mode 100644 index 000000000000..a72d2bdefe00 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h @@ -0,0 +1,208 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 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_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment, + json_tokener_error_size +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep, + json_tokener_state_inf +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +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); + +/** + * Set flags that control how parsing will be done. + */ +extern 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 + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error 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 + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * 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 + * 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. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * This interface is presently not 64-bit clean due to the int len argument + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @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, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c new file mode 100644 index 000000000000..fda5d1ec00a7 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c @@ -0,0 +1,301 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h new file mode 100644 index 000000000000..1005e58c5b0d --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h @@ -0,0 +1,41 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c b/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c new file mode 100644 index 000000000000..5284fd0e70b7 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c @@ -0,0 +1,26 @@ + +/* dummy source file for compatibility purposes */ + +#if defined(HAVE_CDEFS_H) +#include +#endif + +#ifndef __warn_references + +#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) + +#define __warn_references(sym,msg) \ + __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); + +#else +#define __warn_references(sym,msg) /* nothing */ +#endif + +#endif + +#include "json_object.h" + +__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); + +/* __asm__(".section .gnu.warning." __STRING(sym) \ + " ; .ascii \"" msg "\" ; .text") */ diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c new file mode 100644 index 000000000000..50de485638e7 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c @@ -0,0 +1,604 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 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 +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +#include "random_seed.h" +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +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); +} + +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 + * minor mofifications to compile with -Werror + */ + +/* +------------------------------------------------------------------------------- +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 +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +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. +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 + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +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(). + +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. +------------------------------------------------------------------------------- +*/ + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + 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 + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 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 +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + 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 + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- 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 + * rest of the string. Every machine with memory protection I've seen + * 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). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- 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 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + +unsigned long lh_char_hash(const void *k) +{ + static volatile int random_seed = -1; + + if (random_seed == -1) { + int 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 + InterlockedCompareExchange(&random_seed, seed, -1); +#else +#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); +} + +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, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + 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"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void 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; + } + 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); +} + +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) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, const void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + + 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].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + 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 && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +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; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h new file mode 100644 index 000000000000..950d09f35d70 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h @@ -0,0 +1,292 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h b/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h new file mode 100644 index 000000000000..f40b8faf8fd4 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h @@ -0,0 +1,28 @@ +#ifndef __math_compat_h +#define __math_compat_h + +/* Define isnan and isinf on Windows/MSVC */ + +#ifndef HAVE_DECL_ISNAN +# ifdef HAVE_DECL__ISNAN +#include +#define isnan(x) _isnan(x) +# endif +#endif + +#ifndef HAVE_DECL_ISINF +# ifdef HAVE_DECL__FINITE +#include +#define isinf(x) (!_finite(x)) +# endif +#endif + +#ifndef HAVE_DECL_NAN +#error This platform does not have nan() +#endif + +#ifndef HAVE_DECL_INFINITY +#error This platform does not have INFINITY +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c new file mode 100644 index 000000000000..94d41b0d6a5e --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c @@ -0,0 +1,194 @@ +/* + * $Id: printbuf.c,v 1.5 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) + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +static int printbuf_extend(struct printbuf *p, size_t min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, size_t min_size) +{ + char *t; + size_t new_size; + + if (p->size >= min_size) + return 0; + + new_size = json_max(p->size * 2, min_size + 8); +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +size_t 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) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t len) +{ + size_t size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + 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; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, 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 + if output is truncated whereas some return the number of bytes that + would have been written - this code handles both cases. */ + if(size == -1 || size > 127) { + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } + va_end(ap); + printbuf_memappend(p, t, size); + free(t); + return size; + } else { + printbuf_memappend(p, buf, size); + return size; + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h new file mode 100644 index 000000000000..ed003b61f3c2 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h @@ -0,0 +1,81 @@ +/* + * $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/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c new file mode 100644 index 000000000000..ece374e5b74e --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c @@ -0,0 +1,238 @@ +/* + * random_seed.c + * + * Copyright (c) 2013 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 "config.h" + +#define DEBUG_SEED(s) + + +#if defined ENABLE_RDRAND + +/* cpuid */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) +#define HAS_X86_CPUID 1 + +static void do_cpuid(int regs[], int h) +{ + __asm__ __volatile__( +#if defined __x86_64__ + "pushq %%rbx;\n" +#else + "pushl %%ebx;\n" +#endif + "cpuid;\n" +#if defined __x86_64__ + "popq %%rbx;\n" +#else + "popl %%ebx;\n" +#endif + : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + : "a"(h)); +} + +#elif defined _MSC_VER + +#define HAS_X86_CPUID 1 +#define do_cpuid __cpuid + +#endif + +/* has_rdrand */ + +#if HAS_X86_CPUID + +static int has_rdrand() +{ + // CPUID.01H:ECX.RDRAND[bit 30] == 1 + int regs[4]; + do_cpuid(regs, 1); + return (regs[2] & (1 << 30)) != 0; +} + +#endif + +/* get_rdrand_seed - GCC x86 and X64 */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) + +#define HAVE_RDRAND 1 + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; + // rdrand eax + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + return _eax; +} + +#endif + +#if defined _MSC_VER + +#if _MSC_VER >= 1700 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2012 and above */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int r; + while (_rdrand32_step(&r) == 0); + return r; +} + +#elif defined _M_IX86 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; +retry: + // rdrand eax + __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + __asm jnc retry + __asm mov _eax, eax + return _eax; +} + +#endif +#endif + +#endif /* defined ENABLE_RDRAND */ + + +/* has_dev_urandom */ + +#if defined (__APPLE__) || defined(__unix__) || defined(__linux__) + +#include +#include +#include +#include +#include +#include + +#define HAVE_DEV_RANDOM 1 + +static const char *dev_random_file = "/dev/urandom"; + +static int has_dev_urandom() +{ + struct stat buf; + if (stat(dev_random_file, &buf)) { + return 0; + } + return ((buf.st_mode & S_IFCHR) != 0); +} + + +/* get_dev_random_seed */ + +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); + exit(1); + } + close(fd); + return r; +} + +#endif + + +/* get_cryptgenrandom_seed */ + +#ifdef _WIN32 + +#define HAVE_CRYPTGENRANDOM 1 + +#include + +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)) + { + fprintf(stderr, "error CryptAcquireContextW"); + exit(1); + } + + if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) + { + fprintf(stderr, "error CryptGenRandom"); + exit(1); + } + + CryptReleaseContext(hProvider, 0); + + return r; +} + +#endif + + +/* get_time_seed */ + +#include + +static int get_time_seed() +{ + DEBUG_SEED("get_time_seed"); + + return (int)time(NULL) * 433494437; +} + + +/* json_c_get_random_seed */ + +int json_c_get_random_seed() +{ +#if HAVE_RDRAND + if (has_rdrand()) return get_rdrand_seed(); +#endif +#if HAVE_DEV_RANDOM + if (has_dev_urandom()) return get_dev_random_seed(); +#endif +#if HAVE_CRYPTGENRANDOM + return get_cryptgenrandom_seed(); +#endif + return get_time_seed(); +} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h new file mode 100644 index 000000000000..7362d67d9cd5 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h @@ -0,0 +1,25 @@ +/* + * random_seed.h + * + * Copyright (c) 2013 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 seed_h +#define seed_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern int json_c_get_random_seed(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 19103442a8c2..9318c1dd44a9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -170,6 +170,7 @@ INT WINAPI wWinMain( propSheetPage.pfnDlgProc = SetupPropPage3_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); +#ifdef PH_BUILD_API memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; @@ -177,6 +178,15 @@ INT WINAPI wWinMain( propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); propSheetPage.pfnDlgProc = SetupPropPage4_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); +#else + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); + propSheetPage.pfnDlgProc = SetupPropPage5_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); +#endif PhModalPropertySheet(&propSheetHeader); } diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/page2.c index 3d04a4a180d6..016a431d7e9e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page2.c +++ b/tools/CustomSetupTool/CustomSetupTool/page2.c @@ -40,21 +40,9 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( if (resourceBuffer = ExtractResourceToBuffer(MAKEINTRESOURCE(IDR_LICENCE_DATA))) { - if (eulaTextString = PhConvertMultiByteToUtf16(resourceBuffer)) + if (eulaTextString = PhConvertUtf8ToUtf16(resourceBuffer)) { - 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); } diff --git a/tools/CustomSetupTool/CustomSetupTool/page5.c b/tools/CustomSetupTool/CustomSetupTool/page5.c new file mode 100644 index 000000000000..dd6749c90bdc --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/page5.c @@ -0,0 +1,171 @@ +/* + * 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 + +ULONG64 DownloadCurrentLength = 0; +ULONG64 DownloadTotalLength = 0; + +NTSTATUS SetupDownloadProgressThread( + _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + ) +{ + if (SetupQueryUpdateData(Context)) + { + UpdateDownloadUpdateData(Context); + } + + PhDereferenceObject(Context); + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK SetupPropPage5_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ) +{ + 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); + + SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), L"Starting download..."); + SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), L"Starting download..."); + SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), L""); + + // Setup the progress thread + PPH_SETUP_UNINSTALL_CONTEXT progress = PhCreateAlloc(sizeof(PH_SETUP_UNINSTALL_CONTEXT)); + memset(progress, 0, sizeof(PH_SETUP_UNINSTALL_CONTEXT)); + + progress->DialogHandle = hwndDlg; + progress->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); + progress->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); + progress->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); + progress->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); + progress->CurrentMajorVersion = PHAPP_VERSION_MAJOR; + progress->CurrentMinorVersion = PHAPP_VERSION_MINOR; + progress->CurrentRevisionVersion = PHAPP_VERSION_REVISION; + + RtlQueueWorkItem(SetupDownloadProgressThread, progress, 0); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + LPPSHNOTIFY pageNotify = (LPPSHNOTIFY)header; + + switch (pageNotify->hdr.code) + { + case PSN_QUERYCANCEL: + { + if (SetupRunning && DialogPromptExit(hwndDlg)) + { + //PropSheet_CancelToClose(GetParent(hwndDlg)); + //EnableMenuItem(GetSystemMenu(GetParent(hwndDlg), FALSE), SC_CLOSE, MF_GRAYED); + //EnableMenuItem(GetSystemMenu(GetParent(hwndDlg), FALSE), SC_CLOSE, MF_ENABLED); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); + return TRUE; + } + } + break; + case PSN_SETACTIVE: + { + //HWND hwPropSheet; + //HANDLE threadHandle; + //PSETUP_PROGRESS_THREAD progress; + + //hwPropSheet = pageNotify->hdr.hwndFrom; + + //// Disable Next/Back buttons + //PropSheet_SetWizButtons(hwPropSheet, 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, SetupDownloadProgressThread, progress)) + // NtClose(threadHandle); + //} + } + break; + } + } + break; + case WM_START_SETUP: + { + //SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), + // PhaFormatString(L"Downloading 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); + } + break; + case WM_UPDATE_SETUP: + { + //PPH_STRING currentFile = (PPH_STRING)lParam; + // + ////SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), + //// PhaConcatStrings2(L"Extracting: ", currentFile->Buffer)->Buffer + //// ); + //SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), 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); + // + //PhDereferenceObject(currentFile); + } + break; + case WM_END_SETUP: + { + 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"); + + if (SetupStartAppAfterExit) + { + SetupExecuteProcessHacker(GetParent(hwndDlg)); + PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH); + } + } + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.h b/tools/CustomSetupTool/CustomSetupTool/resource.h index a16b725cff7e..49bdd138ee8f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.h +++ b/tools/CustomSetupTool/CustomSetupTool/resource.h @@ -3,15 +3,16 @@ // 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 IDB_PNG1 107 +#define IDR_LICENCE_DATA 108 +#define IDR_BIN_DATA 109 #define IDC_MAINHEADER 1001 +#define IDC_MAINHEADER2 1002 #define IDC_INSTALL_PROGRESS 1005 #define IDC_SUBHEADER 1047 #define IDC_EDIT1 1048 @@ -29,7 +30,6 @@ #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_SHORTCUT_CHECK 1068 #define IDC_STATIC1 1069 @@ -37,16 +37,15 @@ #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 110 #endif #endif diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 887108157590..3599828e2a54 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -33,8 +33,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 @@ -42,7 +42,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 @@ -50,31 +51,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 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,190,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_DISABLED | WS_TABSTOP,201,161,173,10 + GROUPBOX "Installation Folder",IDC_STATIC1,10,43,380,40 + CONTROL "Reset Process Hacker settings",IDC_RESET_CHECK2,"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 @@ -148,71 +149,6 @@ 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 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 - - ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -255,7 +191,7 @@ END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" - "#include ""../../../include/phappres.h""\r\n" + "#include ""../../../ProcessHacker/include/phappres.h""\r\n" "\0" END @@ -283,10 +219,13 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS // RCDATA // -IDR_BIN_DATA RCDATA "..\\..\\..\\build\\processhacker-build-bin.zip" - -IDR_LICENCE_DATA RCDATA "resources\\Licence.txt" +IDR_LICENCE_DATA RCDATA "resources\\LICENSE.txt" +#if defined(APSTUDIO_INVOKED) || defined(PH_BUILD_API) +#if defined(APSTUDIO_INVOKED) +IDR_BIN_DATA$(PH_BUILD_API) RCDATA "..\\..\\..\\build\\output\\processhacker-build-bin.zip" +#endif +#endif #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/version.rc b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc new file mode 100644 index 000000000000..8eced112f05c --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc @@ -0,0 +1,80 @@ +#include "../resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#include "../../../ProcessHacker/include/phappres.h" +#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) + +///////////////////////////////////////////////////////////////////////////// +// +// 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 + +#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 +///////////////////////////////////////////////////////////////////////////// diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 4de1817f7517..92acc4d711af 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -22,13 +22,6 @@ #include #include -typedef struct _PH_SETUP_UNINSTALL_CONTEXT -{ - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; -} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; - #define WM_TASKDIALOGINIT (WM_APP + 550) HWND UninstallDialogHandle = NULL; HANDLE UninstallDialogThreadHandle = NULL; diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 01db439aa0e1..231bd124a266 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -93,14 +93,14 @@ VOID FreeUpdateContext( ) { //PhClearReference(&Context->Version); - // PhClearReference(&Context->RevVersion); + //PhClearReference(&Context->RevVersion); //PhClearReference(&Context->RelDate); - // PhClearReference(&Context->Size); + //PhClearReference(&Context->Size); //PhClearReference(&Context->Hash); - // PhClearReference(&Context->Signature); - // PhClearReference(&Context->ReleaseNotesUrl); + //PhClearReference(&Context->Signature); + //PhClearReference(&Context->ReleaseNotesUrl); //PhClearReference(&Context->SetupFilePath); - // PhClearReference(&Context->SetupFileDownloadUrl); + //PhClearReference(&Context->SetupFileDownloadUrl); if (Context->IconLargeHandle) DestroyIcon(Context->IconLargeHandle); @@ -144,7 +144,7 @@ VOID TaskDialogLinkClicked( _In_ PPH_SETUP_UPDATE_CONTEXT Context ) { - PhShellExecute(Context->DialogHandle, L"https://www.maxmind.com/", NULL); + } LRESULT CALLBACK TaskDialogSubclassProc( From c7fc42e3041fb8eb466c2abaaaa51765c1c6369a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 12:20:01 +1000 Subject: [PATCH 140/839] Fix symbol references, Enable CFG builds for plugins (requires tools\CustomStartTool\bootstrap.exe) --- ProcessHacker/ProcessHacker.vcxproj | 4 ++-- plugins/Plugins.props | 4 +++- tools/peview/peprp.c | 2 +- tools/peview/peview.vcxproj | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 9b9b462afac4..67fcd39e46f5 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -103,7 +103,7 @@ ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - __delayLoadHelper2 + ___delayLoadHelper2@8 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -175,7 +175,7 @@ ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - __delayLoadHelper2 + ___delayLoadHelper2@8 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) diff --git a/plugins/Plugins.props b/plugins/Plugins.props index 73e6bd500644..88e4574b2c86 100644 --- a/plugins/Plugins.props +++ b/plugins/Plugins.props @@ -24,6 +24,7 @@ Level3 true StdCall + Guard false true ProgramDatabase @@ -32,7 +33,6 @@ delaylib.lib;ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) ProcessHacker.exe;%(DelayLoadDLLs) - __delayLoadHelper2 true 6.01 Windows @@ -79,6 +79,7 @@ ..\..\sdk\lib\i386;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) MachineX86 + ___delayLoadHelper2@8 @@ -87,6 +88,7 @@ ..\..\sdk\lib\amd64;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) MachineX64 + __delayLoadHelper2 diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 75ce0487a33f..b2d8d744382f 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -174,7 +174,7 @@ VOID PvPeProperties( } // Symbols page - if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &entry)) && entry->VirtualAddress) + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &entry)) && entry->VirtualAddress) { newPage = PvCreatePropPageContext( MAKEINTRESOURCE(IDD_PESYMBOLS), diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 14e547b6dcc5..ba8868d0c4f1 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -104,7 +104,7 @@ Windows MachineX86 6.01 - __delayLoadHelper2 + ___delayLoadHelper2@8 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -170,7 +170,7 @@ MachineX86 true 6.01 - __delayLoadHelper2 + ___delayLoadHelper2@8 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) From d0878a98434a72205e9d3b3471deeefc440366a6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 13:21:41 +1000 Subject: [PATCH 141/839] Fix build --- .../CustomSetupTool/download.c | 2 +- tools/delaylib/bin/Debug32/delaylib.lib | Bin 20588 -> 21434 bytes tools/delaylib/bin/Debug32/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Debug64/delaylib.lib | Bin 21430 -> 21430 bytes tools/delaylib/bin/Debug64/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Release32/delaylib.lib | Bin 226114 -> 226982 bytes tools/delaylib/bin/Release64/delaylib.lib | Bin 225886 -> 225884 bytes tools/delaylib/main.c | 40 +++++++++++++++++- 8 files changed, 39 insertions(+), 3 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 9ae9010c1566..977872b81370 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -648,7 +648,7 @@ BOOLEAN UpdateDownloadUpdateData( ULONG downloadedBytes = 0; ULONG contentLengthSize = sizeof(ULONG); ULONG contentLength = 0; - PPH_STRING status; + //PPH_STRING status; IO_STATUS_BLOCK isb; BYTE buffer[PAGE_SIZE]; diff --git a/tools/delaylib/bin/Debug32/delaylib.lib b/tools/delaylib/bin/Debug32/delaylib.lib index 418c0549ec1d5c58d058709cb9f229f93ce16b8c..ee4b17dfbb1762135f84694395e1e18ae06262fb 100644 GIT binary patch delta 3613 zcmai0dr*|u6+d@@1$GIr?1JIFEDuElmp2pL?p& z>hJ==(K>+5^Y9_T8VA{QU{0t>eTd+>dUS@GRACJ`5SpVlGytSD;&x)3jrblhZRCGP zBi^Luy#xmug9%}IEWkZGOru7~F3ch@oob;}|0m^i(pP{(C#yxl`%EXq<;~bB4HJY8 zf46USQ=hM+-rwEtA1Hzph>PXcr_$6YRNov28u%8DtL=DEorYoIYhq(StLvYfoHUWT zYp8cQt_?SfWD}7W^#Gl{&-USg@OE|?^${;KA8w79MfgNS&C{ouG{%S2EE+0xj24Ke zkL0@L8};Yq`X5QJ0+$Z%P~&>p2=B-zcc1Dxgy&#UiZ-UNRE=sB>5^y56LYgTS>l2vY+ISNNymxgXH%}zD1IUc^$QPj8IC2UOxY^sB$~Aw((}m{ZIbTJmOWMI$_>16pN zDFK@OJ$(bKt-ckV?G3bs13kXM&c0qqq6VdSHoA{#gVvaA)x2EtU|y5U;%#s`gceJy zU9foU7Fy5Z%w^#_Mq( zv1&Y`H^vx=<{L<8wl}*xi}6gHK6pu=!dNlhGvu;LG{(Qes_>ooTkH;QOE{*gG~ig4 zKG-#F4^xF1P?u<%rl$cNbq#{oZWXK+udNY_i7@6{bm10Qh>&%F6rv4y0o%-_F$PQVIddy9GiWTB;n&d85PPxmq zd(zTF9_RsUqm63Fq~5dfA8DVnSUi_*VxQr?^qDLVGmPad7Z)3oQ&Y%W_y2^)wSZ!3 z7b8cmjTz4;UZnxt^+JQwYj@Z^g4;rYjUl@^!Iv@)var8%HTGpD;{41+X2Ru}%?~}2vcM1;_xnqk?oobJA4Iijj_`6*%gnqI+ z7*kvk8DCLh%r`!5tTr06ib{-GUbC^VvIv*Vc_XliuE(uplHV2Ka8*WHxF#bFnCY%4 zqM8;;WTb%_Ld7CylaU6Tgi1utBO?tgB~&VMJ{f7Cn^2+1^)m>FPy^p3(kzkz8EIfA zT{7G)Exauw4g5jj&L~_MU3XHuNEy-XtZ;J`ZiT|FRJgzfh1{r+sBo_<+%ARNt8hVu zJEU;O74Ch7JEw3TE8K*H=F;qLE95yfK$$q>k7c9*T6g#k#K*V2Q(&769<^?Nx!Ih* zXa7zL6VzdUNfMq8OU89`_0jybYF#+`>w0=}juha|xqnk_Vz{x!N3YHGnk9kjA5epl zyIs(gGP;?+OP($mu}8VPCMV-b166nVqQmg`i{ZuN*S8ZKxjQ=AMh)k8)sAdl#Cwbk z^N#LsbQlUP9{-|$r1|#zkp)p6(F%1P<`1HKM6xilIphBsFr&BDW#F@@nviUKzVy8a zc=okWJY1U|;MRCBc;Zf^NKkxP#W$B<|6)!fO)X`$xJP+F)nK7gLT&{K3R*^}lCm25 zsZ645fHhR6iU_I92vDJhZB+7~CWClD_&{p7rEqz)DE$A(09Ayf;hGhW9|4KmM@Z`X zGeVNRvxG85dzTb0g!Xv4$Z1qGzSJ?Rh9eF9l1gq!3rA(7 zffI!IfLb^yBMqD<#GTc`MHy*eh*nJ+W3`MlFl%uw!(Am>32+qOzL;iu)vR?Y(tSu_RciFUHhbw~p7l(1T A)c^nh delta 2855 zcmZXW3v5%@8OOil*p9K&B#z^}6JHzSggouUkQb0Re&NnZjBQfVmRi4zTHcYy8Q`)8z}E)pVxzVba=YpWQ!6(PIg_#qu!3My zJ2qxG>SHwhlXig0MY6G+IssxU>PJ&!qt3O%vI<}#h;-mX87_UY10bstU(Rso7j;r- zAO4l_Pd`#r z_^F|mSU0Pqeu~MfPg?*q&})WkG%iv^ZsKWtM`M9F3!j^HzV%HStq0h8occ}FPg1{` z`fJp0q5fOyAEcf?;4v3K4P=_D@&Op0x0YuGdwgBdAT)9jb4*P-Zqjm7ULfI*iAi4= z+{%hfE}Q#&(MUiHMSbA}RFh>A_nT_iB%U!nY;(|JfWbAZ6Qctoqp^{7t5O>_tR5bM zBDl@`odrj-PB53$cH1__oOmqT#;)LJ*<%$`^t49k9vEA_cH`RQy(6iCL2@vff)esy zjhM5Jfs~OuM-!@`0HJ8FFB$0xiwR#}Q1r!vKF9`x*-a+Vzce0*#3`J?Jdclq%lyGu zGSb^4#)JOecmS+qX_SWZ+L<<&Cg5-F1^Axjb=HUv@SJt7ww_xT7FA+h zfymsrv*2rX2_Gyx%Ic-oqPLjlx&{AJ>@PIXv~VCICWBpKmoMpG3=WD=iyhTP*>}b3N;)JtV_YF-I@s(;T zUT`{fR=$V0t)fVpJtxeV1@CjYm;oPm^-HNQee=pkxIYjK5)u$-tq ztqmw-gq8Fr^Jpgcxdz75XoQ`rIH8acj;q>9RlBZgH&m@DW5!{#LPqebT3FQ*s&YFkyUNe^^c@T{8^G6KIo?*yy$tIscFoc-^U;TNl>5AVEa`z1Xp1klq~ zf@gH4*xhN#=GSB29XtOxcJt=Vw;VX!c~SENV8S;*m*8RF-8uhSxMS(n_>N#!jPE<0 ztgha1zNURTJ>D_3{9Oa&*yw7M%WLF^?;$T+{*UPf;fsq``F9~deMER5J$$WdHT3MW)7Ew(IdHeC z@k1bMFA&LV4-v`!-XSVWul<1NE*UQo@^7ymuBa1#AmXQrehAu-Iq4YULz;Pb4RCm`GmxK2Z%-J)EJoHcg*VTbCv+hQbMUUV0|{1{>!amkG``PdC{o7B?<$ETl%t=m}lN?socvuI%`Llc3RAtE3Cvd)WBU%v9>hNQFM@YLONG8}Kp<1Z9pS z3zXfAa#~Tes&Y|L_}P~IT>pt;b+Q?4X3|fUChUmyNBH|wMSl3bkO9tUkH<^ozD423 zKYEM8-_XUXLIdbR-I95%7u%_Q0M{>>XP;1V$RTS6p8^NftbkHKiYJyV!7Cs Ihq!_L51jf3ssI20 diff --git a/tools/delaylib/bin/Debug32/delaylib.pdb b/tools/delaylib/bin/Debug32/delaylib.pdb index 41880c99c59b0050976d95b7cacfc7241c617645..c3dd1f5dfc764cb77ce138247c5d5970092a7d36 100644 GIT binary patch delta 2858 zcmbVOc~F#B67S#laSe(vTyhGiL=X{Bk)VPgVpJRvSv<UiNLT(6iscwn0T>n zVn*!>-Unqgi>phmUoB;fQHep;gXl(G;~jOqMYCCtxNLvS7-MbapPj0n_xkm(`}OPB zuluXn;8C-|W2Z|-&~*2xsUV{1+xusAG&YS`_ns*+zW&oqjbRzRT~XecnSIDTs>P4U z;zKk{fZfO~@U-x%@WqJ^{#(1jG!1Qy7Sr2G*oyv1_+_gTd`|nML-kbxR550VsT@=GhOMI1TxqqH(Svp!tvJypb9uR?c$BroQf4l-8(aOV zRi|X4Wro22SJTBQe_=hl>gOokx~mhD{~L3Kr8vu0T55gC2OZ<)#EdC5&#=5OV6Sa4 zOmqJwAF``R*Y$A9CsbHxi|aEi#UsqKtVJ)Zlu`Rwat|;NDU}kLuO!5UrfgI0#Hsf4 z?ZIx2>B<@e__p^T___&3b#YLOJ?cSlwi_K59ZynxTJ^DpC{S<$>BRMvv6Ez>8+rKX zd$^Me9dYU$b?9(h48je^or`Up7lR1H2`78fZhjKs>hLM)X|Qwl#j^mcvtNA{QLX#q zLYXBcwxZ69lM`C#;e&QBIlsQQ$IoOFDr}XOqDp!$>q?H>%eT&ZNymrsE6tUbpK$*4 zT=Rw9Ao|tZatrlnA5X3U?h?RL;_&ld*iT`6$z}V#>w$bU1_oXqgOBkKPK`xA&hoNY z@K{XD8bHAPsRY5>&Y0RVP+Rh874OktdsA^U}MK?XiW z1S3TRB36=*C$QQK44|Hd_LOH=i``=XP*m2O~7=;>RRob(ZXkH2EqehixhZ7B5 ztkC{4(6L1d-?Je&YjJ;~C9KlkJ_=QSBgv8l+PV;wa9E{+ZA9ma6q?WkiW{m@_#%Z? z=YpQx23=@UDRhuR+uan-DMz^X=&LH#T8Rv;3h}GpIpvtD9ID{2%Q3{$Wgk(pT<-MC zyE6B7TUCRu(V4>DzeZAQJ392@)zP6h3Mni`2W7~7q?S~JW8U_Qxskg+go|hRd3o@K zg~gT<^W1SZbMa_PNx7wB0JZDQ>LN$gA;oxEARf}Q{H5FJ%ipoJ4q5moH`HMoZn9xD z;*7TiWE9Xi#R0m@*{jj&c3-b2Mh;wqYdFcz)}Yq)fh=|9WozZd{+lz`q0sfQtORiV zI#l=kk4*Lcn3m`;qsp0d{&JdwclYN}>rtl|RmDqS~lG ziUD$H89E*A!O@$LZ;TXPcX60>3jG{CwI!S22fV^-H)9rJ*|-I7%adQWMNGx>7h6zh zOb{KDnA+><&`IK?dI1gOhI*v9=@%d=d9xnx04Z$UDrZXN@K3NHwkIJ_grs+r@s)XR zSt{mEvQ|{SWiD}aZoRteP7LMxPcSDW#0l4%i4*6n)*P}8e?XeHZJX|EhW6QZeBcuB zp7`{>&_ba_LSxA_a50D3!W+OB#OwI>Ye?5o$Ou8VTtteaCqR;g@_>SkE8{ zu+W|}AonDdM!73=dyPW=vev!=RIpH`&GLGFm2655qqANI-sl1R47u$ zXrY@Ig&5IMY5?`5~T;vZ$@ZZZ$Y(9QE2!;mF}%m>Gk{a z)gWtc&K8HHWVAHM4o^W%JydGlLA2vOsJjG8sZuG1+nUiUEl1!vQf02m5lckW)mNcO zJt0t)>ZF()?Jo0&D*f?wqPu^ScP6>4|DxrBgS4bC@wuV14lEOXbfVrDUakX!>CM=0jH{K>CI_LE{sBW3TATGT~cNcb;zt>4T5 delta 2159 zcmZ`*X>^v=5q_R~v$G^$z9a<52MG{K!jgo|ni5TnNF;`}p;8XSR1yoO5S6eRrm&LdoPtz#CND@u4^HMKQ3uO-RaW%md0ug5^-{!Z z65oe4{FigA4;`{z=Kn&EyD7fMxQ(~u9*DF zKv|K=oeY+-PqNH3i>Ms^sZRp4==*((rt_k}a6f(g=8CN@TK>cl6c(cEK=>il)z!Gd zT&|Dy$WL)81_l;RnTQ7Wk{-o=$SxQ^rdY$c-S8WY!e|uXGCzwSF+$Rx($q&eo1W4H zGTJ%NPoIyiiaX9KJjI1>f1Z(vuVjVsd|RT{W|IaQayk%&bk47 z-ulJ6xcfjgN*KsnwsQpJ>_D}3OTf_*c+oP^Su@lh+h9%s@ot#K58*_Gm#Cj_aG=WK zi*W`8_YCeP7}O+MWN$Qht(a&Q3rz9>zx)G1-O1C55l_KtoFeL>-dMMSRqAL{);+ zL4=MVSA0w=7BPz_W$z-6Ti{H!S}BbN2-8k(ZGnqkhNbjGBHd+$Wa5Y@|G_dc$BwMx^~*) zv*QLcb_21B+sQ?ZIk$r@KOSPRdZopJOBV5phu^cvIbra+cA4w@*XJyPmCw8Zgeq^L z)a?y}%JCL2=JDZC2XAQiNnWkKd@HG_W!v77s&(KwgC}}g++1qWR`Iw=K>QqFSCx9d zN7QM1R^K98)~)8@2E#k(WV)rvNHN%)WKn&`V#8=) zXN)$C7Nz@j(ua<5GQZ-s;Q52Y9yWA^imoKPL;n98x>JCvE4h}voYegc{n`Hkar!HS cSfw1L>pC3VFIM}jweo27-^+dZYRu8>ikm~3yf_q#7#JGX zGchpC2GVDM^jsi)9Y`+#(szLL!p#S`_G$=7_Cz(kG;EJ)^KE(m^S2q(oh$e&0_idf{84Wip`A%U20D*Bt#Q*>R delta 195 zcmdnCoN?Q7#tE{lh6V-(CX+WXicPdwi{MORTn*=xLs>dZYRp^aDQpg9^5Re|WMF7m z&&0q`4y4Zj=}I7d9Z1&z={rEWcJl$Qy&3{@Bw|H;7{h8iox}t-wrt9r+-rP7p!4vH z%E^~}nO68NPc;`cQka}-5+JZ_w)O-gS?T-`0tr(P*=h?-Vuw?3qDQ diff --git a/tools/delaylib/bin/Debug64/delaylib.pdb b/tools/delaylib/bin/Debug64/delaylib.pdb index 4f5770e5944b0847f1b65f42e03d24b05b22a533..db51cbc0e1c9128f5672ec9111a56f7227dbab6f 100644 GIT binary patch delta 104 zcmZozz|pXPV*`tWL=?MXBqIX@L)|2oOCRt5_nZ{C=f=v2CWFl~4m>O@1wfhU4RaX1 dHcR=RG{6TryRg3z$N{0d{IH2@_+BEkRw diff --git a/tools/delaylib/bin/Release32/delaylib.lib b/tools/delaylib/bin/Release32/delaylib.lib index 7e4fb20980bc276c072964297217d7e476865aac..c068ed7e2912139ff721034307bd0ed3db79f851 100644 GIT binary patch delta 6370 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 delta 2840 zcmY+G4^-4u7RP`0HwZX@{1XHb7(jk7$S4ki1Bfz^(@sjb4d$_eW9p`^t03kdN;eV@ z!HRd+|(rj!JuoDV_uso}t_hJ2W>exyL4{Jywug@Wn+?=?RmAvrl& zr!yLp5|WcxXpL479z6nKec20d=vJG4Y4Cif?Qhv3Kr8<6uf z7vQKI>=NU)u3ZrP4~p_lO`x3T;te|+wzymc*xRtc^_;t3#g(CA`Bt`gI{UrfEetT~ z{QTv`-tqEm?^8Z+2IWm(4o~l`$PDzU-+yjX!)@>YejFU~z1a)lIXN;q8FtI7qcfpHelt1?o|Su|v&Ek)<$2Olh?aLqR#+{6 zCM|+~`JrTnQh9MqDkT)hWWpBtVvGgW%9_|E&?INYra`s*N~{T%%IE2`xBQ>jg-|cY z>z6>C{0Dt9ydc-?J21=v zA+Qru9S{b)F^@Tm@f8PH;$IU4;z~+sBCQf;+N23ewiw1}G>p)|>y(t^0|zY7RuGaa z=7Rn{*4}`WnhtQouN)A7`)@%YRO5+TupbVit_iGIaEttZY$E@0Jk|sY;C1{b^KWW` zfEn(Utcoj*1{WICDyMMCZP>4^byk^*y={YPb!_oo6S%N^_bB@U>$*YvV{cB<3}w}sI@yQMDPj~Eo=>U2>2XN+@ZwF=-JBF zaEB@wl?n;0!raLU|G-yUN$HwXDUmC!Y$c@?ZKU*X+|Wi!4s2~BrL;EkC%2Qo6|>vP z--egk$sf^96|$8I%UA{LWQ86~?4Sz0&I(WR3T_>wBzBV05C(TbBn;!CPRN3LSkej8 zVFZtLLNbiuzdGr_$8n?+tnvP!oU}Jh?4+4#lh!L$*RrZ>CaVVEnr|Q-g7M@xV2ugm zi6LqtGi}mlC2`Yajt-}F(G4*hGrA~8&vPU-hnY5Mo03y7nKK8^QjT^WMU`(~ zeo%JiNj|N>t342w@)TM61JtYXK$$A(6YZK!&CI3QUO~PxvmXP5aMM9fti;e>2zFn^ z#h%5*y|60&IW;YhW<}4u&rg?H&NK4Knd_oI#H}@Wxfg<_KhK<+RMvoAt-ThDMZ#aUSJEzEpo3kCGY$j=v0!{xWCX$3UfW%4-}5Me@2 z6jDT4lG5W5a_ZGc5k;utPVD;@Rz1HWtUDtI5i9Dzi5fIp0Y-u*kS@;&NDp;G(-uyGV(eSRcw zu12qB{scHN3cqs0uODCjCh27Q=sOG<;8kdQ?C$J9E;GCYELHD0xJ$aFcjY z*>e$J9iz>2!9S0|Vp@)782$@vJPg~gyh@xCY Date: Sun, 21 May 2017 14:04:43 +1000 Subject: [PATCH 142/839] BuildTools: Fix git dependency, Fix delaylib pdb --- tools/CustomBuildTool/Source Files/Build.cs | 42 +++++++++--------- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes tools/delaylib/bin/Debug32/delaylib.lib | Bin 21434 -> 21434 bytes tools/delaylib/bin/Debug32/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Debug64/delaylib.lib | Bin 21430 -> 21430 bytes tools/delaylib/bin/Debug64/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Release32/delaylib.lib | Bin 226982 -> 226982 bytes tools/delaylib/bin/Release32/delaylib.pdb | Bin 0 -> 126976 bytes tools/delaylib/bin/Release64/delaylib.lib | Bin 225884 -> 225884 bytes tools/delaylib/bin/Release64/delaylib.pdb | Bin 0 -> 126976 bytes tools/delaylib/delaylib.vcxproj | 14 +++--- 12 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 tools/delaylib/bin/Release32/delaylib.pdb create mode 100644 tools/delaylib/bin/Release64/delaylib.pdb diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 8e010b9ace8b..2031e4364c1a 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -33,6 +33,7 @@ namespace CustomBuildTool public static class Build { private static DateTime TimeStart; + private static bool GitExportBuild; private static bool BuildNightly; private static string BuildBranch; private static string BuildOutputFolder; @@ -187,9 +188,13 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) return false; } - if (CheckDependencies) + if (File.Exists(GitExePath)) { - 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("Git not installed... Exiting.", ConsoleColor.Red); return false; @@ -237,16 +242,20 @@ public static void CleanupBuildEnvironment() public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) { - BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); - BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD").Trim(); - Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildBranch, ConsoleColor.White); - Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildCommit, ConsoleColor.White); + if (!GitExportBuild) + { + BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short 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(); + Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(BuildBranch, ConsoleColor.White); + Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); + Program.PrintColorMessage(BuildCommit, ConsoleColor.White); + + 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"; @@ -255,8 +264,8 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo BuildVersion = "3.0." + BuildRevision; BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; - - if (ShowBuildInfo) + + if (ShowBuildInfo && !GitExportBuild) { Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildVersion + Environment.NewLine, ConsoleColor.White); @@ -525,16 +534,9 @@ public static bool CopyKProcessHacker(bool DebugBuild) Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); if (!File.Exists(CustomSignToolPath)) - { - Program.PrintColorMessage("[SKIPPED] CustomSignTool not found.", ConsoleColor.Yellow); return true; - } - if (!File.Exists("build\\kph.key")) - { - Program.PrintColorMessage("[SKIPPED] kph.key not found.", ConsoleColor.Yellow); return true; - } if (DebugBuild) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index fc9e59071d2563b2d76ea3a8c6eb0350d3a977b5..4f1449cb3744c750cdcb24fc9aa41e20f818a47e 100644 GIT binary patch delta 16949 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 C>HGWu delta 17107 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 X-uK~yz7Mr;HtNp|crY+lKc@XJpGXKi diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 8a6ef390251d29c386caf969281fd9efd5b8d05d..790a3d5de5df704d99bfcbce5cb91749d835306a 100644 GIT binary patch delta 11582 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 Cf!-hh delta 11758 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 diff --git a/tools/delaylib/bin/Debug32/delaylib.lib b/tools/delaylib/bin/Debug32/delaylib.lib index ee4b17dfbb1762135f84694395e1e18ae06262fb..70b36d7a10a74d8054b3fb190f8a56a266db2252 100644 GIT binary patch delta 98 zcmdnBoN?E3#tCvPmc~Y=lNA`nCt5y+vA@9>Vv`M+k|3OEOlnNFf}8g-x!Q`lJUn*E nvs%Y%b^#y%p8Cx!j0_A6E|U$dq$XcrVv`M+k|3OEOlnLC?3?#7x!Q^*tkQG- jzj>F_ycS;r-KDD+F)}bPxJ)**lA3&hReH0pL#{9YMAID^ diff --git a/tools/delaylib/bin/Debug32/delaylib.pdb b/tools/delaylib/bin/Debug32/delaylib.pdb index c3dd1f5dfc764cb77ce138247c5d5970092a7d36..71941c98c2d36c2f5796a7d8690334a12b623f7a 100644 GIT binary patch delta 92 zcmZozz|pXPV*`tWgsq@rBqIX@gUiEXmprR=yk-~h@$aeM%(7X=;RQcV*=Cu5?J@z3 adI`))nR(lNH!$88WvZICUGEsLoDeCuME--N1NXlxa=ecD-YamplQ>@gZmc diff --git a/tools/delaylib/bin/Debug64/delaylib.lib b/tools/delaylib/bin/Debug64/delaylib.lib index ece9f7a57318b6a2a5010aa7eafd93ca6e426c37..7ebf690d5d9f7770babd812de496725c92cc06fc 100644 GIT binary patch delta 88 zcmdnCoN?Q7#tCvPmd54=lQ%GmO|)1CXHQ~W17lY}7&=U9Oj&}PLzt?q1wz@ae@nh% c_t||(HYI;mHpk@qwtCD-nR%N9?3RfF0MAw&cK`qY delta 88 zcmdnCoN?Q7#tCvPX6BZrlQ%GmO|)1CXHQ~W17lY}7&=U9OwsI{Lzt?q1?ncbT>5zb czvraDJvUZHG#O03Z>u-?48eKD#f;rsS{6=GZLb@PZ$wY_m+jc9{Ui aZ}H4YnR(kqH!vEDF=-oZ_d3S7%@Y7dkRAH~ delta 92 zcmZozz|pXPV*`tWL=?MXBqIX@L)|2oOCRt5_nZ{C=f=v2CWFl~4lnp|$~MacY?ldO b{1(rgpOm#-bOWQY7}ME<+r5r4Zu0~HcPS*s diff --git a/tools/delaylib/bin/Release32/delaylib.lib b/tools/delaylib/bin/Release32/delaylib.lib index c068ed7e2912139ff721034307bd0ed3db79f851..d84aba5d43a4a3e4b815e2850b5b964a6adf80c3 100644 GIT binary patch delta 145 zcmZ4XmUr1(-U)Inmd3_rlm9V^ZM2ABgmX5pVhn)usvyEO%vy3Df{KwA|2Fe1y}8C delta 141 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 z�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 z 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 Nu}N$<$Y$1I2LO22BM<-p delta 124 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%0Dx8nS+1+e&DzB0mDCopW__l*xTyhYQV>_+PzrY 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 z z=D$6dVp2ykojyyLjt06Ks~R0ge1bQHnF{IfIqeP zKw5j2qaQfgw5*HPvPL=hqR7Ez((iaC6{p`dhxJTW>d1K5xzHcY{Fc0DT!-2K7jM~o zE$lMIg_(gRt+bDS$>9g=<8?%t>eoeUS~u>!U!}$k7kK<2}O#fV3e{%exF5~#~HPkts7)E6i!^yoZ>%e`=#qDSZaw>mok15TPHP7t*Inzd=#gx{;Y^QJ5v{N&L z(n`&eb$OTVUDkTG^uS#LdslODYYYSI_HK*Ou`tL~XRCQ=vq`;nH2zzd)~?`Ni}rRM zZ`K5)%R^lsaOz6?rHl=Y%M6QUV;In7_abWN5W)PRO??ODQfE9UZT+cvR+i1;)E^;uRx_F*zAp4v{pF3sPd`a%Pt$R_E=oc;}!r%$*TU>!A|gpPS5b za%BC_*>+qk=?%<58t-AY+#NYs^#-!jnTcIyns4Yj+%MN!lFYpY*5?RG3%tA0YGUfT z#OCugpow29@qvlPl(S6YLs~`neWaveIfCt-kVNV_93|QI@pwo`F<)w552Vn-& z@yCSYM`o)g{#c3E^CPa}c|u<3I8M^)Oed0lyrhrK)~{pX3scLg7^EQ?Q#ssb)aMJx z%M&C$EH|(m3GU@Kf2j&(MbiW_yR{X#69q0+ohw01jKY$%lO(Ml9_Px%Y51hVQGYC` zWyz-bauZU8`C&j?*PwOg-FWG9;4_>33%c4E#0HxniXU%aT;KFo#ihyR}E2DKIqC3>Uhy zSe8%Gdm9e9>$;yM=?$07!o!u8mhtIro3iC(wgjuXalfT$JzLUa6<7$dA0}MXX{$Kq z2P`m4PT=L3zgoup??R`iNBfiUSa0o;vnCdU1@hW^ha4}yhrG?!oZpu*;|DTk3^g8G zO&QHrHO*2%3df@#32vSdZLlBSGmyZMg0ywa;RlgBxQ74~_|&gA->T=m=;7@CRx_gh zdj5+{9jXW3Y76w@spB|ycQ#pr(&j5>Y7NUW&#YOOl0Te>{lpMNqPV7P#sM?BH%mng zo6Kb-N8{;02x(&w&ATS^k$(zqcRu3EmpdQ%Ie5hO{{_CaJ!65Hk3Pe~dp zE~Edw8d$vviq&{FQf0c>r#80-vR4hdi|lq5ALwK=^(Jvtm%pWVj9sCDk4jnZWhwqxt!00x4h z9UrfDiy}V`tA|-r3v+lq%sI6%N7TcFb^%$|l6shPYw;|thdHknW?4PV(`sRktcO`w z3v*OG%=xu2N7uuwUgZKw`ybN)F2rnsJGKE_h~WZvTm!fe(*^GM25=$93tVRaHt`P9>6#4Cu^h+oEX3wBTy=rw{xA7f;7fz-QlvDGIVkPnpmHJNkV9z z_z3bhnWg zbUYOT8p5LFCTAQ`KV7?cQmna7gr?Wt#d{1is~S5!957)1m?*MEsSn6k5AFH5h9$gG zS2(Tu|0Q3W>}}LG&_H4k*URf{g!=^Z+wS+$cJpOFgLZ0#J)^Qr+N@<-D?{i#Ys==o zQW<@qQTlRVz@2nXd(3eX@RIpB-(RNWsfJ)5-aj8|)&t^Cu|YcV;(RBji2fS~hAapF zDt|p;))>sgvjJu;Vfra)Vf;lZH8GQ&ta6QS_ZG=_`kefu)8<|EZT=7OeQIC*#NW-> zXEDtoyUcp;32EQkWY2=_Epo4U=Z;C_3;RHq(+6z3mF#}4*_f~6`%dc4CTY7NE17K~ zPzDBK6Zl2HR;T;JgH5cge z;UUp2K@kmsfZgoz{#DvwxwOG@Hh~JSc&z-99@^;t?$jH5Df=j@w#v=v`C6M@_i2MK zyRV*u!^X^`j(Wat?ar_-GY*|+JC4)vl?S_a)A8E9gq=6d^I>N*z|yI-Uf01c?Bs)wg)BB4CbGJYunX%n9Q)}m z?o7ZSXU-(S9?bYybuwok4AHn?4`%#uIX9gv2+gnoGcImltI$jk+F<`>8jcjfeZKx_ zY+unW^mvEUS{>jN7w)4L1B1m3pMRtjC_S=u(sq+rf{3Fh2qL0@t&mY2EHqr)5G zp}p<2dnBkyDpWP-cI~tg;A%Gi_K?PTZxr9OrH#uzHTLdET&NF=;?wktjt!;vRn40y z71Rwx#B;T0yM zdm?^3c8{_owx1AWviKXwdZ~qhkKvZ$_#5@jE&ZoZ_c*_FLAkN zTn+|pGG_R1oYCpoF?dJL*@5=;hn=6p7rZ$*jKi6D=gs#ax*U|9WYc^%M!|f4h0-d{ z-&eEvcy?Q+GNClq3u}7Z@}A0NCs8Btmh#7YUWbBC`&nn@H;^>9g9Nc)WRvx&Nb2xd zvPa&XbYqZ@a(fucq+X%lf)Wbnx~N_8{=GSKc7&$MOr5A%NiXYY+Q-CODZ}*6zqh}{ zsSD0RxFv_`p{WBcJd4y|kI3X3NDOVj**YC?bGz}tVb7ybmny6YgDH%;vrQdwq(c|( zaXlsc)I;ps;%BJN&c^Y9Ww4JNjdF||E_cNo8XvSB(_U2@$ z4LRpH3;68^ZP<(&qXn9!g4QPeKytum8gBDDZ4ts(mnwBXTk3AlCjMAgKGZo&Au9(XFLad;v_*&9Gu1ob&RIzXp|Z~xfb}?H#ml! zi0@!edY(ELX_Vjd@Xfx_xH!*m664$gARb#3F$IpQc=pMMRhwoq^4>DUIT^=;Az zT+P~KJ<`}F{rKL?IAhAHW4BD;G$D0~ep_53xNTdQy@F(R7KhNVme*5$JD$LnN8W_S z5)XQK066T2bI`^`?wg(_^K1AaNJ4qV_|lv~;>MsRP1cowl1I?fBe6u_Sj>&oe7I8N za9G!4q{zc-Jg*qY=0FyYWcuMYtf5NKLriBL_$>18icn_ZlQcJpC#7uuMXEoG8)W)C zBkKrN<&A`S=GLfT9UH&jQprq{H9CjfKDZwAaGZ7}uTLtzl!m|^!An(|c&6s%(H_bGaDsK@WqjBituJ9u}( zPe8k~{XCR)g;N%EtZlId?dR$CW81qtA4h(d=MxB%=acv*Y?HY`Vvx5JdgBAJEd#@` zUi?b+Boib4gM`h~QD@?x68sC5x!ZL-{4Ebj`x;)r`0Y#tDEqv>rBrfz2AUsM3Ej76 zfCq7MG|9=Jd1QPA<^a>c$6cNOxyPmaD>cqAE|zcF0uP6C@t@onv+IAxx6_VMwU?in z7|d-J(cZ$Nu;wbDKGaVLixd85bSH|cke3mrp-bnM##Nky5IC za>)A4Ag>!fT*92gFnxDTctym(cOs4Hw0T|7czjsjn@EqwyKw?zw5Q)xB@_ip9@k|P z5C0w$+_vog1*R;U{;<$;mB26bOuB`KV%PX=7%CtX-i&aQJG|@tcMkarlr@`!n7c{C z$u#_sb^G2;Yhs({CqH6XxNVH5`;sv`cGkusKlR=VM5d|tIEHk9U-owdI?(^^IlrUM z?>6W6Jm>d(=l25Vcf0c&_h)_l3!UEwI=>e=zYlVLAME@-a?j^2l?uB4rytL8j9^Nh&(l4NPZI1G^f%CyaLZvbz%XYwiR zQqon4pk<%ted{aiISHreZmjUP37+NP^^AV6L3;r*4`VpGGjIHn4Nqw^jlGd5i+1ca zvK-PS**m9sp?S-fs z|1Orcoq)eSVji})xmHV^b|LK!PYw&!To_?loVVWP`Q9M3^E1oCdpv!XNN+I8H6jc~ z6WiQ2rGB{2t09cGGdiA0m9(r8=D}t#_PLIc2_N>MeZdM|^73JCbZ6h3P$zZnMEs-8 zaENaPyJvGX`16>zeln-8WzZ><8Gk7M4=zaJ0$gA#vwV4Y0pNRZHO9Z0E?vQq)5kQ7 zmcJ3)7SZiWdVe3pkJ@<3>l2JO5~-zyW-Qyj{|XvayOvoYhKYvGXOaE`rt_#)i9W6L zL_*S@jkI-#PUmZ7g5`BC4&r%U*Vb(X;vEy1uPass@qXMo#JS}WZx`Z3>XSVV-hRf* zRWvJiBa8K|JMELfbaX=p-{sxBm`mf_1RN#6y)e3oGlzH%>NOKimq$#4dh9&B7i|Z{ zqNP`ht)~0mkazdtxSp8G7vY6X*PCy@?_Pp9ZofD>X)3dwdcYi@q|o~NjnsQ37dN$QPjNe*_4ePcCyt#H_fp6uwv98m28h!=T4=32T)Whj@otW|e%Zu({ZbQW4kH@5V4ZGUI#|uE$JNQQ z?i_wQzG-P<&e?JH=j_CF)6yZ_E~d^i?ZLQPmX`2P`m83IG;yAE`_lewrX!t>Le3*Q67-sEU ztmmUkwU-f$D&~kUab5%3A6=^74zh{b$A{(JZy-xpS=ubRzI?-o@l$XDS}knj$g11x zMZoo&r3oCU8m~@6`rzhE!(Xf8akQ;3H=ZqPX6X1A>-f~RT*=59R?TQkljkKm4L3m( z1+-@gcfW8#PtOEQU0btLIIb{5F&K6{fsO-k+8jYOO8ddYw)xa+$moN*=z_l!@}VIl-t)g#*?U{!*46TROV4keVo#=nESs z*JKu?+l%Q>1nKa7;wSt0)iRskj+B9{wJ)RM3G_1OZHt!ShBw4zcxq~n_kUS0mea8e z`c@wtO=c@b8T)C=n5Ks)=)p~sQ??zct2*7Z9vspXJ;X6-#j^O+6nD|+R4%wyt=DvA ztfy}PjQy-z#c^tS!=d;@ct`5OYzM$){V$Ry8(Z5ZD4-STigklh^(vhZ&_FA z-?FaKzhzyef6KZ`|Hitinf)7Uz=7VRwrdyy;X%re0i~qnZ9*6KN1%Z{75eCR-nCO4 z>nvj=#^oe@t;=yMKK$MUK=XoSyhF<9YhDHnbvVKm8?c*YhF&7CO%b}^<@3|n>W%~- zT>Ap;1#bA-n(%Ifx$az!`W_3O6PEHs*-&k2&l?UjYspTA1AkUxq%r3gDT7RhMG{^u zd?%6go;j>t$;$kSFnCQSi;-1UH|D%M{tEt2LQqP6|#tu|kcU#kJVYhdbflfVqXs}!i1(P~WQd=?Me1a?e) z&cltPeG>H0>S}M7YyP|)IG3q`N%|IHJ>Q9Nr(K7SVAcdbi1a(mG~!;NFwZp0Nq=l- zyzf_&W4GYC8MxrP3ob<9N3CXV)TLq-=w}RDDc)=zsmS`4xcXJ1FFw2>OJA5Qmohg3 zUs3SPCAAI4P1=)v-+GAxEmjeS!JuV{=H@81I+z=nJeS4Tds9 z3;lqF)A~kj8vTL!X_y%3rzZ2hcR~LQ9{q#)a58;%!C2>agLfn7Cq^H0ELr8Z!DjJ= zWk-qHb=GEWi!kq4a;-?_66a$m_X-uZ1y~s&k0xJP$ewU%$Tz8?!2IJTDs=rv# zt6BFjhbfP9;u`}TdX1Rkc;?J?NYhO!h8Z&_G1L^nJcwzxsNpQUYCB0mU2dljzX{IM zL6T;Ake9r;Lz!dm!&4%rMRXiz-G8SVs;ti!$Kk?1k0qLxZSDi#1=)sxPQWl`&Z5w~ zU&JFvl9xL69_(#qN~!EL?QtR&Zu)(0y75h_`OUl!>a@dl?zHFn%tbTFg$@?c1)|jU0 z&9vSKw$8_N6%oE-PFRLk>d)qJ1lI78<+ewVzqiNV^wBO8goo5c9T8`l=CsSQ$Z%>f zb7ip%2WgxhTD}tJF-*%9>e4um??hU66|PO?wgcsu@yik4lj^nDd>wWY;zw{Me+s_U z?Ac$Iu_B^%v+tJ0E&t4#v}Sa!s)cJnGuwAvByNP?1Mp^ud^w;4>O({+lj(t7Jk6VL*jVM)82z0MHw3ZRkS3FoIt-4XM41yI||7~@7HhbusqVy5v4H;!?=+QO$X-&9Z@P7NptR5 zF(2m$9Z~9D=-nC+rgM(a5go{3?!p3VAWU7}OT7}+>vl=k?D5-XSrmH>W)R|}x8dMs zdDa;nEtSlIv&3f6QzmIgw%3Z9bSvMtf`)Zb+M9oBgLl$gbAJ)ha7J>Ti|NGPU@>!(P^2Z6($ zKlza0Bkm8&?+=E~4@{S-%!ep_Efq{06E2lDXjM^qQwrtU|4@9{vK(glrFPY{;c5SnM}$!$41QVeS6dZ*7cs! zExCu~dI)|VdF%&wo*H2{Yu!dEx?Cf}l-=M=Ohnx+_57mH)L4eK0JFF2@c_4e#zHZ` zzmI$z05pAmKE@#5t3q-;F>d9La^J2>xk)(-LmB8$rP7p~i=!MYQ*&42#EPEJeHrPU zYBF!vSghwNH3PxNLPBKs%ZOX2fPztuoOeNPxvQdgPaV0v7dY*wU-^_b<$*nWG>v>5 zUjljZ)K?MrDXA;RCZ6ko+QU3ewhH;^fY8KroZ*B1&DH>zKFIVAHH<4$<)-P&G=8Yw z!W=BQfenm#I}Pq|qEq$?gS#%BZH(^Wecj>9D($Iu@BT5?NWIzDRM_9QZaY1ONxZd( z^1K=4{u@qNIB(`Vk2AvV2O&wkOXHsB-K=rb=GUgS`0pQn%fW{;Ec<;%v&lV!+LI9B zr{0>Sf?DdL2S5vTne|D#oa}LYded$XpBS=WHhT8kh=b2N*%dT*%A(-Ycbt0Rtaxq7 zhF!I>pyN95%-nH-JAkzDEJKej$=GOIS0*_;fJek)-8lP7IQVO84u{)nL%p$q;h}hX z7xKC$c|I4)+xsAgp50E>@XkH(!Yc3O78T4KsuXxGw4VSqrhhj!pwojV(^6UjJv|vf*GqPUpBr3TKxxj1fJ#YcF%%IJb801=_zvq z+>Aktdzl}%MavQ6t~ckhHru)|?lmTEiyL>NiQ|psxy?>FZ*teI-R+a=Fv4)4H78H~RR?LD^*XZiJPDB$6Jzy*KWA-lmt z)F%~lG&dtS{tNB%SZ({zsfHtCk42pWZ*n)v%FplJG;2|EWa5da>@@Ud=?@uxRd(q` zv(2yCM5|~O9Gn?p8??2_!d823p3`Fi`n~sar(d_X{o{LDm%%?4^&<35A~k~J<~_qh zeTntyR8MROSDxeP;Ur$frc<@Cq0~scC()OP_kw@S6WM1T?6j?if1wnVgyiw|nTJ`{J?Dv83E=3CAHX_aq-hd0OYo znPbYi4&w~-DWP{~#2D0<)e|9WV<0TvA1UySrpb-kg9>za7=}1IoV!R29}XR*eYiS$ zp7y816}L8Lac^mohk-P&Xm=t!44L{2i#1EJ&;ABz1u8l>0+Kcvp2HK0*ww#lg_Az_aHq5i}1!-HxDV0rQ zvokk=eB=$|Jj6|mW7#bAW?UQN@J6SFna^-JKjFf(YnV)V20Nq{ZXx1kc^@H<#lC}Y z5#uvE>*5ba{G99$v)m4bopKi=JTX_*cfo8Kha)cMz%0=)0j}<~3$w<#B633IHEdTe&x5DJ}_y5?xFG3v3EM#|3Kg>G< zVBR0_&wjeBy=Fh!w#l6&PU&h-FyLZR_t<(PY0cBMym%dd0zF)7zC`rb6Pi9fXYJZC)i&T;l8g?pU(uCeBy?u&S*DOWIP z*q;FE{kOxb6-tk*wRLS?OV&?he7R9AiG%>HXFgy~a+fxl09ladvdHt#fsB@JEr{qS9Ud9XMQfwcOT zboXN}Rjpvc25f$^!}}3G-7JE*cgFFBz9#?T@N_fm9L|Xy!%H^?JbaG7HXOloi)C&T z!SFDLO;xiVizq)HPC;yvog?^Zi2_wO^tl=L{dieeSbKmUFYhQS=+*^C%mdwA_x}yF zo5Mi*>Iq*!8o^8^K4kpcU;(F33e_c|8n7QUTwBFA_ryoE|AAzDJ={DQEzNtqm}41+ z>=gv4c}K+ScoMW(`*THpe^mMu1}}X419-mNh^+A%fZnvX@`tgKaUs8#rtkP?NqF@{ z>oLZ6VqR|#C=$nVl-8;22?nmTX3cBEj=dogw1tI9;a=?o*L-Gf>*E;0Ljl=IKWOFDkLW4l zE-d`+j}>-c35EQWSFZIdsQm)eBOQnjXOY7xoI^9y0ElbDG*kz?XfhWFax@cB3NBDk zCJ#XQl*uOdo-r{Qg}+k6Q3+^XGVg(s*Y2S#M3^$PNWN*qZpUwG(Sz{KJO|^O<((3e z`ysxVqh;(6q+`yDxg8o0aZw%|uKWXia!6?4cfa&BtFH({M|4gAEC=Y&^kWOaWLi=>dw7+F&r*P%wvsg1*X>}zY|I@QRzwQ+dw z@k&Bfv=WFuu+xmoiy@nGs0_}^eYfP#|h6Me4Z07A^bEa zyi+AJd~3tv->s4pZW!mxCf)G$Dmm_kafS@j^C16CNO!}xAU)@#-wu3k_zsm^=eGA< zDtW#ezDFfjyY;^hVYmJdBJ9@xA%xxfKaB8Nr~JndKGz99p^{iS82Xp6pGEre6}|d2 zam#d^IYnXVIudc65%WHpjiXH86xlpGEBA63zMWyYkHhd?49h(nhVNte9HSo@eu&|< zLHIF-&ke%LL-@QP+`;hEf^a9p>w@qqhR+YeT?~VpY-^S`qQl0f&F~dEY;4*Lmvs0X zAAYwEul2*%>+rccd=%hSbfY$@`nND0IligQq{+%(=b`-G(x%rNHrkiN+uOMN+Kv4J z+{G&fZNxlxIB9(6j;U6D@6u`h7;UmTblek8x7oFSi7x2cqgfC6+}9RXt?oE!_vww^ z;;W!WUO2@zwjDE<@__SL2PgD*L%nJJqf~U+Q^xl}w|)*+*NuDI?bzF)+avjt`aT?^83oR@tGLt}b=AKZlyk=d`Jspu7SSyi12uulXEi`aH~0w&&WM^oUKz zQh?S{oCe~2Q}zfYFVfa$b!Pu;wOJP+y)To?OGfr9#={qgiI+?^ex5B0FEKfk>b=4R z;5qqt0(fu{cC9d;PM(!5RAkyH7B%Xiu;n!Uhy3huRx4-0oN0Vdt zZ{~%Li{8CKphn>F>DcRGxe?VfJZS7ndJi?7z?SX67NNH$?VL5;iP2>I(y>GmR$*zU z8kodDdLYrAj3o^Yyqy}xkM-8@(0ZPYNhQ_~`8Mp1S~&Wa#q5aI1LQl4Xu}c_&u5Oa1^UMQY zCr@H%IH}`VS6q#OBjZtcE@c(5BR8~OpM57jlfF@J6R(ergdj{?NTul%6SQnj_jFsi ze^TR44B(zj|8TFq(vnUM#s&s79IKs5Tqb+$c(+))H6b;FiT{C9Z*w-RnT z#CkNA7)r-RMtX+_@yqgpvf|T#3qIh%FnR&^M>blygl)#>0Sw}o;MG3*W6?|^4^*4x z??5b-G6%Ip@0j=z$P*p^=p$U(5Auzoj~ASW7>c7WH#lq|w1v)F)WNbo-8gfxDbWjwV;%7Z-d=F`rg}zj zWMRE&C%j#^7g+aThB=pE*Sn{JAA8H$_+UmI@fwV}cBSYg+D>3-^zBE`tvF}Ty9id^ z_&cG`p|h@0*rbYi*zb$x#=4ai?AK>;;IhshFb{Wh^y{cNw!r-J#W%%ORVZ+b>5N-| zvrz#RCXQ#Zd`hHUa6X|d)aZN*84gpU;}1l94K+G`5#j@COgRT3js}}#)=)%x4n~}I zuj{;Xdt&>1tf#I|6aIc?28DcHbtr1aZzu5DmAKxEh?-ScdrUqkabOk&NfgK((Tc zVYUsALcDp|-oPD=a8hn)T9{)HH&UIOp{3cz9g8^J^1_j8!<*v}zO;%v?$ue$9t^JI z5odir;4ukZXP$|}bR|1mf*nZmo`AS5xojSGAuX1?t4w|`M?7zDx#gUQINqj*jmSic zoScMs?0(aTtIIhV;TX&!m2TdD!jT&oZHe;~#M{#`LWhn&)t+Qr& zu7tFmiL$Y4S4$f=R5g8sS&2P`OgX!Wrd;ly(Efb}_+me+8t*CW_x{PBg?B&3yj;Qe zBJK&XoBDgajORNuh&LjuQ%s$h#y7lkb8w)LE8?jYH+KL7U-n@b2NQX1_^ETVtM+B8 zd0aQ=q0xYMCm9-~K`<^s25ny0gtcKlNDR{WHU_8)x^e)t=SuLCHsZ6w{Dk_Kvx3I8 zXRP6yYI^li_s&pb*e=?hjD3jbOJE`KZE~ywZIT^gQ)Inpr}9A8*$!Q>)49i>{4-Rp z9SRdkZI|jq30%)z0Xx-|p*>Prp5nfNyB7%G$~aC1lkHYWoOMDz=y-yr?L(R8;F?@^(r}YIWsjs$$pnn!K&_u@hk}qSc(GE8$*c^fs zUEUhT`+k|$>T}}g65csZyD4wqcFC|EHC$KH3xCzx2P*d#Jm5dq!H;vk;WH6NF2iwM z=9~m&f`2<97ymBuNc_&e4sSu~_Aur8_J@!?k9Ksl?aqm;zQJu)chc@gmi;t;ZEXAM zGC2q8Tr<|Zd>U{b7e<_4yZiYWQRDmHz6e+Oi1et+pt2d@iakNBDdH7E#Z;IimjhzNmV^ zNm13+7FFNb8C3@zh+AB5^3-5!Lk;l#9vs+naqJG>a~yHPW?+F>W4{BeK_N(XS@t$k9+FhKj^9N zeimmnuYsS?4@cAw{-)I5zY97E|D~w9WphMrMcwnSjHqw^QmMi@p1SKZQFUAoPK(^@ zsnbER=n(s!`1LsB51}~~^cwtn1@lo6*euGjcZ}!yFeuA4- z-BHzZl&3B{6}UeVQPDqp>YK@k`r6;4Dt>W9eevs_`rW}w{UjAp%l_o4oga;;TYje0 zONMYq`3bb`9XKf)OoNIZ~Q?E#P>i)-~ zYGMZMxhz_u|+pgGct>RQzENAJVmkE80B_ea(H?#3Dvap?~#b=sTZKOpO= zJ5GwIH@_^ZX75w#c^`oP&M!pON7s4k)eEBPflorFo(p05t5UCji&7uE0rGK|r%K@C z4ZCpX;NXb*?juo^`#?mU37#D>5>YQ)09tMX@7@F6LY^i+;iZ#8@SE+p7Qy1MHRkzM5_4xXTy7Bd%`skezRp@~XzbC4$ zhYWmX5WIS8L>=)A$l(yI?#H2x<59KgbMQIzZKV!;zfxaC9rkREs$-UVY9;vmqpMIK z&^myAcG&yj!~gl%TU+L-=l#l4Z~K5!KmB7=ZT)IQeHgOx%AbI)S3p+JMO*Gr>THzP zdq7m(b{Eb9Z1vP{9#ra`sNbLGJk`I%Q@?=he*toMK{TR%_BEw`warrxeLSLG-;Z{> zK&jmWp8DZdr4~QmQ&*txeg#IFFTT!GU;0o)rJtwNFD9bu_7_Lhuh19UUJ+5lw?@=| z+!Iyh%V7X{JNgIG4n!Y6=Ss-HqNrN^n~3`8E0y{t+U$VKpg(3kbz9X_g(&*Sah^K# zTF6K{^ubG`>cvA5Re@Yy2VHZ^zeLoRy1;`z_^5;KxFiu#&-e@a=9B2xy;1eu3nS`d zsNb^hfS(UU)K@O>)RQBmal`zl!RqQv%Zk4d$7TB zG(%C)4xR2 zYoKF(iMCk$W!y`90{nO*+64!~UxB*cbv528fE;xG0{p!#s(? zdV5rz1-U=tf~a}~^4xR2r#|%&g+7Kc0B~=4M^ueNZ*9s))GGA5ySpHlGg0*f`u81& zczeg^=~D(9dp- zs%N9U7G4kkU)#~Qjz)W;|6aF3sW(Daeh+!L@l()4Dd>fFLEeVY7Qe%I0{SlfIP}V4 z7)M{F)R*6c_I;+O&I3(LAhS1o2W<@5e&1!lH$SS@Ohwh3LDM1gJoV0(K-PW(+ONP^ z3ObfXpjY4Hsdqm=qCR^p#;12g)bx#>%B(>9d^W27;|`3S-JW_WWagHHQqzY=)JL9F z>I~?p8H{OP_>@xL0zZC;{`tojDD~!NLsvl$-+y*Q{p4+)x)tsA$BBq~5W3~?7ho^x zc^F$>3tQP6fCoCuy9ISUKB9j2Yt##}Jp%ds3V9ZxQ{N?nz~%en&?V>W`1 z(3_i&1&tWj#xZwz>5`~gI09d;qY-s1<}p73{il3WsUOex)X%=E)b{i7>hbCDn*|xX z9CexjO&d0N>LnO=&bt$R@|}=p=*OQv3uE$+Kr3YPx2Vq<_eRw5*{I55?2DZXUSO{D z-k(O*#gOwi4nh~7hpNM7X zrx;`2_XzMnR^D|K_Do*rskc5es-E_EME&`97-!Fc4(WhwV?6o%`S`|IQ_6zZ3o*7r ze?9&U(6~6NUi9v$deugyx(=<+NgTw^N|KQ*z*J6e#BEByaztapg;bGJ~sx~>(GBL*%VPPYm2JCpr4+1 zJ!I}C$j0X*>hJd|^^Q+K?}6`!q5VJh2IzmZ^}Ue0178guLIw+OL>S}pDVSHB|6NaY z-Hmx+1Y-l{G2i}-Qd_%~dhyZdFF%W@Z$URN#Mu66@TlWk(CMG?)cD;PgCB;@`aRyM zMc=%$7*U_-QfkXC&^Cttc@gyfGtoaVW?X$_RDJa-(9n;ue66SMz6SHeDB9*?jIFPa zsJ79lI{Ri%J*x`;-3NK^y(@c(%5?_-!BJn%J7JqS6t1^waHKSI}D@c-I7^Ej!B zYmZlV8$<*gL{SmRhzpAjVHqdHm33s4Wq=tr2iuvR9;RVtx|!}_bBPsI{= zd;1=2kTUM@t7SVJy0pt6Y_al~sT|Dw(t-@o^NZ3kbKSuaGX$Od)l$Z8Y1CXspFWFu zrMAVG75H7eLU8P&=BZ<`1>(r;O!^QTn?7udn4KE<7H|vahi_x9`73h;w!rD=kqyiN z-5Tf%$g%PW#?gA}g&uyCzWy}x$ErD;?79Lw89i9Omid~t?kZ!N`u%h*efu%`-dBvb z9kBV^qGkd8{$b|M#}<(%cG%$aBjy9<;y$$7U2hq47=A5##mwcgn7QbW5%YM86Cv}k zr_sGR^m)!0vkd#V>IcTG`xQ202AdDv)_XmAZyNJVIsVvQCr%(OWA?3ABc}8!#%8~m zxq>!51>ODHRmM!FFFrmiW?uOayy3wy)BARG(V^^a84xkQd{63Q%wv5c<_-GZMU0!> z4>ab2b;jKEJLEbG9e6a)6}0E4?3YE)^vC8njJbG^&tm4R4;de~#LNS?gMWH+)Vy~D z_VeJVxf=WS^J&-$r(?S@mX@Ono|#A*^w`noVB?|3pNF@e^vAycLtSn#rWTv2@p0aT zm^W@b47>6n-Z#1%J5OART~p88)%TN#`8bDdQi;tx4cFJ0TgZR!-*Iy98OGWW?5{nI z>Cub+f{gm!fDODCwj%rt{2E*7&8T_y(U^G|dF_;rnnUT&9q_P{dVTybHZbjYX*F~0 zO46Wv?qfda@gVjXbIHKNBIZ`+uy>d<)-vaepv}%sn{*_Z6Z}X1#Ao}SJ=3VOW(6KSIbRX=Z zdE|K&?_w7)hu?~QiVgWVve^?GPmG#**GEm_ zY5EU+X$rQ(bZsonstBYJ?&oX@UG1D4>Hz}@9L@8 zY6ls!`efvO6K#c#Kb1OMP=bwvE<2-l#5{03_96EAN9Us>$#35i80%FL`>t{i{rqgk z>dkBEM+afMeS<#V16z~+n7WcNG5|eH8%CKgmeMYz%qO>#=7bbF?QHrn?Rqt`$i2ec zg>2s3&$In%iTh>(`LB+crW5GTKZ%@G4MA1eHiV0Tt93*=Ed<>@qTpzHV=C1 zD%$B!=f}*N<7rFgx2B7tX83C6O4{zM6#56f7^M!!oE|e1ss9Uec{f5Yp7$tnVa|Az z@!y3pISU25hIhiBG498qzg}lP95$Lc1KrUt5;4c^A2YS^dn)rqV>9(co;~QNyWPh- zGOag%T7Df3oMGd#N$Qu}z1KX8w@pF|-x)*ET0&L-0I~@wuMo{>&l! z?*vBQTc-F|lt57eMF|uoP?SJX0!0ZFB~X+=Q36E?6eUoUz`wNwwtr@LWR%o^_u>P2 z*DZNZ@F_QJ*aoi+8(84vPws6IH=5srSZk6$!H|;B7<*C$(?QaA--fkB_Rq5N$s$nr zFAn}wE-vS6T9WaMhbLl>J=Jy^GJ3Sdi6B>t6R`=5i*TNea@nHbO6TP#zM9uIn%Cy| zYh_P-s$p3!8865#KY$#)I(N}L-M;hhL9-f^1}@*fD|B1@x|-Go=O^VOoM=}Y*OL+F ze8YA5cJe{yB7Z5nyNPTH?=zggR4$(BXlO}!$9wY%4+Z-5?5=rBo3q?VDPH}1s{hft z&v^%1C%>5!IS!+`GXy!(v5#82Kx;!BKqvwAa^W3>gJY-REs417bI)*6fwBbKg1#0l zvH5o@vk=>zcLS}k*2%*%kC30>lk~9pM$C>pMV7sJiY(=~)XDoDDUmuz-Mg3wt43a0 zug<~!YP)KDhYv4l#KrIraMM}ag_itxFNogD4On8cGm7dFH%KU!jWArB`5 z*{{AQ;ZlxGYxCusFMd*|p?=y($s*!79*3#F!$lt+%0u#^OPCRw$4EbqXvre-7a6(o zg_g7j;G}$^MRQeX+8>6}+A?)a2`?fGkx_=zT->I+q=3sz7D}p>UOBcl{};^ z__Aj8<>-(Q?+nHpDQC3!fWX9xb4 zhbm}EFZC53=4$$*f_PR>IUeSzzvJOZXh|F9A+C8GQ;>(_p&{_MJWPg`^i~nTL!+io z2JuQ{96BE6tH0x6478*T^N`X!78K;+cxVayEe}(nCB3wl$e~Tsr-OKQY&#wnslVf4 z9JHhj^N`g%as_!f9v1ulQL6*Gck%S((n9~J=f7P2kLcVksHu+2VeGM-o#xp7%qq_L z$7hc8RCwoMXmIH($7#Nsz^o!|_0*}8{EUQ9-5)JELDO!+u8?vkO&Bw!wmN?J^r^Mg zekOUo%EyVCmy@aJI&oo{o=hC!`4o*iwsTonJUWa1nGYqhvA>qRLi5{%O%s-1)fBFG!BF3U(n^qlv*Klh3{%#yQ7!!A#5 zTCD77vnZ|Jc~8M^HHI^eSuW_26T*6C>>SU(J+)W>+PynrVyK35t-qv^vs*qSN3MRp zMd_%`^Pz72^Uh*fF%coohUew0aIzFFoPPUqIue9nT_c82E;u z$EKEhp)N;T?gfO8G95PVN`4<~T#4=)@!;Gs*jx52py;e_^tFqK@BSv{OYU3gERm73 zD@8u;x5&i(mU_G2QeXF5>gRq7|MGj^&h-eGZ>q8;jHTH;d*f*QxOa9ESL7OwdzZ$&TjRRAiTxY%oeyRR0k4K=zh4Xk6TFQHjXH5ni{#<){nHvrYnJ?q8uZgf@g1sCV2~%=< z7Xo)*hr^`r0Jo%p7GD%y>!teyBWrpNEdNwaE(V>EaiUJK*dTw^L7&&b8=P4Ton7i-TC@4Lyk*+hL?5Y^RZwB)%su|#uf3D>6}EE`9k zZ6aMp!oa<>GIHs>5?f4R8qZ&{3*>sF<)f)nJ_^F}_$Ua=X6x;E@#LQkHEK|Y!Z1nb z+AZPLZ%LuN^8NGW)#PO25{1S}L73&=mGu*wuF3Cqj`w{3{Ic@Hg!9k*KBr-h3%{S{ z`Ac>o{?1X}eI^KV{9Tn77W8fLUz~08l7z~?+>7sL!3)_F4EN=ADZ6w<~f0eCEvx+o}vtAOx;dVOg zP00(Om}EQ4TMI2raC3dze7P{Z+F>gXRXy@*L0oRVtU*DKX=~(yXcwEhI~}{4_|?sm zW#tm2p7E*0pbUrG`z0?ty*N2t?oQ$!lO*%Fr?XzSVLsA$(qaNNP7*X6pLZ|Ln8czE z?!XBm03y)RkrlKccyaag;&Q_~Ff*Hq>&ljU5v!XOs4v;bm01qPKITr^yPVwjc4zW zXa~Dc!$Mun|X176Do+|`sFw;kA&?V2&r_^_AV0a}l-}m+`O~~S z1xJT#n7lBT_6!>a8WLGHfScsP-I(~P4HsVqKt;JPN3S0Bg%$B-^gR6|9&Z#JED35HKglJI<_2Q4|VDD zGPJiY!e-O~Sh%cCfWIevLon0Yu*J*Z)-E zTP>snw@szn;)~r(Bf7|qmFqf*=cz1@kKe0oA1`A+v6feX-;^AKzkQirr0&Ii1OFZ1 zh2vFLo`2A^Y}xng5RzMJ7JRt&I>_>oNVj3pIey*bE%J5oJe|_Cky7!lGY{k{wyd;+ zwvjE%@jp1=A8akKyx;?z7c0xg1M4#F=>>q~WaWpTm^Skonrt6YmuYUx)uq}{bdAZn zw%kJQJ5CK1?04xqZ;|KMl*yKDzpjo4+9pLm@#VLHx>h$g2?7^jo*n?2>nHb%96C}J zWV;<7GgZC`E8qFxl$SQ8 zT9Vn)K}$zf46YLt{PdD+>B1CpA3n?%^YCFqhYcNmz_5yuLxFC-npAb*< z@*%@Keef5h*B;{0UnzaXq@dk4+h^&2u5Ze_v5fDqK6HI>u-1)xpF(qXFsI+C;i0i) z@85CtZ-g&yyh{q~T1^`Jr@qCJR@VO@d^F{croTCU`s1hl%^T3j!1cvx(#HyPvH!Be zICSNn*;zcbvRyfH-?SZ&K5P^BZE3r`MS81O7ADav8>vXY@9KPE=O^hq-;k%Y(=9*Z zJcQKNO}tK(Dq$-0#{hU>_~HEd0K z#xfR~TP@Tcc}bcsq>(xJQrfv~^JBI(m1s|A(@nWjt_;H#EY0RRSe&REF>Lq`>tsIb zV9{e)-Qq-DjwR7-U2B$~!^?-F-$ooDaooI;T$;$_bWF=V86#*@9=6~0(mFgs9b8?P zOP>kM6JM{%H%#W8TpHWm#g%W%@l@86CuAdg);FlF&OtOP#L?_3cL}13eO4 zX^nhbTh>VVg?-y!Imr5LetxI%+$&))uAioCH*YhS*}21&rE~vAFCT91ce+{ZBr(8R zmbCkA9Siz%NUPVR7cyVE{?Mu3)A1*=>8J8PiSts9C;84DPqi&(nYl5Jz5y?s_mX%U z?{Rj|hl{tFu^ktC%*FeVF|_f#bws*hYJ(UhKCcVxSeu?~cP}<$eW4+}ie;(8s)bCR-lLk>IGRvrj<~2a` z!erc9b6s1mC0oz1uyxy;{3p|&9%eCK>bkGi_2|l=T{b6gxwFB^`+6%Ac~7Wf^v_7O zX2fjCHs|DG!jQh181x@e5%x}HDSMEXeLaKS!lr=@eye!HMXO=_-3RRvpG2>tt`RjTnu(@ZLVo}MNN84Hjzp(B&1#C zoR`dr$77o~{>0|1ZWgOI(5do1Bf7@fJxciyi&`3uiOmFM-m2RlAO@1yqpFn?{PX(L@4EaU`ZWHWk^*NC8h zHdvX?O}3{eEajUm*tAx#NHmz6pFm#H_D;78-^1Wr=Ae=2D%VfeTUj@ud}h@zOnN4j zYlor4ZB6n{%W{+J6PvRs5Whc$J||zVPuTtvUTE^y8oYMic>D;j1N;8z`fH^vTXbvZ>n}gjdf%}pc8=Ot<2U{O z?O9HC38*F2K-r5m&A0>WXiWP~DH_MUQ%?46vYDPuHX~${u_C56+01cuHP$AZJcFNM zo2((#RyQ%7&CMQFQMa_Byl&j2p~{rom6kV<3^OtjQcVuf>-x=&i)2uc19wd@d8q9E!=oOn&qWsHRDRlc%w+Sw?$l14 z;qQmr00zSqJTJptgS!fsn*o*=&v$X_aqDo~fi2Jz_v!XvCGqTsE5$Vq1&@m7J~)Zf z4OfEO3AZ~g@_qIj@N8%XSBGZ;cQ$Sk7)Y1!JOCVp7kQ2@0f&#L{I1&#ECHT7^k)qU z*AKT3PH6dEi#r51THG_Z7jY6t{QrOx|Mzg+sap?RFWk*csq04XgvN@v(zqH1{69Jp$3h<(CybzqW$H9JKVW*t6l$*iLSpNvW!4Ii~?-OQ#d3XkL z>Io*}LlJ};HK(#-dtnt=7}ZfTvU}9Lacjgp2iC~3$Z*3tFx1`!YhoqXkYG`)T0_4- zGivVoC~7_d)8`}h=`0!+HLH&X6XkPb?!6S8kTux(^y^;lQ>Uk6=5EUSBOiO7y$M`D zuw`y|mcIYDsCjN8?;mGI%hUJOg|B%}vcd9;nAsH^qR(XaFxYRxXUmCE^JDtUyqoC@hk-wJ zJL`1xVd4LQ{gCI0jQQPTW+&R}qj$jb7zF-QlKw`YA3)hXc=lWf)(X#lxQky0Q;oiJ z9N0pAp-XXBgJ*LPGPnc$so$&xCkMQh=mj3;(7h?fCvh&iFlz4E3k;LDz$0Yr?StPb z;Am~nb0zp#9YfJ;;`cH7ho_V~WChrxER3%N3rWfsY^z>8@7Wo>hd!MBeAFBdu1$}2 zFs{0TV>S)k#pUpGIeQjwVQjD??8=Lf+nk8mg>+w?2Bz92QS)osbj9kJS@8tej&Fb~ zN!V|fp?hXW&6o6}V;B?D4+6gk|JCP#vvxc9PxrEy^f)kq=$GHe|Kz1Hv+NGWH@_$S z967(wc=>PiLM@mwXVRZ1$IKXh?@joTgTQD3%Z!&_^8j*x;mN4k7dc#xURaL2?)W@r z-a!5z(@zbyzx?gT%(Hi?{aO64D1o8`iV`SFpeTW&1d0+UO5oo_0^#%j|A9Ss9{8Kv z7M%Yt=e&bBG5f~zlt1pt`{(zxu<*pka@Gyo0l+uCz2x(}f{`N;PfUah@0P@30b zOYf|712ktSzg0Q1Mjmg=D$xrq~ z{YmrRW=D?(y49A~RcT;bE!|7$&z0`4G)rhUd~c=Y6U{D?&-Kv>3fyF7FxzEV&1YooVQmulMUm@=9o1J*xBurJq!K zJ8he1lm>Rz*5x^+ftCFZyrj}$US1$C!EA_XJO4)W{z7?qMd=06cFQR$a8{w_*CuXJCfFH*W6w9Jvf{aSwaf|hpUcYK>4Yy@Ec^v~ULp1M$lrC5L5H0H^rN=A% ziqe%zvy5taovbvURkn<`unx>LbMd*%;GW)|MqYBZp%gi74O>n3V*5&4`c4gH2xi3< zTd558ID7MgXj`4@;*_zFGUjS~*F(!Zv4hHR0krhTlgD{7Y*G3or87!Lm2Ou$0{!jV zKa*_8B^zMP&RK#LEqf<#Q{L`SdOPU$&D$AUZU3nKM#wHU$J71rAp82nD3dmNLhJk0 zSdV^I!z&4QdxK;==^mu{Ngt-mgH&QqSxWZvxP3bPC2peG?te@)FU&iScmT?}I@iuN zpce2Qf<05Rr?plfA8OJB2YbV&kr!8zH|l1UQDSqra$MMybfuhG%L@~nm|5H?&XG2I z)NE9UKR?V*800DVFro5>SA^2JFin;hCSyEY9)Uieepryu-z%BSFVEkj<@_~?zXwWi zU&7^t<>809L?KzF^UL9sRZwRarpfZcas}lP=-0~+3ljSEa&0rceu-@tw^zuQy$uNV zfH~RXZRP6X`?B)_{Jlj^K6v{w^Xo63_S?O;r`y#JZ(BE)x6i*9;O~zT{DGQGaMo>{ zqC~i3?`|)XU=P$}*pB8RcpG6e0&wB>S_$4jUYszj1#RHsd1Z$3%7Z@%;@w%8pTJ+p zAwq$`pw5oBy9?u0b2B{SVHst*I2)B2f>sIhh52UZh$DGUe2@S5dzv+X-FOPtpSl3%i+@E46eUoUKv4ok e2^1wzlt57eMF|uoP?SJX0!0ZFCGh`T0{;sxP!7=m literal 0 HcmV?d00001 diff --git a/tools/delaylib/delaylib.vcxproj b/tools/delaylib/delaylib.vcxproj index a06c38f4c7eb..112cba61cfd8 100644 --- a/tools/delaylib/delaylib.vcxproj +++ b/tools/delaylib/delaylib.vcxproj @@ -92,8 +92,6 @@ - - Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) @@ -106,6 +104,7 @@ true true ProgramDatabase + $(OutDir)$(TargetName).pdb Windows @@ -113,8 +112,6 @@ - - Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) @@ -127,6 +124,7 @@ true true ProgramDatabase + $(OutDir)$(TargetName).pdb Windows @@ -135,8 +133,6 @@ Level3 - - MaxSpeed true true @@ -148,6 +144,8 @@ true true true + ProgramDatabase + $(OutDir)$(TargetName).pdb Windows @@ -158,8 +156,6 @@ Level3 - - MaxSpeed true true @@ -171,6 +167,8 @@ true true true + ProgramDatabase + $(OutDir)$(TargetName).pdb Windows From adaa37287aff1e657c8a5462b929a9f0aafb17a2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 14:16:57 +1000 Subject: [PATCH 143/839] Fix setup build --- tools/CustomSetupTool/CustomSetupTool/resource.rc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 3599828e2a54..51ef2760a531 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -222,10 +222,11 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS IDR_LICENCE_DATA RCDATA "resources\\LICENSE.txt" #if defined(APSTUDIO_INVOKED) || defined(PH_BUILD_API) -#if defined(APSTUDIO_INVOKED) -IDR_BIN_DATA$(PH_BUILD_API) RCDATA "..\\..\\..\\build\\output\\processhacker-build-bin.zip" -#endif + +IDR_BIN_DATA RCDATA "..\\..\\..\\build\\output\\processhacker-build-bin.zip" + #endif + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// From e121b8c62358bdb60f197c39e58f2882d5daa9d5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 14:30:17 +1000 Subject: [PATCH 144/839] Fix delayload bug --- tools/delaylib/bin/Debug32/delaylib.lib | Bin 21434 -> 21466 bytes tools/delaylib/bin/Debug32/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Debug64/delaylib.lib | Bin 21430 -> 22424 bytes tools/delaylib/bin/Debug64/delaylib.pdb | Bin 135168 -> 135168 bytes tools/delaylib/bin/Release32/delaylib.lib | Bin 226982 -> 227024 bytes tools/delaylib/bin/Release32/delaylib.pdb | Bin 126976 -> 126976 bytes tools/delaylib/bin/Release64/delaylib.lib | Bin 225884 -> 226788 bytes tools/delaylib/bin/Release64/delaylib.pdb | Bin 126976 -> 135168 bytes tools/delaylib/main.c | 18 ++++++++---------- 9 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tools/delaylib/bin/Debug32/delaylib.lib b/tools/delaylib/bin/Debug32/delaylib.lib index 70b36d7a10a74d8054b3fb190f8a56a266db2252..cc2d9ac662d043be09abfdc7695e9b120c70135a 100644 GIT binary patch delta 2395 zcmaJ@e{54#6h3X&@ir#gWIu=n8J4l3LpnCb1}@nck4;!%8><<_nq&Y zbKgDZ-QL>|j~T8UGwh#cF0_>t6|*cu=7Eyyy79ZtGMLTiv}r+EF;~9LR#a4!l{L?{ zV1Z5ZG$d9VlIh*d8LJPoA!9|tg<_&xMxs;fA(PdxoaLLW2^JI4@lv95Y#p>>)@jNy zS1cyF$4;26<~bEadF(RSdrOE;v%kR_Dv5?!cAV8b3KnAaIICe6+Y*;!p8qsaDLV|? z%Z6ZgE+g`@9}&3^j0Muod4^K{!a*VuaPunZc z`B60-4x13Zxz)Sg?doWCm%Ci8?zT2cr-`YKzIzVb-A0I+pE1!*Rqb5O_T!9 zKy-X}OMB~f%7hYx!Y9@2b#*kkqoZcP(H{+sOr_q*<_gmd-E5^`Ny*08lMPyt7DVn~ zFABLSvtTcbhlD zqV{ww0)BlOwO2i0?~dA+;WyIC)L#)>6&-`;S=eg{Rw<9jw-DAGU={F5;IqKFzzx8B z;7h;)pbRVoD!>K6W55#NaUjOwMjTWLeKwgIMCa@QH1b7hmR^ao*-q|T9$F6^Oc3Rn?^R2Td7 zzYA!+Bf54`*S^-ZZ*?uCYjCL*R!$R%|J>J5diqm8r8TC8q@{b( zHeFS({_r|J2Q6Xw3m;(@&B?61Y?U;uI0R*}V1|QaaZs@fN^Mf-Sbx4YRKuPU9tHt*6jpDq&8qe`^{53FmK|S@H zXc2PnbKt1VzZNL_8?>iOdp1fMG~tO#d-zb1Vx|8mHo$up@{nh0k7KkBlN6I`JmHv@ z_|=^g&=$TQ@XpnNn=7G45xB_*|rL4EBwP;|Z9Y_&JY80gXUy@C+2N5tB z$yp=@J1ohk#e06WeKI~hd@*lQM3r4zY^&odl3)B7m>Qup0zR&HgYz!(i??B%yP7tV zcE_9&{19PlE4EAdSO9#ZPor`$zWC#5wXQYm+9j|@HSZO$*;>Y3Fsr6v8;)u-z@}?j zuCA4W@hPW~L-($YcqNEiboU;xEG_v!#0~EeFq@{m4aSezG&%*AtFe#uxDhaH>6rc~ z*gTEh0K@hU?PoA-@L+#|&DWSix$O#ETJn)#xWM+=E0g#!&Rb!=J!N0uy8Q#Aa~^;3 t1tjn$n`mbBihP_3bTLNZgJmG~ehlxT5ToEsp|}_Y|8+2A;7-ku@gKctQQiOm delta 2179 zcmZXVZERCj7{{O6uB9uJb+Yod0fjOLZfdu|U@Q~pxH6DC*jxoMD8)fw!Wb|_WY{)y z>h;hFIZjOwd|^6>Ba(Cq3LiQ&Kq4ZqqGU0{?86K-san23bpFq6cg*x=%kTgEpXa>X zbMBtq>{Pz$RQ64@l$4be&*Cm+?EbP}WN=5SG2CPFdyFF2u&2o5DJ?B^xr#lrOJ*CH zHTI=SCRJojd8&o?m@3nwbBKO55gq0CncYeyFEYE+O=hCQ6+|EN)$qphkU8Hn)k}1f zA2Yix8I?pM`E{u9LZWv57u38(7{T+>+?EqiSGktvRxEsLTD~QFF;Ol*h`5^fAYQYC zsGk3Z%0{Sd5vQe4sSpdEBqAGr#Zw58k0=fLcl=I9ehw|l61kyAjLAn};xAjTD{y6BSL!O~uHD9gWZPLnCr3 z24Fnq7%HyvH`b>rmm=|_n5A+37II){e`?xwjm_J3(O5V!?y!wh`uGgn!-d_*_M*Ig z(?&9pm7HXjTb_&sM=QqWb1`hJUTNX> z^pdrmNt>crXcU!YT}s6lQn7-|6wmjmc)@?-7gF(c)ca2D7J&IUgP@p%Q0f#u*05FbzQPY{OgTuSUl(jSAuom)1Ww_)!+v38L%D1=7QbeX7Cia6TAfm z@iPFng7@PL-T=pdVbBe}3D$ydgHM`rhz>wB!8i!Ef**jdf*(e1+S_M1iZ5P#E@N5S zXLDAZJ^WJI&mED#%%DB+5ego_YmKB&Qp!%>N$;}sEckkpb?GT(r@7L5RC?Q`w^Mqp z(tAyMJgEX$_#J5;mW7?ti%9Q;^uCtfIq6-M-VN!+q!*XoRVz`c@xnKtCJAXL@prq5 zoX&-}&R$u(yKu>Yrcs%v1CcbIGe0yd!F)F6wOLR@x;nr#8>`6(oiRdRA5(3t$`R@@ zLald$AoC^o<@popVz;+2j84=JI)z=H7!c6aSnHk`HCxZB_mtb(IkkHlT+E}ZCz+O^ zYo{?p_eD#hy84Z-p4Zh2x_U`hudzf}>*n(oObG17cfoua1-?GBuUGf=Gqs=DLbotY zuz3nYY|0xP6f1Qxt*fR1wXxdN+VR`2WOWbjmUQii$R4R>YHayXMeCk8?eH_LpJj#4 z7*)QGb8RhX7O9Qq>1xo}=_}0Fq5EDJJKd|R`*ig!U42(q-@{JBEj-^l(ebT#wL}9o z;)aRJB;y8x!f`8}`a@%8(}y^BXQLh)|m8G28l#i~O! zVsp8|#AEbV(Th7!BTrb?MzlFSBRJ}c8NLb;5=fn*xly-*&*+YcrF8D-P^P}2?7 zCF@Q@p-*Dr^HAuNP}iVJ4AloU(@-%e^iO1cELI;$EIegXqWrkFC`0_S6GsvGVds|- zbM@yYe^D92lNcb*(@fKrRu`ewQgoOjru}=EBc{JQ%n|2$XPAT5O>t9Xd`*u@vHt@N CSMksQ diff --git a/tools/delaylib/bin/Debug32/delaylib.pdb b/tools/delaylib/bin/Debug32/delaylib.pdb index 71941c98c2d36c2f5796a7d8690334a12b623f7a..37c40a7dd5a473a493e4a1da035d44b9289762b5 100644 GIT binary patch delta 73 zcmZozz|pXPV*`tW#0qi6NJa(*2Ag*x8G>Kx-MU1w?;p7TYWijwhZp=fWt(LJw#x)C HKG_cd&lwz* delta 73 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 M7K>)Obo1!{0b`i2OaK4? delta 3967 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 CVqCHS diff --git a/tools/delaylib/bin/Release32/delaylib.lib b/tools/delaylib/bin/Release32/delaylib.lib index d84aba5d43a4a3e4b815e2850b5b964a6adf80c3..667e7b009d508510365868b9312929583b421cfa 100644 GIT binary patch delta 1673 zcmZ`(ZA?>F7(O4RR)MSv!sKI?QZJO00WH(EQs_r!GAUfc$2LYlXe4#fTL|(o1ff!Q zw+U9TnPp9+>ey3~^R@jAyk^C(VR6kINe0GfUX{acfM3@ZgjQEE z^5gm3qBi@}c*d2LFB-nQcxTZlG~e8IjH0ZJO^C-H#D1XPY9EynWDCahn%vkWO?q1M zZwQ%U3z`x<9RF4GGsctgtJ>mp{lZ^&|F~J`KK6vE2~cBh-RJ zIAC3teT&8V@U<+25;bgon(WwThqyy*O{=y@;JH8s`WHc?T8|*Fcg>5R*x$ zMAn+-bT-*LXtK&)W4$1wm#G)c(*t& zSP`Ay%2&|&Iy80^BIe_;i1J;^+jngF*oI8`uiFgdq48stuC~s*?$-lS$>JCKFI6lXCxe zGKbesCdenVt84;EH+V^WcD);?)CWbVn)_MFESw4_^Uo4j<8i29bYuD$q@Peq92aht zFGIid;amJ1IFmJHm&m?TFd`p6ah?G;rxu)S{v62x=)Y%)Ve=`tCx9M)mz|v@IYt>w zKpj_5!ykYp3}?d8kLdK_ANkRDvG#K$zs)|OW}HsGJ_S!enO0v<2BZA-b;9<-TZ)`@ z66bW4AnZADWvK$-y7;~~RIVK;M_A?kFvHqd^#j4+9S-ylv=E z&(ZD?e{c7p{ez(+NG3|kWu!w!Oq}e5MDuYSyCBgT>}3xmYGQQ~O)Gd?>g(+vXzZ4H zoUrbw77(ahXcLQxK+Za$#I?7JREMaX2mi`;8i7~F-Wzvli6bVZ;akM06bTWrr z*6iwpL0L1;!{!90JWS0pn}Am(G0v%(3Hmf8GfRBY`7qs^Z=2ugEkhi!Nzb|8zu)&e z-|yV}<6ZOIU32E~B2SCQ)119ewp{gg;#Dhm8Oy8YP$6)FT3t=eHu&`xBlO?_MiKl> z>DDgq{haB8%R0Wg^D0G~OdF7heVzT5es26_J3*e8vlc^n_B}&MQRo&z zk7chI95|Z$$*_d+Xl~AEFR{3nmJXZbQ&+s5-&{Iwxcz=^x%4e7?o0PM-|TbZg+3>_ z-9vXUA0HVbR(^PlRMIC|P*@)9ih&Ja`NS&yKF z+QjPf=E?1i8R`WM8AoEYUK~`$`&bBetrZqEe1|#JJ+(+{%3IfLPBlC&A#FErn;^Bc zT?_Gx6QrCEPmnUxemKN)tVL`N3ib&k_W~N=p_+6T=cIfO-J_IwhF@JxL4j z%p@t>7K4$$4jJ{wVUh7cK<#AR0>=%DPA9}0CdsbGL+j#;IU@nCiK>FW5Aj`Ha8iLU z<8IBLPe@BGC-RQxq1Own7oH#KMSEDBn=^2C;<_4fU5z*-i-s#|^y+HFbv0gEt43U? z;i^kKDnt@U=^T#Acu>a2xMK<=@8^*z5)6(2FGWEJI{jBcWfa^{Je)s(k_L-iZ&(CI z7UjCQ0sZ{5DNq}ciTM5>96$of8u)uqoHx5D13x(eA3fQzupYHeK;46iI zGjETG^Iz172pq7c!FqVh{{7}DGYdDe#Jcz|xV8%?MfQNTi|q^GRxleu3jzW9A;-#KS*v~~K_rx^TM nY}}C@Fjg&G7@#+e6u#Pge~NDAr&H8SLbrH(ik9(@Q`G(sLd?tE diff --git a/tools/delaylib/bin/Release32/delaylib.pdb b/tools/delaylib/bin/Release32/delaylib.pdb index a39cad85a921cd2a5f67dc568aa9b71582950f3b..0bf14ba171d73ac093d01ba017edea9e59be0c84 100644 GIT binary patch delta 73 zcmZp8z~1nHeFKYw#5!@sNJa(*2FJhEE1y}~JMGIh%l)YzB)eJ0;RQcV*=Cu5?J@z3 HPv!#v&Y~LG delta 73 zcmZp8z~1nHeFKYwgomJFBqIX@!=_~qCH6e8^qZ=a;OnrUb;o8IhZp=fWt(LJw#x)C HKA8^y+O!+P diff --git a/tools/delaylib/bin/Release64/delaylib.lib b/tools/delaylib/bin/Release64/delaylib.lib index 9a4e68d5ee6894cafef8e41cdb7095448ebeea2e..b02b934e17e3e210055d34ce0fbb739c1a0788ba 100644 GIT binary patch delta 5041 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 delta 4103 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 diff --git a/tools/delaylib/bin/Release64/delaylib.pdb b/tools/delaylib/bin/Release64/delaylib.pdb index 12ca4d89de69107a8c599032fcf8bf29b52c2506..05e18d14c67d6fc6ac455d1e2dce98390b49f515 100644 GIT binary patch delta 3445 zcma)933QZ260WcRoE$kPAt47jAO}%M$OYs8Btb9~k*%Igi0*s7xBq)J)n9dWb#-@D zbx%{9XVXTn*_3(ld7sMwdNXo z*E@lYhO1|qWp0(Vc1rDEt@Sl#n>6V{m$vNY>c3&nmOua3T3W~5rBiyRx!zJa(OOee zW&2+p50}oA8uMIBU#C6nu=gK5f6s^f`K0Z8x%x}$tL_ok=UOUnG0&@-(`TiYx^5%S zAa5dNlfBq0+c~=^zo>ZHO#3U>Lp@y0L)U{EyCMkz9-?E&k0yN-n#YPPR=5v&`2|Ru z$b;UMzJ8?q_6r|CpkNv6COy6SG1884jnVUx?ACj(G4H#^^pi0<5Z?CJwgbKVdK5t) zx)cxEyDmlcx)^;m)V;^=l*>U_Z-3y&A&q)AFPczYZJpyh7+Jf8E?(#wC_AG)@cUI? zNxju(nPVf}r&kn0J?C0lweUA%qNvnnwppBd+Qv}o9oc6$fyx%tStzn=D!B)F%GQ1? z9`}C9H#Yb;F53$`f_Z%$yxAIuHTaah<57wuJS!drILce&@g6?sh6LP&FZgrKaUIEl($-NKB3w@% zDqKf$T<_%0{;8JvHgd+(V8LEA*<4#$ZK;h&DHAI;96F)^Rt%9Rt2 z;F;s(6o&9e#i<57M1kjANfxJ~1BHkSPD$=bDbX)+;*=Y>!ih)Sz;#ZXdIL8(aoP>M+=$}k(_IkX&&2;w#6V&oS7t{kzL=a|vj_BA#tUy61xz4XXS%@j5F)8{dePaKE4E>uAum z1Vt^01}!ujw56TsLY6@%4=B1Pf+%Z=qNf)bRD7pF`QIy2Q)SAna_*-=+holBRWhbr z(HXgA{O?v|J7SO})u5XuC>nXdpmNbYF-K9!>!638HYj4TqJ0mlY$ifa%59`+ytK<4DNQ)&Yi}!#YF&=iUO2{UR!b>7&O28l)Bl864wScvNp3OyRyoB# zua<)SB|o+Xi_yXUYmwytwJ7l?qA~bH#=`rqZ;@b6SRh}k}+kg+1 zw}~%rKs$W+*haZS{CVIeH2Uk(m!_8~OVlofbMq#Y`bP@Zo@UE2YH^eU&&gsD&k>vP z4obm!P-Sw7tJ8E9QIv|t1 zUzB@i4DWss>tbRE!7@AP_rp}%?G2Xt1=FkQZ4Kt?p4S>(?OellggCFP6m$4@JQGttv zo)mgY=n0_f8DX+Ti`+5Teh8PC8ES5Vxls9igaQa6*PbXN8{;8vV9}i5Jlw z9~rcG3}|01(E*VUeMHf+B1P|5BqAl34vB0~ry_0hFrR3VP5qd!wIH@J$=9H+tsw8G zKu4uFH-V`2O_1;36n!gM^Cu5Q$3#9d*Pv6vi{cbjXDIsf`=Aqo7p#NRs{tW$DGJS! z;^A@0ppdJIF8E16FEq$Ul{8Dt9K^{*i^F-VkRWRh^< zK6fb^EIeLl)p5`p#}(aMOf*bzf9`ktnnd={AVmSfS6()# zVFXc#mVHkYEL@u&{s_@?380nN4d)a;pH&paN@# zTFNO&J$bn9k_|h!S5cfe-1Gs_DXFg8r5=7E)$+_gC5I*U^_L{E;N_UmikH20rEp&% zxRd?&AaG!wJR&q{O(_n0@YetPk0n~-L|^_?AoQ@*9sMOF6NL&dlNri5`Vel7kaFu3 i4Hh0Iq>GZyltDt_J0>jh-I4abZ;M}Dr|*iAy#E4Vo=1xS delta 2586 zcmZ`*X;f5K60Yw)c7>*Cp+#s}+*pMc1e8TODsf*za5+auJ=#G}bliv|8AmWmG@u4Y zl2UPBCX#UpVUF);W+w578pSU!Vz>ej7$Z&lqc z-0D$S?_L_=9YRFQM2x3%RoLVjlS@5OVHHAuF?}BD654TqIcz-Sa|-G5_v z+M_osI+j%hXI^ZFQ&r|+54E)`M|!enDXhU|J?;4aBSy8q6z)avGyEMf6Zw#bZ=Spp zfsR>-yh!;Tx2)Z~Cz!WKVmnUqz$j#+hSx`7C{FXuDAeFAS4QJ)oM&4MM&cWu6@wDg z^3xb3;1b8i!isNsU@X4TE?bBjViC;AJ>kR^-qaIoQK#GE&;z)}jHm^m}c zIVJC%dCvLtxG{@d{o4_#hg_M0rD)W9_dzP~gy;4}GXBjKeG#piEPSaiR{Q-V4%*8G z6qDxX?XS_#7G98wV(+KIzkDJ%EENG9Vnt`&kp>IYe=IyE9fw?=ixt~5VCCoxEK$uC z-j{(+AU=_S9scM*6i?aCnQu{Jlmz7=K`CC+50l`+4gF-RT={7~9EUp}&O`w`+2KHH zPCFT9gcKlWkn210ftXx&CJW5vcM_( zZ20Zi!VPjxndY2FPg=P^;X*OYnLA}_{!7mAxfSDP&5Cc0$H1Q%*xrV}GH|ape9piL zuW$j?8${wOf*u*TcN-=pIH?W$8aTNPhZ;Dg4aXa}PaDoOusxoPg&6^kYQgpwxF6Bv z7NXZHgE=iY;T0}y!HKVMX$$V%f;(~L>vGgVoIgm4%EqS$VLn21$6#o136t4)_7H^e zj3JWpUG%V_DA3SN-!=j!pod;L5+xcU_4I5xMMUd4Z%XwcmUrcV5y$Ccq^PXAa}4?e zfm|^bL4kH-%Sl%BhBJ4@{5GM#dTxFNdh;Gl|<~#IpZYtytsCpgq6&Zy+xz7H$f8zE4pU|=gx-BJ=2@W zZJOee+2~!+Ase*pGteJ>rLLsyd|&|``~%2+0@3|7iuO1)>b{t$elX~1grcfQjaHT` z+I^kqajHgEVh7K2W4aXtc0GlB@x=SH>)!q0#A;if(xlZI0F` z?~+E&1TCKGqCs&dHOdj=x~Ymr9Rj_z7-UqpR=tYM`WaGc9 zuXKFx^Kl{Cck3(A_m*r6H8&gO+}y0mFVP*}R>_q22;E+YvnD;X6ze>2h7Ybo5YBP! zIwYGfh+B);c96d9Lv8^V*|uJK?sr_V9?S7P+czNEd{vB0Vl+&(8vTQ3ZNNR(dZC~5 z#B$ujaUQ%8C9cYYWb(Uo9N471yEh)2(t1xN+<@6qt<`)l04!Q7v(s`ET2hZMFzD zB2AOSGM3}7hi#X|$~}?3Lt0a3zPkf)2<5vMh8E|dc?9Qlb{9X+}36O{AD-3UfJAKZQr6R>5B_bb)JZ@a$NICEo>Va?dyN6{u zvtKdjid^OqNg7=ieU0}Xfi?Yxyml>8R5D!3#VJ`MiQOQa^^v06qHl^!NL2K08mMsy z=!>sGSr;_A^bl0Ts74p>J)cV#t^j#{2s**`YFJ-8BctLnG`c8ycy~oJlN9~t6zGEB z{PNa7>g3&+Y2qKP!4HWk~O8GBX!fqm#NB!hfnp=SZ)PMB(DkTeQ#HiY%go zMLxa)`s)=%i$)T45**6+B_i}-^HEs(bQ5N&RZ%<9Wj|{4PEScJgL@$7E86fDRYbHs z0#x=)Yw`O0j*?WHcofMtt9*X;K2bRq&u-|jdC|{#wT~`TJ%I0&hcqhumYlvYyK)0X6 zm;dv(D@^Wn Date: Mon, 22 May 2017 04:30:46 +1000 Subject: [PATCH 145/839] Revert --- .gitignore | 1 - ProcessHacker.sln | 5 - ProcessHacker/ProcessHacker.def | 20 ++-- ProcessHacker/ProcessHacker.vcxproj | 21 ++-- ProcessHacker/ProcessHacker.vcxproj.filters | 3 - ProcessHacker/exports.c | 97 ------------------- ProcessHacker/include/appsup.h | 4 +- ProcessHacker/include/mainwnd.h | 2 +- ProcessHacker/include/netprv.h | 4 + ProcessHacker/include/phapp.h | 88 +---------------- ProcessHacker/include/phplug.h | 13 --- ProcessHacker/include/procprv.h | 5 + ProcessHacker/include/srvprv.h | 5 + ProcessHacker/log.c | 5 +- ProcessHacker/logwnd.c | 5 +- ProcessHacker/main.c | 2 +- ProcessHacker/mainwnd.c | 6 +- ProcessHacker/memlists.c | 5 +- ProcessHacker/miniinfo.c | 12 +-- ProcessHacker/mwpgnet.c | 8 +- ProcessHacker/mwpgproc.c | 8 +- ProcessHacker/mwpgsrv.c | 8 +- ProcessHacker/netprv.c | 14 ++- ProcessHacker/notifico.c | 2 +- ProcessHacker/plugin.c | 20 ++-- ProcessHacker/procprv.c | 15 ++- ProcessHacker/procrec.c | 2 +- ProcessHacker/prpggen.c | 4 +- ProcessHacker/prpgperf.c | 5 +- ProcessHacker/prpgstat.c | 5 +- ProcessHacker/prpgthrd.c | 2 +- ProcessHacker/srvctl.c | 5 +- ProcessHacker/srvlist.c | 8 +- ProcessHacker/srvprv.c | 14 ++- ProcessHacker/sysinfo.c | 8 +- ProcessHacker/thrdprv.c | 5 +- phlib/global.c | 34 +++---- phlib/include/phconfig.h | 71 ++++++++------ phlib/include/phsup.h | 8 -- phnt/include/phnt.h | 10 -- plugins/DotNetTools/clrsup.c | 2 +- plugins/DotNetTools/perfpage.c | 2 +- plugins/DotNetTools/stackext.c | 2 +- plugins/ExtendedNotifications/filelog.c | 2 +- plugins/ExtendedServices/main.c | 20 ++-- plugins/ExtendedServices/other.c | 2 +- plugins/ExtendedServices/recovery.c | 4 +- plugins/ExtendedTools/disktab.c | 34 +++---- plugins/ExtendedTools/etwdisk.c | 2 +- plugins/ExtendedTools/etwmon.c | 8 +- plugins/ExtendedTools/etwprprp.c | 14 +-- plugins/ExtendedTools/etwstat.c | 8 +- plugins/ExtendedTools/exttools.h | 4 + plugins/ExtendedTools/gpumon.c | 10 +- plugins/ExtendedTools/gpunodes.c | 10 +- plugins/ExtendedTools/gpuprprp.c | 16 +-- plugins/ExtendedTools/main.c | 6 +- plugins/ExtendedTools/unldll.c | 6 +- plugins/ExtendedTools/wswatch.c | 2 +- plugins/ExtraPlugins/cloud.c | 2 +- plugins/ExtraPlugins/dialog.c | 2 +- plugins/ExtraPlugins/setup/page5.c | 8 +- plugins/ExtraPlugins/setup/updater.c | 28 ++++-- plugins/HardwareDevices/disknotify.c | 6 +- plugins/HardwareDevices/main.c | 4 +- plugins/HardwareDevices/netdetails.c | 4 +- plugins/HardwareDevices/netoptions.c | 4 +- plugins/HardwareDevices/storage.c | 6 +- plugins/NetworkTools/pages.c | 6 +- plugins/NetworkTools/ping.c | 8 +- plugins/NetworkTools/tracert.c | 7 +- plugins/NetworkTools/update.c | 63 ++++++++---- plugins/NetworkTools/whois.c | 5 +- plugins/OnlineChecks/main.c | 14 ++- plugins/OnlineChecks/page1.c | 2 +- plugins/OnlineChecks/page2.c | 2 +- plugins/OnlineChecks/page3.c | 2 +- plugins/OnlineChecks/page4.c | 4 +- plugins/OnlineChecks/upload.c | 40 ++++++-- plugins/OnlineChecks/virustotal.c | 8 +- plugins/Plugins.props | 9 +- plugins/ToolStatus/customizesb.c | 4 +- plugins/ToolStatus/customizetb.c | 10 +- plugins/ToolStatus/graph.c | 14 +-- plugins/ToolStatus/main.c | 58 +++++------ plugins/ToolStatus/options.c | 6 +- plugins/ToolStatus/statusbar.c | 8 +- plugins/ToolStatus/toolbar.c | 6 +- plugins/Updater/options.c | 4 +- plugins/Updater/page5.c | 8 +- plugins/Updater/updater.c | 24 ++--- plugins/Updater/updater.h | 3 + plugins/UserNotes/main.c | 15 ++- plugins/WindowExplorer/WindowExplorer.vcxproj | 4 +- plugins/WindowExplorer/main.c | 2 +- plugins/WindowExplorer/wndexp.h | 4 +- plugins/WindowExplorer/wndprp.c | 2 +- tools/peview/peview.vcxproj | 20 ++-- 98 files changed, 505 insertions(+), 634 deletions(-) delete mode 100644 ProcessHacker/exports.c diff --git a/.gitignore b/.gitignore index 230b4fd6ceab..53afad1c8da7 100644 --- a/.gitignore +++ b/.gitignore @@ -117,4 +117,3 @@ tools/GenerateZw/GenerateZw/bin/Release/* !tools/GenerateZw/GenerateZw/bin/Release/GenerateZw.exe !tools/delaylib/bin/ -!*pdb* diff --git a/ProcessHacker.sln b/ProcessHacker.sln index 241b7a6e05d2..4e87a04af677 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -12,8 +12,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phlib", "phlib\phlib.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peview", "tools\peview\peview.vcxproj", "{72C124A2-3C80-41C6-ABA1-C4948B713204}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9963724D-4A1D-4442-AD5A-1CE899D30D96}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -50,7 +48,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {72C124A2-3C80-41C6-ABA1-C4948B713204} = {9963724D-4A1D-4442-AD5A-1CE899D30D96} - EndGlobalSection EndGlobal diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 99e444c9bc65..97692aee76bf 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -37,16 +37,18 @@ EXPORTS PhfWakeForReleaseQueuedLock ; phconfig - PhGetSystemBasicInformation - PhWindowsVersion - PhGetGlobalDpi - PhGetImageBase + PhGlobalDpi DATA + PhHeapHandle DATA PhIsExecutingInWow64 - PhProcessAllAccess - PhProcessQueryAccess - PhThreadAllAccess - PhThreadQueryAccess - PhThreadSetAccess + PhLibImageBase DATA + PhOsVersion DATA + PhSystemBasicInformation DATA + ProcessAllAccess DATA + ProcessQueryAccess DATA + ThreadAllAccess DATA + ThreadQueryAccess DATA + ThreadSetAccess DATA + WindowsVersion DATA ; phbasesup PhAddElementAvlTree diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 67fcd39e46f5..5e3e2d24f2de 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -95,15 +95,14 @@ true - aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX86 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ___delayLoadHelper2@8 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -127,15 +126,14 @@ true - aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX64 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -164,7 +162,7 @@ StreamingSIMDExtensions - aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -172,10 +170,9 @@ MachineX86 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - ___delayLoadHelper2@8 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -199,7 +196,7 @@ true - aclui.lib;comctl32.lib;delaylib.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -207,10 +204,9 @@ MachineX64 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - __delayLoadHelper2 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -229,7 +225,6 @@ - diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index d8d900f22ea6..e4fac5fddc0e 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -381,9 +381,6 @@ Process Hacker - - Process Hacker - diff --git a/ProcessHacker/exports.c b/ProcessHacker/exports.c deleted file mode 100644 index c20fa19ae156..000000000000 --- a/ProcessHacker/exports.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Process Hacker - - * exported variables - * - * 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 - -HFONT PhGetApplicationFont( - VOID - ) -{ - return PhApplicationFont; -} - -HWND PhGetMainWndHandle( - VOID - ) -{ - return PhMainWndHandle; -} - -PVOID PhGetImageBase( - VOID - ) -{ - return PhLibImageBase; -} - -ULONG PhGetGlobalDpi( - VOID - ) -{ - return PhGlobalDpi; -} - -SYSTEM_BASIC_INFORMATION PhGetSystemBasicInformation( - VOID - ) -{ - return PhSystemBasicInformation; -} - -ACCESS_MASK PhProcessQueryAccess( - VOID - ) -{ - return ProcessQueryAccess; -} - -ACCESS_MASK PhProcessAllAccess( - VOID - ) -{ - return ProcessAllAccess; -} - -ACCESS_MASK PhThreadQueryAccess( - VOID - ) -{ - return ThreadQueryAccess; -} - -ACCESS_MASK PhThreadSetAccess( - VOID - ) -{ - return ThreadSetAccess; -} - -ACCESS_MASK PhThreadAllAccess( - VOID - ) -{ - return ThreadAllAccess; -} \ No newline at end of file diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 275273f6d4e8..fe34c8977206 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -416,8 +416,8 @@ PPH_STRING PhPcre2GetErrorMessage( _In_ INT ErrorCode ); -#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 +#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) FORCEINLINE PVOID PhpGenericPropertyPageHeader( _In_ HWND hwndDlg, diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index d93cc50263ac..d7607624acd1 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -3,7 +3,7 @@ #define PH_MAINWND_CLASSNAME L"ProcessHacker" // phapppub -extern HWND PhMainWndHandle; +PHAPPAPI extern HWND PhMainWndHandle; // phapppub extern BOOLEAN PhMainWndExiting; #define WM_PH_FIRST (WM_APP + 99) diff --git a/ProcessHacker/include/netprv.h b/ProcessHacker/include/netprv.h index b4b34835f35f..beaecea2b4a1 100644 --- a/ProcessHacker/include/netprv.h +++ b/ProcessHacker/include/netprv.h @@ -2,6 +2,10 @@ #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; diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index d0160089cf5b..5a9914f8a514 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -74,7 +74,7 @@ typedef struct _PH_STARTUP_PARAMETERS extern PPH_STRING PhApplicationDirectory; extern PPH_STRING PhApplicationFileName; -extern HFONT PhApplicationFont; +PHAPPAPI extern HFONT PhApplicationFont; // phapppub extern PPH_STRING PhCurrentUserName; extern HINSTANCE PhInstanceHandle; extern PPH_STRING PhLocalSystemName; @@ -87,6 +87,8 @@ 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 @@ -133,87 +135,6 @@ VOID PhInitializeFont( _In_ HWND hWnd ); -// begin_phapppub -PHAPPAPI -HFONT -NTAPI -PhGetApplicationFont( - VOID - ); - -PHAPPAPI -HWND -NTAPI -PhGetMainWndHandle( - VOID - ); - -#define PhMainWindowHandle \ - PhGetMainWndHandle() - -PHLIBAPI -PVOID -NTAPI -PhGetImageBase( - VOID - ); - -#define PhImageBaseAddress \ - PhGetImageBase() - -PHLIBAPI -ULONG -NTAPI -PhGetGlobalDpi( - VOID - ); - -#define PH_SCALE_DPI(Value) \ - PhMultiplyDivide(Value, PhGetGlobalDpi(), 96) - -PHLIBAPI -SYSTEM_BASIC_INFORMATION -NTAPI -PhGetSystemBasicInformation( - VOID - ); - -PHLIBAPI -ACCESS_MASK -NTAPI -PhProcessQueryAccess( - VOID - ); - -PHLIBAPI -ACCESS_MASK -NTAPI -PhProcessAllAccess( - VOID - ); - -PHLIBAPI -ACCESS_MASK -NTAPI -PhThreadQueryAccess( - VOID - ); - -PHLIBAPI -ACCESS_MASK -NTAPI -PhThreadSetAccess( - VOID - ); - -PHLIBAPI -ACCESS_MASK -NTAPI -PhThreadAllAccess( - VOID - ); -// end_phapppub - // plugin extern PH_AVL_TREE PhPluginsByName; @@ -290,6 +211,7 @@ typedef struct _PH_LOG_ENTRY } PH_LOG_ENTRY, *PPH_LOG_ENTRY; extern PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; +PHAPPAPI extern PH_CALLBACK PhLoggedCallback; // phapppub VOID PhLogInitialization( VOID @@ -721,7 +643,7 @@ PhCreateCommonFont( return NULL; fontHandle = CreateFont( - -PhMultiplyDivideSigned(Size, PhGetGlobalDpi(), 72), + -PhMultiplyDivideSigned(Size, PhGlobalDpi, 72), 0, 0, 0, diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index cc2de419a98c..68ea5602ede1 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -44,19 +44,6 @@ typedef enum _PH_GENERAL_CALLBACK 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] - GeneralCallbackLoggedEvent = 34, // PPH_LOG_ENTRY data [provider thread] - GeneralCallbackProcessProviderAdded = 35, // PPH_PROCESS_ITEM data [provider thread] - GeneralCallbackProcessProviderModified = 36, // PPH_PROCESS_ITEM data [provider thread] - GeneralCallbackProcessProviderRemoved = 37, // PPH_PROCESS_ITEM data [provider thread] - GeneralCallbackProcessProviderUpdated = 38, // [provider thread] - GeneralCallbackServiceProviderAdded = 39, // PPH_SERVICE_ITEM data [provider thread] - GeneralCallbackServiceProviderModified = 40, // PPH_SERVICE_ITEM data [provider thread] - GeneralCallbackServiceProviderRemoved = 41, // PPH_SERVICE_ITEM data [provider thread] - GeneralCallbackServiceProviderUpdated = 42, // [provider thread] - GeneralCallbackNetworkProviderAdded = 43, // PPH_NETWORK_ITEM data [provider thread] - GeneralCallbackNetworkProviderModified = 44, // PPH_NETWORK_ITEM data [provider thread] - GeneralCallbackNetworkProviderRemoved = 45, // PPH_NETWORK_ITEM data [provider thread] - GeneralCallbackNetworkProviderUpdated = 46, // [provider thread] GeneralCallbackMaximum } PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 169268452962..f79546f534fd 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -6,6 +6,11 @@ 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; diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index d511db7cdf69..a01349d8005e 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -3,6 +3,11 @@ 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 diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index b5334c064109..a57862b1c614 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -21,10 +21,11 @@ */ #include -#include + #include PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; +PHAPPAPI PH_CALLBACK_DECLARE(PhLoggedCallback); VOID PhLogInitialization( VOID @@ -154,7 +155,7 @@ VOID PhpLogEntry( if (oldEntry) PhpFreeLogEntry(oldEntry); - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), Entry); + PhInvokeCallback(&PhLoggedCallback, Entry); } VOID PhClearLogEntries( diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 2be84a749e1e..0a9b71a94b62 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -21,7 +21,6 @@ */ #include -#include #include #include @@ -188,7 +187,7 @@ INT_PTR CALLBACK PhpLogDlgProc( Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOSCROLL), BST_CHECKED); - PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), LoggedCallback, NULL, &LoggedRegistration); + PhRegisterCallback(&PhLoggedCallback, LoggedCallback, NULL, &LoggedRegistration); PhpUpdateLogList(); ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); } @@ -200,7 +199,7 @@ INT_PTR CALLBACK PhpLogDlgProc( PhDeleteLayoutManager(&WindowLayoutManager); - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), &LoggedRegistration); + PhUnregisterCallback(&PhLoggedCallback, &LoggedRegistration); PhUnregisterDialog(PhLogWindowHandle); PhLogWindowHandle = NULL; } diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index f25656fa2284..3d8b82255a96 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -77,7 +77,7 @@ VOID PhpEnablePrivileges( PPH_STRING PhApplicationDirectory; PPH_STRING PhApplicationFileName; -HFONT PhApplicationFont; +PHAPPAPI HFONT PhApplicationFont; PPH_STRING PhCurrentUserName = NULL; HINSTANCE PhInstanceHandle; PPH_STRING PhLocalSystemName = NULL; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index b6b7a5d9c4c6..3d4c8539dd18 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -57,7 +57,7 @@ #define RUNAS_MODE_ADMIN 1 #define RUNAS_MODE_LIMITED 2 -HWND PhMainWndHandle; +PHAPPAPI HWND PhMainWndHandle; BOOLEAN PhMainWndExiting = FALSE; HMENU PhMainWndMenuHandle; @@ -342,12 +342,12 @@ BOOLEAN PhMwpInitializeWindowClass( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(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 = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); if (!RegisterClassEx(&wcex)) return FALSE; diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 093db01c7a5b..6f73a3f3d918 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -21,7 +21,6 @@ */ #include -#include #include #include @@ -172,7 +171,7 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( { case WM_INITDIALOG: { - PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); + PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); PhpUpdateMemoryListInfo(hwndDlg); PhLoadWindowPlacementFromSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); @@ -184,7 +183,7 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( PhUnregisterDialog(hwndDlg); PhSaveWindowPlacementToSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedRegistration); UnregisterDialogFunction(hwndDlg); PhMemoryListsWindowHandle = NULL; diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index b3e2f4105b47..01d139a083e8 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -110,11 +110,11 @@ VOID PhPinMiniInformation( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; - wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( @@ -337,7 +337,7 @@ VOID PhMipContainerOnShowWindow( PhMipMessageLoopFilterEntry = PhRegisterMessageLoopFilter(PhMipMessageLoopFilter, NULL); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, PhMipUpdateHandler, NULL, &ProcessesUpdatedRegistration @@ -357,7 +357,7 @@ VOID PhMipContainerOnShowWindow( PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE); PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, &ProcessesUpdatedRegistration ); @@ -448,10 +448,10 @@ VOID PhMipOnInitDialog( HICON cog; HICON pin; - cog = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); + cog = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COG)); SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); - pin = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PIN)); + pin = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PIN)); SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); diff --git a/ProcessHacker/mwpgnet.c b/ProcessHacker/mwpgnet.c index 4229dd858ed3..c8fa1d5cd18f 100644 --- a/ProcessHacker/mwpgnet.c +++ b/ProcessHacker/mwpgnet.c @@ -62,25 +62,25 @@ BOOLEAN PhMwpNetworkPageCallback( PhMwpNetworkPage = Page; PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderAdded), + &PhNetworkItemAddedEvent, PhMwpNetworkItemAddedHandler, NULL, &NetworkItemAddedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderModified), + &PhNetworkItemModifiedEvent, PhMwpNetworkItemModifiedHandler, NULL, &NetworkItemModifiedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderRemoved), + &PhNetworkItemRemovedEvent, PhMwpNetworkItemRemovedHandler, NULL, &NetworkItemRemovedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), + &PhNetworkItemsUpdatedEvent, PhMwpNetworkItemsUpdatedHandler, NULL, &NetworkItemsUpdatedRegistration diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 6d0fbad59d2b..11307c937294 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -70,25 +70,25 @@ BOOLEAN PhMwpProcessesPageCallback( PhMwpProcessesPage = Page; PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderAdded), + &PhProcessAddedEvent, PhMwpProcessAddedHandler, NULL, &ProcessAddedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderModified), + &PhProcessModifiedEvent, PhMwpProcessModifiedHandler, NULL, &ProcessModifiedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderRemoved), + &PhProcessRemovedEvent, PhMwpProcessRemovedHandler, NULL, &ProcessRemovedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, PhMwpProcessesUpdatedHandler, NULL, &ProcessesUpdatedRegistration diff --git a/ProcessHacker/mwpgsrv.c b/ProcessHacker/mwpgsrv.c index f9e1ff1832a7..866ca49dc2e3 100644 --- a/ProcessHacker/mwpgsrv.c +++ b/ProcessHacker/mwpgsrv.c @@ -63,25 +63,25 @@ BOOLEAN PhMwpServicesPageCallback( PhMwpServicesPage = Page; PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderAdded), + &PhServiceAddedEvent, PhMwpServiceAddedHandler, NULL, &ServiceAddedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderModified), + &PhServiceModifiedEvent, PhMwpServiceModifiedHandler, NULL, &ServiceModifiedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderRemoved), + &PhServiceRemovedEvent, PhMwpServiceRemovedHandler, NULL, &ServiceRemovedRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderUpdated), + &PhServicesUpdatedEvent, PhMwpServicesUpdatedHandler, NULL, &ServicesUpdatedRegistration diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index 1247c49721d1..d3cc1187db6a 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -95,6 +94,11 @@ 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; @@ -499,7 +503,7 @@ VOID PhNetworkProviderUpdate( if (!found) { - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderRemoved), *networkItem); + PhInvokeCallback(&PhNetworkItemRemovedEvent, *networkItem); if (!connectionsToRemove) connectionsToRemove = PhCreateList(2); @@ -662,7 +666,7 @@ VOID PhNetworkProviderUpdate( PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); // Raise the network item added event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderAdded), networkItem); + PhInvokeCallback(&PhNetworkItemAddedEvent, networkItem); } else { @@ -706,7 +710,7 @@ VOID PhNetworkProviderUpdate( if (modified) { // Raise the network item modified event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderModified), networkItem); + PhInvokeCallback(&PhNetworkItemModifiedEvent, networkItem); } PhDereferenceObject(networkItem); @@ -715,7 +719,7 @@ VOID PhNetworkProviderUpdate( PhFree(connections); - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), NULL); + PhInvokeCallback(&PhNetworkItemsUpdatedEvent, NULL); } PWSTR PhGetProtocolTypeName( diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 7ad86bbb1486..729f81412e78 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -113,7 +113,7 @@ VOID PhNfLoadStage2( } PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, PhNfpProcessesUpdatedHandler, NULL, &PhNfpProcessesUpdatedRegistration diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index efb22a49c654..8f30f4e9da33 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -264,30 +264,30 @@ VOID PhLoadPlugins( // In certain startup modes we want to ignore all plugin load errors. if (LoadErrors && LoadErrors->Count != 0 && !PhStartupParameters.PhSvc) { - PH_STRING_BUILDER stringBuilder; + PH_STRING_BUILDER sb; ULONG i; PPHP_PLUGIN_LOAD_ERROR loadError; PPH_STRING baseName; - PhInitializeStringBuilder(&stringBuilder, 100); + 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(&stringBuilder, L"%s: %s\n", + PhAppendFormatStringBuilder(&sb, L"%s: %s\n", baseName->Buffer, PhGetStringOrDefault(loadError->ErrorMessage, L"An unknown error occurred.")); PhDereferenceObject(baseName); } - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); + PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); - if (PhShowError2( + if (PhShowMessage( NULL, - L"Unable to load the following plugin(s):", - stringBuilder.String->Buffer - )) + MB_ICONERROR | MB_YESNO, + sb.String->Buffer + ) == IDYES) { ULONG i; @@ -300,7 +300,7 @@ VOID PhLoadPlugins( } } - PhDeleteStringBuilder(&stringBuilder); + PhDeleteStringBuilder(&sb); } // When we loaded settings before, we didn't know about plugin settings, so they diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 06ba7e4c6a69..9445050002f7 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -179,6 +179,11 @@ 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; @@ -1896,7 +1901,7 @@ VOID PhFlushProcessQueryData( // which will lead to very bad things happening. PhAcquireQueuedLockExclusive(&data->ProcessItem->RemoveLock); if (!(data->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)) - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderModified), data->ProcessItem); + PhInvokeCallback(&PhProcessModifiedEvent, data->ProcessItem); PhReleaseQueuedLockExclusive(&data->ProcessItem->RemoveLock); } else @@ -2171,7 +2176,7 @@ VOID PhProcessProviderUpdate( // Raise the process removed event. // See PhFlushProcessQueryData for why we need to lock here. PhAcquireQueuedLockExclusive(&processItem->RemoveLock); - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderRemoved), processItem); + PhInvokeCallback(&PhProcessRemovedEvent, processItem); PhReleaseQueuedLockExclusive(&processItem->RemoveLock); if (!processesToRemove) @@ -2293,7 +2298,7 @@ VOID PhProcessProviderUpdate( PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); // Raise the process added event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderAdded), processItem); + PhInvokeCallback(&PhProcessAddedEvent, processItem); processItem->AddedEventSent = TRUE; // (Ref: for the process item being in the hashtable.) @@ -2456,7 +2461,7 @@ VOID PhProcessProviderUpdate( if (modified) { - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderModified), processItem); + PhInvokeCallback(&PhProcessModifiedEvent, processItem); } // No reference added by PhpLookupProcessItem. @@ -2557,7 +2562,7 @@ VOID PhProcessProviderUpdate( } } - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), NULL); + PhInvokeCallback(&PhProcessesUpdatedEvent, NULL); runCount++; } diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index dbcb2c98019a..486a3db8841c 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -186,7 +186,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( 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(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); + (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER))); SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)context->FileIcon, 0); diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 76318e4492e0..30b2115d2695 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -147,8 +147,8 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( HICON folder; HICON magnifier; - folder = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER)); - magnifier = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_MAGNIFIER)); + folder = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER)); + magnifier = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_MAGNIFIER)); SET_BUTTON_ICON(IDC_INSPECT, magnifier); SET_BUTTON_ICON(IDC_OPENFILENAME, folder); diff --git a/ProcessHacker/prpgperf.c b/ProcessHacker/prpgperf.c index d525919ab666..9efb80e93072 100644 --- a/ProcessHacker/prpgperf.c +++ b/ProcessHacker/prpgperf.c @@ -21,7 +21,6 @@ */ #include -#include #include #include @@ -75,7 +74,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( performanceContext->WindowHandle = hwndDlg; PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, PerformanceUpdateHandler, performanceContext, &performanceContext->ProcessesUpdatedRegistration @@ -114,7 +113,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhDeleteGraphState(&performanceContext->IoGraphState); PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, &performanceContext->ProcessesUpdatedRegistration ); PhFree(performanceContext); diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 4953d401b699..97b0127b4ea9 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -21,7 +21,6 @@ */ #include -#include #include #include @@ -247,7 +246,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, StatisticsUpdateHandler, statisticsContext, &statisticsContext->ProcessesUpdatedRegistration @@ -259,7 +258,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( case WM_DESTROY: { PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, &statisticsContext->ProcessesUpdatedRegistration ); diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index cf23dd2cccc2..53e2cf7797e5 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -639,7 +639,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PhThreadProviderInitialUpdate(threadsContext->Provider); PhRegisterThreadProvider(threadsContext->Provider, &threadsContext->ProviderRegistration); - SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); + SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER))); } break; case WM_DESTROY: diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 3596d3176853..a53d1f769498 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -21,7 +21,6 @@ */ #include -#include #include #include @@ -230,7 +229,7 @@ INT_PTR CALLBACK PhpServicesPageProc( ULONG i; PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderModified), + &PhServiceModifiedEvent, ServiceModifiedHandler, servicesContext, &servicesContext->ModifiedEventRegistration @@ -281,7 +280,7 @@ INT_PTR CALLBACK PhpServicesPageProc( PhFree(servicesContext->Services); PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceProviderModified), + &PhServiceModifiedEvent, &servicesContext->ModifiedEventRegistration ); diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 1e2eaeb655df..003e86f51e31 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -689,10 +689,10 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( if (!ServiceIconsLoaded) { - ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATION)); - ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); - ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); - ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COGGO)); + 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; } diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index b716fa37a141..18f258c7aa3d 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -94,6 +93,11 @@ 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; @@ -575,7 +579,7 @@ VOID PhServiceProviderUpdate( } // Raise the service removed event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderRemoved), *serviceItem); + PhInvokeCallback(&PhServiceRemovedEvent, *serviceItem); if (!servicesToRemove) servicesToRemove = PhCreateList(2); @@ -650,7 +654,7 @@ VOID PhServiceProviderUpdate( PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); // Raise the service added event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderAdded), serviceItem); + PhInvokeCallback(&PhServiceAddedEvent, serviceItem); } else { @@ -754,7 +758,7 @@ VOID PhServiceProviderUpdate( } // Raise the service modified event. - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderModified), &serviceModifiedData); + PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); } } } @@ -763,7 +767,7 @@ VOID PhServiceProviderUpdate( PhFree(services); UpdateEnd: - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderUpdated), NULL); + PhInvokeCallback(&PhServicesUpdatedEvent, NULL); runCount++; } diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index ee2feb3038ad..5dcbb236ea54 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -281,8 +281,8 @@ VOID PhSipOnInitDialog( VOID ) { - SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER))); PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); @@ -291,7 +291,7 @@ VOID PhSipOnInitDialog( PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, PhSipSysInfoUpdateHandler, NULL, &ProcessesUpdatedRegistration @@ -313,7 +313,7 @@ VOID PhSipOnDestroy( ) { PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, &ProcessesUpdatedRegistration ); diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index ac616f887e03..8f7a60b85b6a 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -27,7 +27,6 @@ */ #include -#include #include #include @@ -213,7 +212,7 @@ VOID PhRegisterThreadProvider( ) { PhReferenceObject(ThreadProvider); - PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); + PhRegisterCallback(&PhProcessesUpdatedEvent, PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); } VOID PhUnregisterThreadProvider( @@ -221,7 +220,7 @@ VOID PhUnregisterThreadProvider( _In_ PPH_CALLBACK_REGISTRATION CallbackRegistration ) { - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), CallbackRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, CallbackRegistration); PhDereferenceObject(ThreadProvider); } diff --git a/phlib/global.c b/phlib/global.c index 34c6ddf1eaf4..fb8a44453ab0 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -38,19 +38,20 @@ VOID PhInitializeWindowsVersion( VOID ); -PVOID PhLibImageBase; -PWSTR PhApplicationName = L"Application"; -ULONG PhGlobalDpi = 96; -PVOID PhHeapHandle; -RTL_OSVERSIONINFOEXW PhOsVersion; -SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; -ULONG WindowsVersion; - -ACCESS_MASK ProcessQueryAccess; -ACCESS_MASK ProcessAllAccess; -ACCESS_MASK ThreadQueryAccess; -ACCESS_MASK ThreadSetAccess; -ACCESS_MASK ThreadAllAccess; +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 @@ -236,10 +237,3 @@ static VOID PhInitializeWindowsVersion( ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; } } - -ULONG PhWindowsVersion( - VOID - ) -{ - return WindowsVersion; -} \ No newline at end of file diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index d163c60ecaed..75892b856fe8 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -7,19 +7,47 @@ extern "C" { #define _User_set_ -extern _User_set_ PVOID PhLibImageBase; -extern _User_set_ PWSTR PhApplicationName; -extern _User_set_ ULONG PhGlobalDpi; -extern PVOID PhHeapHandle; -extern RTL_OSVERSIONINFOEXW PhOsVersion; -extern SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; -extern ULONG WindowsVersion; - -extern ACCESS_MASK ProcessQueryAccess; -extern ACCESS_MASK ProcessAllAccess; -extern ACCESS_MASK ThreadQueryAccess; -extern ACCESS_MASK ThreadSetAccess; -extern ACCESS_MASK ThreadAllAccess; +PHLIBAPI extern _User_set_ PVOID PhLibImageBase; + +PHLIBAPI extern _User_set_ PWSTR PhApplicationName; +PHLIBAPI extern _User_set_ ULONG PhGlobalDpi; +PHLIBAPI extern PVOID PhHeapHandle; +PHLIBAPI extern RTL_OSVERSIONINFOEXW PhOsVersion; +PHLIBAPI extern SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; +PHLIBAPI extern ULONG WindowsVersion; + +PHLIBAPI extern ACCESS_MASK ProcessQueryAccess; +PHLIBAPI extern ACCESS_MASK ProcessAllAccess; +PHLIBAPI extern ACCESS_MASK ThreadQueryAccess; +PHLIBAPI extern ACCESS_MASK ThreadSetAccess; +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_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 + +#ifdef DEBUG +#define dprintf(format, ...) DbgPrint(format, __VA_ARGS__) +#else +#define dprintf(format, ...) +#endif // global @@ -74,23 +102,6 @@ PhIsExecutingInWow64( ); #endif - -PHLIBAPI -ULONG -NTAPI -PhWindowsVersion( - VOID - ); - -#define WINDOWS_HAS_CONSOLE_HOST (PhWindowsVersion() >= WINDOWS_7) -#define WINDOWS_HAS_CYCLE_TIME (PhWindowsVersion() >= WINDOWS_VISTA) -#define WINDOWS_HAS_IFILEDIALOG (PhWindowsVersion() >= WINDOWS_VISTA) -#define WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID (PhWindowsVersion() >= WINDOWS_VISTA) -#define WINDOWS_HAS_IMMERSIVE (PhWindowsVersion() >= WINDOWS_8) -#define WINDOWS_HAS_LIMITED_ACCESS (PhWindowsVersion() >= WINDOWS_VISTA) -#define WINDOWS_HAS_SERVICE_TAGS (PhWindowsVersion() >= WINDOWS_VISTA) -#define WINDOWS_HAS_UAC (PhWindowsVersion() >= WINDOWS_VISTA) - #ifdef __cplusplus } #endif diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 82e4b306303e..55e36cf5160f 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -8,14 +8,6 @@ #include #include -// Debugging - -#ifdef DEBUG -#define dprintf(format, ...) DbgPrint(format, __VA_ARGS__) -#else -#define dprintf(format, ...) -#endif - // Memory #define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) diff --git a/phnt/include/phnt.h b/phnt/include/phnt.h index 1d7dffcca31e..42bf2a9d8007 100644 --- a/phnt/include/phnt.h +++ b/phnt/include/phnt.h @@ -24,16 +24,6 @@ #define PHNT_MODE_USER 1 // Version -#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 PHNT_WIN2K 50 #define PHNT_WINXP 51 #define PHNT_WS03 52 diff --git a/plugins/DotNetTools/clrsup.c b/plugins/DotNetTools/clrsup.c index afc00b24cbf5..3fca660cb6ce 100644 --- a/plugins/DotNetTools/clrsup.c +++ b/plugins/DotNetTools/clrsup.c @@ -283,7 +283,7 @@ ICLRDataTarget *DnCLRDataTarget_Create( HANDLE processHandle; BOOLEAN isWow64; - if (!NT_SUCCESS(PhOpenProcess(&processHandle, PhProcessQueryAccess() | PROCESS_VM_READ, ProcessId))) + if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId))) return NULL; #ifdef _WIN64 diff --git a/plugins/DotNetTools/perfpage.c b/plugins/DotNetTools/perfpage.c index 1b3094966b8c..c5cf54c72bcf 100644 --- a/plugins/DotNetTools/perfpage.c +++ b/plugins/DotNetTools/perfpage.c @@ -765,7 +765,7 @@ INT_PTR CALLBACK DotNetPerfPageDlgProc( if (NT_SUCCESS(PhOpenProcess( &context->ProcessHandle, - PROCESS_VM_READ | PhProcessQueryAccess() | PROCESS_DUP_HANDLE | SYNCHRONIZE, + PROCESS_VM_READ | ProcessQueryAccess | PROCESS_DUP_HANDLE | SYNCHRONIZE, context->ProcessItem->ProcessId ))) { diff --git a/plugins/DotNetTools/stackext.c b/plugins/DotNetTools/stackext.c index 30528fb5b856..13ef6a3aca5c 100644 --- a/plugins/DotNetTools/stackext.c +++ b/plugins/DotNetTools/stackext.c @@ -93,7 +93,7 @@ VOID ProcessThreadStackControl( context->ThreadHandle = Control->u.Initializing.ThreadHandle; #if _WIN64 - if (NT_SUCCESS(PhOpenProcess(&processHandle, PhProcessQueryAccess(), Control->u.Initializing.ProcessId))) + if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, Control->u.Initializing.ProcessId))) { PhGetProcessIsWow64(processHandle, &context->IsWow64); NtClose(processHandle); diff --git a/plugins/ExtendedNotifications/filelog.c b/plugins/ExtendedNotifications/filelog.c index e0c3d10163b8..e1cc06587719 100644 --- a/plugins/ExtendedNotifications/filelog.c +++ b/plugins/ExtendedNotifications/filelog.c @@ -55,7 +55,7 @@ VOID FileLogInitialization( if (NT_SUCCESS(status)) { PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackLoggedEvent), + &PhLoggedCallback, LoggedCallback, NULL, &LoggedCallbackRegistration diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index 335c99fff0f1..c36e3b8f38f7 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -57,28 +57,28 @@ VOID NTAPI MenuItemCallback( { case ID_SERVICE_GOTOSERVICE: { - ProcessHacker_SelectTabPage(PhMainWindowHandle, 1); - ProcessHacker_SelectServiceItem(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); + ProcessHacker_SelectTabPage(PhMainWndHandle, 1); + ProcessHacker_SelectServiceItem(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_START: { - PhUiStartService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiStartService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_CONTINUE: { - PhUiContinueService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiContinueService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_PAUSE: { - PhUiPauseService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiPauseService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_STOP: { - PhUiStopService(PhMainWindowHandle, (PPH_SERVICE_ITEM)menuItem->Context); + PhUiStopService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); } break; case ID_SERVICE_RESTART: @@ -89,7 +89,7 @@ VOID NTAPI MenuItemCallback( if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS)) { - EsRestartServiceWithProgress(PhMainWindowHandle, serviceItem, serviceHandle); + EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle); CloseServiceHandle(serviceHandle); } else @@ -100,7 +100,7 @@ VOID NTAPI MenuItemCallback( if (win32Result != 0) { PhShowStatus( - PhMainWindowHandle, + PhMainWndHandle, PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer, 0, win32Result @@ -348,7 +348,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (PhWindowsVersion() >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); @@ -362,7 +362,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (PhWindowsVersion() >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); diff --git a/plugins/ExtendedServices/other.c b/plugins/ExtendedServices/other.c index 7c94a31beacd..5b7957afa653 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -365,7 +365,7 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_PROTECTION), EspServiceLaunchProtectedStrings, sizeof(EspServiceLaunchProtectedStrings) / sizeof(PWSTR)); - if (PhWindowsVersion() < WINDOWS_8_1) + if (WindowsVersion < WINDOWS_8_1) EnableWindow(GetDlgItem(hwndDlg, IDC_PROTECTION), FALSE); SetDlgItemText(hwndDlg, IDC_SERVICESID, diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index 245bce8a6372..b917b0334498 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -214,7 +214,7 @@ NTSTATUS EspLoadRecoveryInfo( // Enable actions for stops with errors // This is Vista and above only. - if (PhWindowsVersion() >= WINDOWS_VISTA && QueryServiceConfig2( + if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2( serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (BYTE *)&failureActionsFlag, @@ -320,7 +320,7 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( { SetDlgItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0"); - if (PhWindowsVersion() >= WINDOWS_VISTA) + if (WindowsVersion >= WINDOWS_VISTA) { context->EnableFlagCheckBox = TRUE; EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE); diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index daf90924b520..b8b1ae72e953 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -62,7 +62,7 @@ VOID EtInitializeDiskTab( memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); PhInitializeStringRef(&page.Name, L"Disk"); page.Callback = EtpDiskPageCallback; - DiskPage = ProcessHacker_CreateTabPage(PhMainWindowHandle, &page); + DiskPage = ProcessHacker_CreateTabPage(PhMainWndHandle, &page); if (ToolStatusInterface) { @@ -101,7 +101,7 @@ BOOLEAN EtpDiskPageCallback( 0, 3, 3, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -115,7 +115,7 @@ BOOLEAN EtpDiskPageCallback( *(HWND *)Parameter1 = CreateDialog( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_DISKTABERROR), - PhMainWindowHandle, + PhMainWndHandle, EtpDiskTabErrorDialogProc ); return TRUE; @@ -829,17 +829,17 @@ VOID EtHandleDiskCommand( if ((processNode = PhFindProcessNode(diskItem->ProcessId)) && processNode->ProcessItem->CreateTime.QuadPart == diskItem->ProcessRecord->CreateTime.QuadPart) { - ProcessHacker_SelectTabPage(PhMainWindowHandle, 0); + ProcessHacker_SelectTabPage(PhMainWndHandle, 0); PhSelectAndEnsureVisibleProcessNode(processNode); } else { - PhShowProcessRecordDialog(PhMainWindowHandle, diskItem->ProcessRecord); + PhShowProcessRecordDialog(PhMainWndHandle, diskItem->ProcessRecord); } } else { - PhShowError(PhMainWindowHandle, L"The process does not exist."); + PhShowError(PhMainWndHandle, L"The process does not exist."); } PhDereferenceObject(diskItem); @@ -852,7 +852,7 @@ VOID EtHandleDiskCommand( if (diskItem) { - PhShellExploreFile(PhMainWindowHandle, diskItem->FileNameWin32->Buffer); + PhShellExploreFile(PhMainWndHandle, diskItem->FileNameWin32->Buffer); } } break; @@ -867,7 +867,7 @@ VOID EtHandleDiskCommand( if (diskItem) { - PhShellProperties(PhMainWindowHandle, diskItem->FileNameWin32->Buffer); + PhShellProperties(PhMainWndHandle, diskItem->FileNameWin32->Buffer); } } break; @@ -937,7 +937,7 @@ VOID EtShowDiskContextMenu( item = PhShowEMenu( menu, - PhMainWindowHandle, + PhMainWndHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, Location.x, @@ -963,7 +963,7 @@ VOID NTAPI EtpDiskItemAddedHandler( PET_DISK_ITEM diskItem = (PET_DISK_ITEM)Parameter; PhReferenceObject(diskItem); - ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemAdded, diskItem); + ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemAdded, diskItem); } VOID NTAPI EtpDiskItemModifiedHandler( @@ -971,7 +971,7 @@ VOID NTAPI EtpDiskItemModifiedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemModified, (PET_DISK_ITEM)Parameter); + ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemModified, (PET_DISK_ITEM)Parameter); } VOID NTAPI EtpDiskItemRemovedHandler( @@ -979,7 +979,7 @@ VOID NTAPI EtpDiskItemRemovedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemRemoved, (PET_DISK_ITEM)Parameter); + ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemRemoved, (PET_DISK_ITEM)Parameter); } VOID NTAPI EtpDiskItemsUpdatedHandler( @@ -987,7 +987,7 @@ VOID NTAPI EtpDiskItemsUpdatedHandler( _In_opt_ PVOID Context ) { - ProcessHacker_Invoke(PhMainWindowHandle, EtpOnDiskItemsUpdated, NULL); + ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemsUpdated, NULL); } VOID NTAPI EtpOnDiskItemAdded( @@ -1136,10 +1136,10 @@ INT_PTR CALLBACK EtpDiskTabErrorDialogProc( switch (LOWORD(wParam)) { case IDC_RESTART: - ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); if (PhShellProcessHacker( - PhMainWindowHandle, + PhMainWndHandle, L"-v -selecttab Disk", SW_SHOW, PH_SHELL_EXECUTE_ADMIN, @@ -1148,11 +1148,11 @@ INT_PTR CALLBACK EtpDiskTabErrorDialogProc( NULL )) { - ProcessHacker_Destroy(PhMainWindowHandle); + ProcessHacker_Destroy(PhMainWndHandle); } else { - ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); + ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); } break; diff --git a/plugins/ExtendedTools/etwdisk.c b/plugins/ExtendedTools/etwdisk.c index 237e34715d71..c8512f20a01d 100644 --- a/plugins/ExtendedTools/etwdisk.c +++ b/plugins/ExtendedTools/etwdisk.c @@ -95,7 +95,7 @@ VOID EtInitializeDiskInformation( EtStartEtwRundown(); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, EtpDiskProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration diff --git a/plugins/ExtendedTools/etwmon.c b/plugins/ExtendedTools/etwmon.c index 402b3417a27e..7c4823ae21ba 100644 --- a/plugins/ExtendedTools/etwmon.c +++ b/plugins/ExtendedTools/etwmon.c @@ -117,7 +117,7 @@ VOID EtStartEtwSession( ULONG result; ULONG bufferSize; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { EtpActualKernelLoggerName = &EtpPrivateKernelLoggerName; EtpActualSessionGuid = &ProcessHackerGuid; @@ -146,7 +146,7 @@ VOID EtStartEtwSession( EtpTraceProperties->LogFileNameOffset = 0; EtpTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) EtpTraceProperties->LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE; result = StartTrace(&EtpSessionHandle, EtpActualKernelLoggerName->Buffer, EtpTraceProperties); @@ -240,7 +240,7 @@ VOID NTAPI EtpEtwEventCallback( { DiskIo_TypeGroup1 *data = EventRecord->UserData; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { diskEvent.ClientId.UniqueThread = UlongToHandle(data->IssuingThreadId); diskEvent.ClientId.UniqueProcess = EtThreadIdToProcessId(diskEvent.ClientId.UniqueThread); @@ -387,7 +387,7 @@ NTSTATUS EtpEtwMonitorThreadStart( TRACEHANDLE traceHandle; // See comment in EtEtwProcessesUpdatedCallback. - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) EtUpdateProcessInformation(); memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); diff --git a/plugins/ExtendedTools/etwprprp.c b/plugins/ExtendedTools/etwprprp.c index ba8059a07b8e..1e87ffbd4672 100644 --- a/plugins/ExtendedTools/etwprprp.c +++ b/plugins/ExtendedTools/etwprprp.c @@ -148,9 +148,9 @@ VOID EtwDiskNetworkLayoutGraphs( 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); + 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; @@ -326,7 +326,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( EtwDiskNetworkUpdatePanel(context); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, EtwDiskNetworkUpdateHandler, context, &context->ProcessesUpdatedRegistration @@ -352,7 +352,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( if (context->PanelHandle) DestroyWindow(context->PanelHandle); - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); PhFree(context); PhPropPageDlgProcDestroy(hwndDlg); @@ -394,7 +394,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( )); hdc = Graph_GetBufferedContext(context->DiskGraphHandle); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText(hdc, drawInfo, &context->DiskGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } @@ -462,7 +462,7 @@ INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( )); hdc = Graph_GetBufferedContext(context->NetworkGraphHandle); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText(hdc, drawInfo, &context->NetworkGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/ExtendedTools/etwstat.c b/plugins/ExtendedTools/etwstat.c index 663613de863e..33ee65dd33ee 100644 --- a/plugins/ExtendedTools/etwstat.c +++ b/plugins/ExtendedTools/etwstat.c @@ -85,13 +85,13 @@ VOID EtEtwStatisticsInitialization( PhInitializeCircularBuffer_ULONG(&EtMaxNetworkHistory, sampleCount); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, EtEtwProcessesUpdatedCallback, NULL, &EtpProcessesUpdatedCallbackRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), + &PhNetworkItemsUpdatedEvent, EtEtwNetworkItemsUpdatedCallback, NULL, &EtpNetworkItemsUpdatedCallbackRegistration @@ -224,7 +224,7 @@ VOID NTAPI EtEtwProcessesUpdatedCallback( // 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 (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) EtUpdateProcessInformation(); // ETW is extremely lazy when it comes to flushing buffers, so we must do it @@ -357,7 +357,7 @@ VOID NTAPI EtEtwNetworkItemsUpdatedCallback( { // Values have changed. Invalidate the network node. PhReferenceObject(block->NetworkItem); - ProcessHacker_Invoke(PhMainWindowHandle, EtpInvalidateNetworkNode, block->NetworkItem); + ProcessHacker_Invoke(PhMainWndHandle, EtpInvalidateNetworkNode, block->NetworkItem); } listEntry = listEntry->Flink; diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index c5201b3fabc5..967f6d30f356 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -27,6 +27,10 @@ extern HWND NetworkTreeNewHandle; #define SETTING_NAME_UNLOADED_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") #define SETTING_NAME_UNLOADED_COLUMNS (PLUGIN_NAME L".UnloadedListColumns") +#define ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) +#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) +#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) + // Graph update message #define UPDATE_MSG (WM_APP + 1) diff --git a/plugins/ExtendedTools/gpumon.c b/plugins/ExtendedTools/gpumon.c index 4905e6fe2374..ca608783baa3 100644 --- a/plugins/ExtendedTools/gpumon.c +++ b/plugins/ExtendedTools/gpumon.c @@ -97,7 +97,7 @@ VOID EtGpuMonitorInitialization( } PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, EtGpuProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration @@ -244,7 +244,7 @@ BOOLEAN EtpInitializeD3DStatistics( ULONG64 commitLimit; ULONG aperture; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit; aperture = queryStatistics.QueryResult.SegmentInformation.Aperture; @@ -418,7 +418,7 @@ VOID EtpUpdateSegmentInformation( { ULONG64 bytesCommitted; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } @@ -436,7 +436,7 @@ VOID EtpUpdateSegmentInformation( { ULONG64 bytesCommitted; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident; } @@ -777,7 +777,7 @@ VOID EtQueryProcessGpuStatistics( { ULONG64 bytesCommitted; - if (PhWindowsVersion() >= WINDOWS_8) + if (WindowsVersion >= WINDOWS_8) { bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; } diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index 6b1e083f01e4..fe9467dda387 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -121,13 +121,15 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( WindowHandle = hwndDlg; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); 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); @@ -204,8 +206,6 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); EtpLoadNodeBitMap(); - - PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration); } break; case WM_DESTROY: @@ -214,7 +214,7 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( EtpSaveNodeBitMap(); - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &ProcessesUpdatedCallbackRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedCallbackRegistration); for (i = 0; i < EtGpuTotalNodeCount; i++) { diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index 6661ee3e747c..882b3c453d75 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -263,9 +263,9 @@ VOID GpuPropLayoutGraphs( 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); + 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; @@ -474,7 +474,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( GpuPropUpdatePanel(context); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, ProcessesUpdatedHandler, context, &context->ProcessesUpdatedRegistration @@ -502,7 +502,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( if (context->PanelHandle) DestroyWindow(context->PanelHandle); - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); PhFree(context); PhPropPageDlgProcDestroy(hwndDlg); @@ -543,7 +543,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->GpuGraphHandle); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText(hdc, drawInfo, &context->GpuGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } @@ -574,7 +574,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->MemGraphHandle); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText( hdc, drawInfo, @@ -630,7 +630,7 @@ INT_PTR CALLBACK EtpGpuPageDlgProc( )); hdc = Graph_GetBufferedContext(context->SharedGraphHandle); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText(hdc, drawInfo, &context->MemorySharedGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 542438278ebd..22c279729158 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -91,7 +91,7 @@ VOID NTAPI MenuItemCallback( break; case ID_PROCESS_WSWATCH: { - EtShowWsWatchDialog(PhMainWindowHandle, menuItem->Context); + EtShowWsWatchDialog(PhMainWndHandle, menuItem->Context); } break; case ID_THREAD_CANCELIO: @@ -565,13 +565,13 @@ LOGICAL DllMain( ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdated), + &PhNetworkItemsUpdatedEvent, NetworkItemsUpdatedCallback, NULL, &NetworkItemsUpdatedCallbackRegistration diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 04fd28abfce5..54d9f4b8ae27 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -305,8 +305,8 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); @@ -337,7 +337,7 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( if (PhGetIntegerPairSetting(SETTING_NAME_UNLOADED_WINDOW_POSITION).X != 0) PhLoadWindowPlacementFromSetting(SETTING_NAME_UNLOADED_WINDOW_POSITION, SETTING_NAME_UNLOADED_WINDOW_SIZE, hwndDlg); else - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); if (!EtpRefreshUnloadedDlls(hwndDlg, context)) { diff --git a/plugins/ExtendedTools/wswatch.c b/plugins/ExtendedTools/wswatch.c index 29094695fd0d..470092cb3b0d 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -389,7 +389,7 @@ static BOOLEAN NTAPI EnumGenericModulesCallback( // in Windows 7. if ( context->LoadingSymbolsForProcessId == SYSTEM_PROCESS_ID && - (ULONG_PTR)Module->BaseAddress <= PhGetSystemBasicInformation().MaximumUserModeAddress + (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress ) return TRUE; diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index ac286ad2b0be..57509cc969c0 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -58,7 +58,7 @@ NTSTATUS QueryPluginsCallbackThread( if (!(httpSessionHandle = WinHttpOpen( L"ExtraPlugins_1.0", - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index ab5c5ea929d2..b924fe09d2d9 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -243,7 +243,7 @@ INT_PTR CALLBACK CloudPluginsDlgProc( context->NormalFontHandle = PhCreateCommonFont(-14, FW_NORMAL, NULL); context->BoldFontHandle = PhCreateCommonFont(-16, FW_BOLD, NULL); - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); PhAddTreeNewFilter(GetPluginListFilterSupport(context), ProcessTreeFilterCallback, context); diff --git a/plugins/ExtraPlugins/setup/page5.c b/plugins/ExtraPlugins/setup/page5.c index 256c6a8d52dc..e703258e37b6 100644 --- a/plugins/ExtraPlugins/setup/page5.c +++ b/plugins/ExtraPlugins/setup/page5.c @@ -45,9 +45,9 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( { if ((INT)wParam == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); PhShellProcessHacker( - PhMainWindowHandle, + PhMainWndHandle, L"-v", SW_SHOW, 0, @@ -56,7 +56,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( NULL ); //PhShellProcessHacker( - // PhMainWindowHandle, + // PhMainWndHandle, // L"-plugin " PLUGIN_NAME L":INSTALL -plugin " PLUGIN_NAME L":hex64value", // SW_SHOW, // 0, @@ -64,7 +64,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( // 0, // NULL // ); - ProcessHacker_Destroy(PhMainWindowHandle); + ProcessHacker_Destroy(PhMainWndHandle); } } break; diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 02e2e37eddb7..77662132c054 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -60,8 +60,22 @@ VOID TaskDialogCreateIcons( ) { // Load the Process Hacker window icon - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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); @@ -407,7 +421,7 @@ NTSTATUS UpdateDownloadThread( // Open the HTTP session with the system proxy configuration if available if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -416,7 +430,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -451,7 +465,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_7) + if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -724,11 +738,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( // break; //case WM_NCACTIVATE: // { - // if (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) + // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) // { // if (!context->FixedWindowStyles) // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWindowHandle); + // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); // context->FixedWindowStyles = TRUE; // } diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index a68605ffbbd5..f212a04d456a 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -94,7 +94,7 @@ VOID AddRemoveDeviceChangeCallback( ) { // We get called during the plugin LoadCallback, don't do anything. - if (!PhMainWindowHandle) + if (!PhMainWndHandle) return; // Add the subclass only when disks are being monitored, remove when no longer needed. @@ -103,7 +103,7 @@ VOID AddRemoveDeviceChangeCallback( if (!SubclassActive) { // We have a disk device, subclass the main window to detect drive letter changes. - SetWindowSubclass(PhMainWindowHandle, MainWndDevicesSubclassProc, 0, 0); + SetWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0, 0); SubclassActive = TRUE; } } @@ -112,7 +112,7 @@ VOID AddRemoveDeviceChangeCallback( if (SubclassActive) { // The user has removed the last disk device, remove the subclass. - RemoveWindowSubclass(PhMainWindowHandle, MainWndDevicesSubclassProc, 0); + RemoveWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0); SubclassActive = FALSE; } } diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index fd212f6d7e64..9b2e7c4a39bd 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -259,7 +259,7 @@ VOID ShowDeviceMenu( selectedItem = PhShowEMenu( menu, - PhMainWindowHandle, + PhMainWndHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, @@ -364,7 +364,7 @@ LOGICAL DllMain( ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration diff --git a/plugins/HardwareDevices/netdetails.c b/plugins/HardwareDevices/netdetails.c index f703b9cd89d7..983a7ca071db 100644 --- a/plugins/HardwareDevices/netdetails.c +++ b/plugins/HardwareDevices/netdetails.c @@ -536,7 +536,7 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( NetAdapterAddListViewItemGroups(context->ListViewHandle); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, NetAdapterProcessesUpdatedHandler, context, &context->ProcessesUpdatedRegistration @@ -556,7 +556,7 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( break; case WM_DESTROY: { - PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); if (context->NotifyHandle) CancelMibChangeNotify2(context->NotifyHandle); diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index b82b52fd44e7..0807ba9a9e3b 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -301,7 +301,7 @@ BOOLEAN QueryNetworkDeviceInterfaceDescription( if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? deviceInstanceHandle, - PhWindowsVersion() >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, + WindowsVersion >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)deviceDescription->Buffer, &bufferSize, @@ -313,7 +313,7 @@ BOOLEAN QueryNetworkDeviceInterfaceDescription( result = CM_Get_DevNode_Property( deviceInstanceHandle, - PhWindowsVersion() >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, + WindowsVersion >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, &devicePropertyType, (PBYTE)deviceDescription->Buffer, &bufferSize, diff --git a/plugins/HardwareDevices/storage.c b/plugins/HardwareDevices/storage.c index 5c7a4472b533..2be01224b59c 100644 --- a/plugins/HardwareDevices/storage.c +++ b/plugins/HardwareDevices/storage.c @@ -918,21 +918,21 @@ BOOLEAN DiskDriveQueryFileSystemInfo( case FILESYSTEM_STATISTICS_TYPE_NTFS: case FILESYSTEM_STATISTICS_TYPE_REFS: // ReFS uses the same statistics as NTFS. { - bufferLength = sizeof(NTFS_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; + bufferLength = sizeof(NTFS_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } break; case FILESYSTEM_STATISTICS_TYPE_FAT: { - bufferLength = sizeof(FAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; + bufferLength = sizeof(FAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } break; case FILESYSTEM_STATISTICS_TYPE_EXFAT: { - bufferLength = sizeof(EXFAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhGetSystemBasicInformation().NumberOfProcessors; + bufferLength = sizeof(EXFAT_FILESYSTEM_STATISTICS) * 64 * (ULONG)PhSystemBasicInformation.NumberOfProcessors; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); } diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index 887789488e94..f2bce861c8ca 100644 --- a/plugins/NetworkTools/pages.c +++ b/plugins/NetworkTools/pages.c @@ -107,9 +107,9 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( { if ((INT)wParam == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); PhShellProcessHacker( - PhMainWindowHandle, + PhMainWndHandle, NULL, SW_SHOW, 0, @@ -117,7 +117,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( 0, NULL ); - ProcessHacker_Destroy(PhMainWindowHandle); + ProcessHacker_Destroy(PhMainWndHandle); } } break; diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index bbed74977946..0ccb364a7005 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -330,7 +330,7 @@ INT_PTR CALLBACK NetworkPingWndProc( if (PhGetIntegerPairSetting(SETTING_NAME_PING_WINDOW_POSITION).X != 0) PhLoadWindowPlacementFromSetting(SETTING_NAME_PING_WINDOW_POSITION, SETTING_NAME_PING_WINDOW_SIZE, hwndDlg); else - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); SetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer); SetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with %lu bytes of data...", @@ -339,7 +339,7 @@ INT_PTR CALLBACK NetworkPingWndProc( ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, NetworkPingUpdateHandler, context, &context->ProcessesUpdatedRegistration @@ -362,7 +362,7 @@ INT_PTR CALLBACK NetworkPingWndProc( case WM_DESTROY: { PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration ); @@ -447,7 +447,7 @@ INT_PTR CALLBACK NetworkPingWndProc( PhFormatString(L"%lu ms", context->CurrentPingMs) ); - SelectObject(hdc, PhGetApplicationFont()); + SelectObject(hdc, PhApplicationFont); PhSetGraphText(hdc, drawInfo, &context->PingGraphState.Text->sr, &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); } diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 519bbf6b47f1..41550a1f123b 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -568,10 +568,7 @@ INT_PTR CALLBACK TracertDlgProc( { HANDLE tracertThread; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); Static_SetText(hwndDlg, PhaFormatString(L"Tracing %s...", context->IpAddressString)->Buffer @@ -595,7 +592,7 @@ INT_PTR CALLBACK TracertDlgProc( if (PhGetIntegerPairSetting(SETTING_NAME_TRACERT_WINDOW_POSITION).X != 0) PhLoadWindowPlacementFromSetting(SETTING_NAME_TRACERT_WINDOW_POSITION, SETTING_NAME_TRACERT_WINDOW_SIZE, hwndDlg); else - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); PhReferenceObject(context); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index c7ceba20e69f..988029df021f 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -35,14 +35,21 @@ VOID FreeUpdateContext( ) { //PhClearReference(&Context->Version); - //PhClearReference(&Context->RevVersion); + // PhClearReference(&Context->RevVersion); //PhClearReference(&Context->RelDate); - //PhClearReference(&Context->Size); + // PhClearReference(&Context->Size); //PhClearReference(&Context->Hash); - //PhClearReference(&Context->Signature); - //PhClearReference(&Context->ReleaseNotesUrl); + // PhClearReference(&Context->Signature); + // PhClearReference(&Context->ReleaseNotesUrl); //PhClearReference(&Context->SetupFilePath); - //PhClearReference(&Context->SetupFileDownloadUrl); + // PhClearReference(&Context->SetupFileDownloadUrl); + + if (Context->IconLargeHandle) + DestroyIcon(Context->IconLargeHandle); + + if (Context->IconSmallHandle) + DestroyIcon(Context->IconSmallHandle); + //PhClearReference(&Context); } @@ -50,11 +57,29 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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)Context->IconLargeHandle); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconSmallHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); } VOID TaskDialogLinkClicked( @@ -134,7 +159,7 @@ PPH_STRING QueryFwLinkUrl( if (!(httpSessionHandle = WinHttpOpen( NULL, - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -143,7 +168,7 @@ PPH_STRING QueryFwLinkUrl( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -198,7 +223,7 @@ PPH_STRING QueryFwLinkUrl( ); } - if (PhWindowsVersion() >= WINDOWS_7) + if (WindowsVersion >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_REDIRECTS; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); @@ -390,7 +415,7 @@ NTSTATUS GeoIPUpdateThread( if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -399,7 +424,7 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); @@ -428,7 +453,7 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_7) + if (WindowsVersion >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); @@ -710,7 +735,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( UpdateDialogHandle = context->DialogHandle = hwndDlg; // Center the update window on PH if it's visible else we center on the desktop. - PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); @@ -762,7 +787,7 @@ NTSTATUS GeoIPUpdateDialogThread( //info.hwnd = Parameter; //info.lpVerb = L"runas"; // - //ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + //ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); // //if (ShellExecuteEx(&info)) //{ @@ -785,7 +810,7 @@ NTSTATUS GeoIPUpdateDialogThread( // NULL // ); // - // ProcessHacker_Destroy(PhMainWindowHandle); + // ProcessHacker_Destroy(PhMainWndHandle); // } // } // @@ -793,7 +818,7 @@ NTSTATUS GeoIPUpdateDialogThread( //} //else //{ - // ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); + // ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); //} } diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index b7ea2d5a38d5..bf6e602d0270 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -396,9 +396,6 @@ INT_PTR CALLBACK NetworkOutputDlgProc( { HANDLE dialogThread; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); context->WindowHandle = hwndDlg; @@ -419,7 +416,7 @@ INT_PTR CALLBACK NetworkOutputDlgProc( if (PhGetIntegerPairSetting(SETTING_NAME_WHOIS_WINDOW_POSITION).X != 0) PhLoadWindowPlacementFromSetting(SETTING_NAME_WHOIS_WINDOW_POSITION, SETTING_NAME_WHOIS_WINDOW_SIZE, hwndDlg); else - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); if (dialogThread = PhCreateThread(0, NetworkWhoisThreadStart, (PVOID)context)) NtClose(dialogThread); diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 687ba5defef1..35ef25384a98 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -178,7 +178,13 @@ VOID NTAPI MenuItemCallback( 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(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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."; @@ -186,9 +192,9 @@ VOID NTAPI MenuItemCallback( if (SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result == IDYES) { - ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); PhShellProcessHacker( - PhMainWindowHandle, + PhMainWndHandle, L"-v", SW_SHOW, 0, @@ -196,7 +202,7 @@ VOID NTAPI MenuItemCallback( 0, NULL ); - ProcessHacker_Destroy(PhMainWindowHandle); + ProcessHacker_Destroy(PhMainWndHandle); } DestroyIcon(config.hMainIcon); diff --git a/plugins/OnlineChecks/page1.c b/plugins/OnlineChecks/page1.c index 2ce418db265f..7b8489b72c95 100644 --- a/plugins/OnlineChecks/page1.c +++ b/plugins/OnlineChecks/page1.c @@ -40,7 +40,7 @@ HRESULT CALLBACK TaskDialogProcessingCallbackProc( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); if (context->TaskbarListClass) - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_INDETERMINATE); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_INDETERMINATE); PhReferenceObject(context); PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UploadCheckThreadStart, context); diff --git a/plugins/OnlineChecks/page2.c b/plugins/OnlineChecks/page2.c index b6b972258f19..aff040808c29 100644 --- a/plugins/OnlineChecks/page2.c +++ b/plugins/OnlineChecks/page2.c @@ -45,7 +45,7 @@ HRESULT CALLBACK TaskDialogResultFoundProc( { if (context->TaskbarListClass) { - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); } } break; diff --git a/plugins/OnlineChecks/page3.c b/plugins/OnlineChecks/page3.c index 9d6fac0bc773..c35e7309c7ec 100644 --- a/plugins/OnlineChecks/page3.c +++ b/plugins/OnlineChecks/page3.c @@ -40,7 +40,7 @@ HRESULT CALLBACK TaskDialogProgressCallbackProc( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); if (context->TaskbarListClass) - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_INDETERMINATE); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_INDETERMINATE); PhReferenceObject(context); context->UploadThreadHandle = PhCreateThread(0, UploadFileThreadStart, context); diff --git a/plugins/OnlineChecks/page4.c b/plugins/OnlineChecks/page4.c index e174cb39f63c..849bdcd1e0cf 100644 --- a/plugins/OnlineChecks/page4.c +++ b/plugins/OnlineChecks/page4.c @@ -38,8 +38,8 @@ HRESULT CALLBACK TaskDialogErrorProc( { if (context->TaskbarListClass) { - ITaskbarList3_SetProgressValue(context->TaskbarListClass, PhMainWindowHandle, 1, 1); - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_ERROR); + ITaskbarList3_SetProgressValue(context->TaskbarListClass, PhMainWndHandle, 1, 1); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_ERROR); } } break; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index d0736ff8414e..803689827131 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -172,6 +172,18 @@ VOID UploadContextDeleteProcedure( 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->FileName); PhClearReference(&context->BaseFileName); @@ -193,7 +205,7 @@ VOID TaskDialogFreeContext( { // Reset Taskbar progress state(s) if (Context->TaskbarListClass) - ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); if (Context->TaskbarListClass) ITaskbarList3_SetProgressState(Context->TaskbarListClass, Context->DialogHandle, TBPF_NOPROGRESS); @@ -205,8 +217,20 @@ VOID TaskDialogCreateIcons( _In_ PUPLOAD_CONTEXT Context ) { - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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); @@ -686,7 +710,7 @@ NTSTATUS UploadFileThreadStart( { ITaskbarList3_SetProgressState( context->TaskbarListClass, - PhMainWindowHandle, + PhMainWndHandle, TBPF_NORMAL ); } @@ -890,7 +914,7 @@ NTSTATUS UploadFileThreadStart( // Reset Taskbar progress state(s) if (context->TaskbarListClass) { - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWindowHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); ITaskbarList3_SetProgressState(context->TaskbarListClass, context->DialogHandle, TBPF_NOPROGRESS); } @@ -990,7 +1014,7 @@ NTSTATUS UploadCheckThreadStart( if (!(context->HttpHandle = WinHttpOpen( userAgent->Buffer, - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -999,7 +1023,7 @@ NTSTATUS UploadCheckThreadStart( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; WinHttpSetOption(context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); @@ -1228,7 +1252,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( context->DialogHandle = hwndDlg; // Center the update window on PH if it's visible else we center on the desktop. - PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index bc93f9df57bd..642ce0ddd9f3 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -317,7 +317,7 @@ PSTR VirusTotalSendHttpRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -326,7 +326,7 @@ PSTR VirusTotalSendHttpRequest( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); @@ -461,7 +461,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( if (!(httpSessionHandle = WinHttpOpen( userAgent->Buffer, - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -470,7 +470,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); diff --git a/plugins/Plugins.props b/plugins/Plugins.props index 88e4574b2c86..f47cc8f6e6d0 100644 --- a/plugins/Plugins.props +++ b/plugins/Plugins.props @@ -31,8 +31,7 @@ true - delaylib.lib;ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) - ProcessHacker.exe;%(DelayLoadDLLs) + ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) true 6.01 Windows @@ -77,18 +76,16 @@ StreamingSIMDExtensions - ..\..\sdk\lib\i386;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\..\sdk\lib\i386;%(AdditionalLibraryDirectories) MachineX86 - ___delayLoadHelper2@8 - ..\..\sdk\lib\amd64;..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ..\..\sdk\lib\amd64;%(AdditionalLibraryDirectories) MachineX64 - __delayLoadHelper2 diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index 9b5ca897757d..cd4dbd29034d 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -310,7 +310,7 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); context->DialogHandle = hwndDlg; context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); @@ -627,7 +627,7 @@ VOID StatusBarShowCustomizeDialog( DialogBox( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_CUSTOMIZE_SB), - PhMainWindowHandle, + PhMainWndHandle, CustomizeStatusBarDialogProc ); } \ No newline at end of file diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index c457964c4ffc..cef8fdffbb33 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -509,7 +509,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); context->DialogHandle = hwndDlg; context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); @@ -788,12 +788,12 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } else { - SetMenu(PhMainWindowHandle, MainMenu); - DrawMenuBar(PhMainWindowHandle); + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); } } } @@ -937,7 +937,7 @@ VOID ToolBarShowCustomizeDialog( DialogBox( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_CUSTOMIZE_TB), - PhMainWindowHandle, + PhMainWndHandle, CustomizeToolbarDialogProc ); } \ No newline at end of file diff --git a/plugins/ToolStatus/graph.c b/plugins/ToolStatus/graph.c index 97a62b7b1ae2..c8effcd4396e 100644 --- a/plugins/ToolStatus/graph.c +++ b/plugins/ToolStatus/graph.c @@ -45,7 +45,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -65,7 +65,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -85,7 +85,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -105,7 +105,7 @@ VOID ToolbarCreateGraphs(VOID) 0, 0, 0, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -449,7 +449,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) PhDivideSinglesBySingle( MemGraphState.Data1, - (FLOAT)PhGetSystemBasicInformation().NumberOfPhysicalPages, + (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, drawInfo->LineDataCount ); @@ -617,7 +617,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) if (record) { - PhShowProcessRecordDialog(PhMainWindowHandle, record); + PhShowProcessRecordDialog(PhMainWndHandle, record); PhDereferenceProcessRecord(record); } } @@ -651,7 +651,7 @@ VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) if (record) { - PhShowProcessRecordDialog(PhMainWindowHandle, record); + PhShowProcessRecordDialog(PhMainWndHandle, record); PhDereferenceProcessRecord(record); } } diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 29f4ad589e31..2c81fa57babb 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -252,7 +252,7 @@ VOID ShowCustomizeMenu( selectedItem = PhShowEMenu( menu, - PhMainWindowHandle, + PhMainWndHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, @@ -271,12 +271,12 @@ VOID ShowCustomizeMenu( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } else { - SetMenu(PhMainWindowHandle, MainMenu); - DrawMenuBar(PhMainWindowHandle); + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); } } break; @@ -293,7 +293,7 @@ VOID ShowCustomizeMenu( { // Adding the Searchbox makes it focused, // reset the focus back to the main window. - SetFocus(PhMainWindowHandle); + SetFocus(PhMainWndHandle); } } break; @@ -452,8 +452,8 @@ VOID NTAPI LayoutPaddingCallback( SendMessage(RebarHandle, WM_SIZE, 0, 0); - // TODO: GetClientRect with PhMainWindowHandle causes crash. - //GetClientRect(PhMainWindowHandle, &clientRect); + // TODO: GetClientRect with PhMainWndHandle causes crash. + //GetClientRect(PhMainWndHandle, &clientRect); GetClientRect(RebarHandle, &rebarRect); // Adjust the PH client area and exclude the rebar width. @@ -556,22 +556,22 @@ BOOLEAN NTAPI MessageLoopFilter( ) { if ( - Message->hwnd == PhMainWindowHandle || - IsChild(PhMainWindowHandle, Message->hwnd) + Message->hwnd == PhMainWndHandle || + IsChild(PhMainWndHandle, Message->hwnd) ) { - if (TranslateAccelerator(PhMainWindowHandle, AcceleratorTable, Message)) + if (TranslateAccelerator(PhMainWndHandle, AcceleratorTable, Message)) return TRUE; - if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWindowHandle)) + 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(PhMainWindowHandle, MainMenu); - DrawMenuBar(PhMainWindowHandle); - SendMessage(PhMainWindowHandle, WM_SYSCHAR, Message->wParam, Message->lParam); + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); + SendMessage(PhMainWndHandle, WM_SYSCHAR, Message->wParam, Message->lParam); return TRUE; } } @@ -774,7 +774,7 @@ LRESULT CALLBACK MainWndSubclassProc( case RBN_HEIGHTCHANGE: { // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWindowHandle, WM_SIZE, 0, 0); + SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); } break; case RBN_CHEVRONPUSHED: @@ -886,7 +886,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (selectedItem && selectedItem->Id != -1) { - SendMessage(PhMainWindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); } PhDestroyEMenu(menu); @@ -997,7 +997,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (selectedItem && selectedItem->Id != -1) { - SendMessage(PhMainWindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); } PhDestroyEMenu(menu); @@ -1123,7 +1123,7 @@ LRESULT CALLBACK MainWndSubclassProc( SetCursor(LoadCursor(NULL, IDC_ARROW)); // Bring the window back to the top, and preserve the Always on Top setting. - SetWindowPos(PhMainWindowHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, + SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); TargetingWindow = FALSE; @@ -1236,7 +1236,7 @@ LRESULT CALLBACK MainWndSubclassProc( } } - SetWindowPos(PhMainWindowHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, + SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); TargetingCompleted = TRUE; @@ -1263,14 +1263,14 @@ LRESULT CALLBACK MainWndSubclassProc( if (!ToolStatusConfig.AutoHideMenu) break; - if (GetMenu(PhMainWindowHandle)) + if (GetMenu(PhMainWndHandle)) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } else { - SetMenu(PhMainWindowHandle, MainMenu); - DrawMenuBar(PhMainWindowHandle); + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); } } else if ((wParam & 0xFFF0) == SC_MINIMIZE) @@ -1288,9 +1288,9 @@ LRESULT CALLBACK MainWndSubclassProc( if (!ToolStatusConfig.AutoHideMenu) break; - if (GetMenu(PhMainWindowHandle)) + if (GetMenu(PhMainWndHandle)) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } } break; @@ -1309,21 +1309,21 @@ VOID NTAPI MainWindowShowingCallback( { PhRegisterMessageLoopFilter(MessageLoopFilter, NULL); PhRegisterCallback( - ProcessHacker_GetCallbackLayoutPadding(PhMainWindowHandle), + ProcessHacker_GetCallbackLayoutPadding(PhMainWndHandle), LayoutPaddingCallback, NULL, &LayoutPaddingCallbackRegistration ); - SetWindowSubclass(PhMainWindowHandle, MainWndSubclassProc, 0, 0); + SetWindowSubclass(PhMainWndHandle, MainWndSubclassProc, 0, 0); ToolbarLoadSettings(); ReBarLoadLayoutSettings(); StatusBarLoadSettings(); - MainMenu = GetMenu(PhMainWindowHandle); + MainMenu = GetMenu(PhMainWndHandle); if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } } diff --git a/plugins/ToolStatus/options.c b/plugins/ToolStatus/options.c index fed1a0811a97..f46e5b5564ac 100644 --- a/plugins/ToolStatus/options.c +++ b/plugins/ToolStatus/options.c @@ -66,12 +66,12 @@ INT_PTR CALLBACK OptionsDlgProc( if (ToolStatusConfig.AutoHideMenu) { - SetMenu(PhMainWindowHandle, NULL); + SetMenu(PhMainWndHandle, NULL); } else { - SetMenu(PhMainWindowHandle, MainMenu); - DrawMenuBar(PhMainWindowHandle); + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); } EndDialog(hwndDlg, IDOK); diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index 550771c7c7a3..92a8515eb812 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -229,7 +229,7 @@ VOID StatusBarShowMenu( selectedItem = PhShowEMenu( menu, - PhMainWindowHandle, + PhMainWndHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, cursorPos.x, @@ -324,8 +324,8 @@ VOID StatusBarUpdate( break; case ID_STATUS_PHYSICALMEMORY: { - ULONG physicalUsage = PhGetSystemBasicInformation().NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; - FLOAT physicalFraction = (FLOAT)physicalUsage / PhGetSystemBasicInformation().NumberOfPhysicalPages * 100; + ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; + FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages * 100; text[count] = PhFormatString( L"Physical memory: %s (%.2f%%)", @@ -337,7 +337,7 @@ VOID StatusBarUpdate( case ID_STATUS_FREEMEMORY: { ULONG physicalFree = SystemStatistics.Performance->AvailablePages; - FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhGetSystemBasicInformation().NumberOfPhysicalPages * 100; + FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhSystemBasicInformation.NumberOfPhysicalPages * 100; text[count] = PhFormatString( L"Free memory: %s (%.2f%%)", diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index 6393b2771090..c7b752460c7b 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -127,7 +127,7 @@ VOID RebarLoadSettings( 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, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -188,7 +188,7 @@ VOID RebarLoadSettings( NULL, WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - PhMainWindowHandle, + PhMainWndHandle, NULL, NULL, NULL @@ -374,7 +374,7 @@ VOID ToolbarLoadSettings( } // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWindowHandle, WM_SIZE, 0, 0); + SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); } VOID ToolbarResetSettings( diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 902ba775c308..77c0c978bda6 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -88,8 +88,8 @@ INT_PTR CALLBACK TextDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhInitializeLayoutManager(&LayoutManager, hwndDlg); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 07b6e873b18d..0b766debcfeb 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -71,16 +71,16 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( info.nShow = SW_SHOW; info.hwnd = hwndDlg; - ProcessHacker_PrepareForEarlyShutdown(PhMainWindowHandle); + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); if (ShellExecuteEx(&info)) { - ProcessHacker_Destroy(PhMainWindowHandle); + ProcessHacker_Destroy(PhMainWndHandle); } else { // Install failed, cancel the shutdown. - ProcessHacker_CancelEarlyShutdown(PhMainWindowHandle); + ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); // Set button text for next action //Button_SetText(GetDlgItem(hwndDlg, IDOK), L"Retry"); @@ -144,7 +144,7 @@ VOID ShowLatestVersionDialog( config.lpCallbackData = (LONG_PTR)Context; // HACK - imageDosHeader = (PIMAGE_DOS_HEADER)PhImageBaseAddress; + 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); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 328bd6f35862..6cc3cdc1facd 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -70,8 +70,8 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhImageBaseAddress, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); @@ -317,7 +317,7 @@ BOOLEAN QueryUpdateData( if (!(httpSessionHandle = WinHttpOpen( NULL, - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -327,7 +327,7 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; @@ -364,7 +364,7 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_7) + if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -760,7 +760,7 @@ NTSTATUS UpdateDownloadThread( // Open the HTTP session with the system proxy configuration if available if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), - PhWindowsVersion() >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -770,7 +770,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) { ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); @@ -801,7 +801,7 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (PhWindowsVersion() >= WINDOWS_7) + if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -1093,11 +1093,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( // break; //case WM_NCACTIVATE: // { - // if (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) + // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) // { // if (!context->FixedWindowStyles) // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWindowHandle); + // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); // context->FixedWindowStyles = TRUE; // } @@ -1126,7 +1126,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( UpdateDialogHandle = context->DialogHandle = hwndDlg; // Center the update window on PH if it's visible else we center on the desktop. - PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWindowHandle) && !IsMinimized(PhMainWindowHandle)) ? PhMainWindowHandle : NULL); + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); // Create the Taskdialog icons TaskDialogCreateIcons(context); @@ -1193,7 +1193,7 @@ VOID ShowUpdateDialog( { if (!(UpdateDialogThreadHandle = PhCreateThread(0, ShowUpdateDialogThread, Context))) { - PhShowStatus(PhMainWindowHandle, L"Unable to create the updater window.", 0, GetLastError()); + PhShowStatus(PhMainWndHandle, L"Unable to create the updater window.", 0, GetLastError()); return; } diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 5afc567627b0..ab8b850a635c 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -55,6 +55,9 @@ ((ULONGLONG)(build) << 16) | \ ((ULONGLONG)(revision) << 0)) +#define UT_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) +#define UT_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) + #ifdef _DEBUG // Force update checks to succeed (most of the below flags require this to be defined). //#define FORCE_UPDATE_CHECK diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 42026cb27874..88a55cfa438b 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -181,7 +181,7 @@ ULONG GetProcessAffinity( if (NT_SUCCESS(PhOpenProcess( &processHandle, - PhProcessQueryAccess(), + ProcessQueryAccess, ProcessId ))) { @@ -208,7 +208,7 @@ IO_PRIORITY_HINT GetProcessIoPriority( if (NT_SUCCESS(PhOpenProcess( &processHandle, - PhProcessQueryAccess(), + ProcessQueryAccess, ProcessId ))) { @@ -425,7 +425,7 @@ VOID NTAPI MenuItemCallback( if (!highlightPresent) { CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; - chooseColor.hwndOwner = PhMainWindowHandle; + chooseColor.hwndOwner = PhMainWndHandle; chooseColor.lpCustColors = ProcessCustomColors; chooseColor.lpfnHook = ColorDlgHookProc; chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK; @@ -624,7 +624,7 @@ VOID NTAPI MenuHookCallback( affinityMask = GetProcessAffinity(processItem->ProcessId); // Show the affinity dialog (with our values). - if (PhShowProcessAffinityDialog2(PhMainWindowHandle, affinityMask, &newAffinityMask)) + if (PhShowProcessAffinityDialog2(PhMainWndHandle, affinityMask, &newAffinityMask)) { PDB_OBJECT object; @@ -1432,14 +1432,13 @@ LOGICAL DllMain( NULL, &MiListSectionMenuInitializingCallbackRegistration ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderModified), + PhRegisterCallback(&PhProcessModifiedEvent, ProcessModifiedCallback, NULL, &ProcessModifiedCallbackRegistration ); PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessProviderUpdated), + &PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration @@ -1882,7 +1881,7 @@ UINT_PTR CALLBACK ColorDlgHookProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, PhMainWindowHandle); + PhCenterWindow(hwndDlg, PhMainWndHandle); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj b/plugins/WindowExplorer/WindowExplorer.vcxproj index 955db20f5099..f61d6d998529 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj @@ -53,12 +53,12 @@ - ProcessHacker.exe;comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) - ProcessHacker.exe;comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index 987bdd8e6833..c31834ae619d 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -231,7 +231,7 @@ LOGICAL DllMain( isClient = FALSE; - if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhGetImageBase")) + if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhLibImageBase")) { isClient = TRUE; } diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index 2f03023ad597..dba7a96bc9a2 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -116,8 +116,8 @@ VOID WeShowWindowProperties( // utils -#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhGetMainWndHandle")) -#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("PhWindowsVersion")) +#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhMainWndHandle")) +#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("WindowsVersion")) PVOID WeGetProcedureAddress( _In_ PSTR Name diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 7ed88dbf2a40..515b17ee9c40 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -611,7 +611,7 @@ static VOID WepEnsureHookDataValid( #ifdef _WIN64 // We can't use the hook on WOW64 processes. - if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("PhProcessQueryAccess"), Context->ClientId.UniqueProcess))) + if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("ProcessQueryAccess"), Context->ClientId.UniqueProcess))) { PhGetProcessIsWow64(processHandle, &isWow64); NtClose(processHandle); diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index ba8868d0c4f1..a5a6ae577e12 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -96,15 +96,14 @@ true - delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true Windows MachineX86 6.01 - ___delayLoadHelper2@8 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -126,15 +125,14 @@ true - delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true Windows MachineX64 6.01 - __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -159,8 +157,8 @@ true - delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true @@ -170,7 +168,6 @@ MachineX86 true 6.01 - ___delayLoadHelper2@8 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -194,8 +191,8 @@ true - delaylib.lib;noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);..\..\tools\delaylib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) true @@ -205,7 +202,6 @@ MachineX64 true 6.01 - __delayLoadHelper2 ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) From 6baf027f476d46c227f15d7ea4a6f66e38184828 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 21:08:35 +1000 Subject: [PATCH 146/839] Add missing symbolic access_mask PROCESS_SET_LIMITED_INFORMATION --- phlib/secdata.c | 1 + 1 file changed, 1 insertion(+) diff --git a/phlib/secdata.c b/phlib/secdata.c index fe08d207ac82..d87f74df6a5e 100644 --- a/phlib/secdata.c +++ b/phlib/secdata.c @@ -256,6 +256,7 @@ ACCESS_ENTRIES(Process60) { 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 }, From a7aa27ffb6f00b4b96558efa4432ac80a22884e0 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 21:58:30 +1000 Subject: [PATCH 147/839] BuildTools: Fix changelog string format --- tools/CustomBuildTool/Source Files/Build.cs | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 2031e4364c1a..3cf61d381a22 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -894,7 +894,7 @@ public static void WebServiceUpdateConfig() if (string.IsNullOrEmpty(BuildSetupSig)) return; - string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --pretty=format:\"%h %an %s (%cr)\""); + string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\""); string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); string buildPostString = Json.Serialize(new BuildUpdateRequest { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 4f1449cb3744c750cdcb24fc9aa41e20f818a47e..ef4b4940de3aa5582192417cf4abdbb58da3875c 100644 GIT binary patch delta 3979 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 Js|@24UH~2Fb@KoK delta 442 zcmZpe!_qK^WdjSB1V680BqIX@gXi;aMYdr}JXV-aDwyQ9!fUe(myUo);LWFvw~p+~ z72jI-^7Tjgw5lV^R5v=VVO-21!^XjR|8dKciet50p!T3ZToe8Za=Z;*iq<$^j7=pz32_hyqGDLFHIC Z&;4r1%5+nA^Mzj~tT>}&s|@24UI62eimU(t From b02f88961887ee086feeec361eda558c82e0796f Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 22:02:51 +1000 Subject: [PATCH 148/839] Fix remaining window icons --- ProcessHacker/include/appsup.h | 4 +-- ProcessHacker/mainwnd.c | 4 +-- ProcessHacker/memedit.c | 3 ++ ProcessHacker/miniinfo.c | 8 +++--- ProcessHacker/procrec.c | 2 +- ProcessHacker/prpgenv.c | 8 +++--- ProcessHacker/prpggen.c | 4 +-- ProcessHacker/prpgthrd.c | 2 +- ProcessHacker/srvlist.c | 8 +++--- ProcessHacker/sysinfo.c | 4 +-- ProcessHacker/thrdstk.c | 3 ++ plugins/ExtendedTools/etwprprp.c | 6 ++-- plugins/ExtendedTools/exttools.h | 4 --- plugins/ExtendedTools/gpunodes.c | 4 +-- plugins/ExtendedTools/gpuprprp.c | 6 ++-- plugins/ExtendedTools/unldll.c | 4 +-- plugins/ExtraPlugins/setup/updater.c | 18 ++---------- plugins/NetworkTools/ping.c | 3 ++ plugins/NetworkTools/tracert.c | 3 ++ plugins/NetworkTools/update.c | 43 ++++++---------------------- plugins/NetworkTools/whois.c | 3 ++ plugins/OnlineChecks/main.c | 2 +- plugins/OnlineChecks/upload.c | 28 ++---------------- plugins/Updater/options.c | 6 ++-- plugins/Updater/updater.c | 4 +-- plugins/Updater/updater.h | 3 -- 26 files changed, 66 insertions(+), 121 deletions(-) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index fe34c8977206..275273f6d4e8 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -416,8 +416,8 @@ 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) +#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, diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 3d4c8539dd18..26533a980e4c 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -342,12 +342,12 @@ BOOLEAN PhMwpInitializeWindowClass( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, 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 = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); if (!RegisterClassEx(&wcex)) return FALSE; diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index 4cf0897ad7e9..1dfbef18a22c 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -185,6 +185,9 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( { NTSTATUS status; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + if (context->Title) { SetWindowText(hwndDlg, context->Title->Buffer); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 01d139a083e8..64bf00ebfe15 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -110,11 +110,11 @@ VOID PhPinMiniInformation( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; - wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( @@ -448,10 +448,10 @@ VOID PhMipOnInitDialog( HICON cog; HICON pin; - cog = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COG)); + cog = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); - pin = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PIN)); + pin = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PIN)); SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 486a3db8841c..dbcb2c98019a 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -186,7 +186,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( 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))); + (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)context->FileIcon, 0); diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index 2f8f181b94a0..e819a260440c 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -454,6 +454,9 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( { case WM_INITDIALOG: { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); @@ -476,10 +479,7 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( SetDlgItemText(hwndDlg, IDC_NAME, context->Name); SetDlgItemText(hwndDlg, IDC_VALUE, context->Value ? context->Value : L""); - if (context->Value) - { - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUE), TRUE); - } + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); } break; case WM_DESTROY: diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 30b2115d2695..76318e4492e0 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -147,8 +147,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(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER)); + magnifier = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_MAGNIFIER)); SET_BUTTON_ICON(IDC_INSPECT, magnifier); SET_BUTTON_ICON(IDC_OPENFILENAME, folder); diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 53e2cf7797e5..cf23dd2cccc2 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -639,7 +639,7 @@ 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))); + SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); } break; case WM_DESTROY: diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 003e86f51e31..1e2eaeb655df 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -689,10 +689,10 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( 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)); + ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATION)); + ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); + ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); + ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COGGO)); ServiceIconsLoaded = TRUE; } diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 5dcbb236ea54..889066c520c7 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -281,8 +281,8 @@ VOID PhSipOnInitDialog( VOID ) { - SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 5e7d9e053328..35ac287e38a5 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -169,6 +169,9 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( HWND lvHandle; PPH_LAYOUT_MANAGER layoutManager; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + threadStackContext = (PTHREAD_STACK_CONTEXT)lParam; SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext); diff --git a/plugins/ExtendedTools/etwprprp.c b/plugins/ExtendedTools/etwprprp.c index 1e87ffbd4672..67e7ce1b0cd6 100644 --- a/plugins/ExtendedTools/etwprprp.c +++ b/plugins/ExtendedTools/etwprprp.c @@ -148,9 +148,9 @@ VOID EtwDiskNetworkLayoutGraphs( 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); + 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; diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index 967f6d30f356..c5201b3fabc5 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -27,10 +27,6 @@ extern HWND NetworkTreeNewHandle; #define SETTING_NAME_UNLOADED_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") #define SETTING_NAME_UNLOADED_COLUMNS (PLUGIN_NAME L".UnloadedListColumns") -#define ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) -#define ET_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define ET_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) - // Graph update message #define UPDATE_MSG (WM_APP + 1) diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index fe9467dda387..cc56abcd55b6 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -121,8 +121,8 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( WindowHandle = hwndDlg; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhInitializeLayoutManager(&LayoutManager, hwndDlg); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index 882b3c453d75..fc9138bf0915 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -263,9 +263,9 @@ VOID GpuPropLayoutGraphs( 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); + 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; diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 54d9f4b8ae27..93325b16fbf3 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -305,8 +305,8 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)ET_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)ET_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 77662132c054..0cc2c26e9d32 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -60,22 +60,8 @@ VOID TaskDialogCreateIcons( ) { // 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 - ); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); // Set the TaskDialog window icons SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 0ccb364a7005..cb6165e4e30d 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -285,6 +285,9 @@ INT_PTR CALLBACK NetworkPingWndProc( { PPH_LAYOUT_ITEM panelItem; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, 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. diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 41550a1f123b..f5fc427381e8 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -568,6 +568,9 @@ INT_PTR CALLBACK TracertDlgProc( { HANDLE tracertThread; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, PhMainWndHandle); Static_SetText(hwndDlg, diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 988029df021f..579feb6015cc 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -35,21 +35,14 @@ VOID FreeUpdateContext( ) { //PhClearReference(&Context->Version); - // PhClearReference(&Context->RevVersion); + //PhClearReference(&Context->RevVersion); //PhClearReference(&Context->RelDate); - // PhClearReference(&Context->Size); + //PhClearReference(&Context->Size); //PhClearReference(&Context->Hash); - // PhClearReference(&Context->Signature); - // PhClearReference(&Context->ReleaseNotesUrl); + //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->SetupFileDownloadUrl); //PhClearReference(&Context); } @@ -57,29 +50,11 @@ 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; + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); } VOID TaskDialogLinkClicked( diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index bf6e602d0270..5be18698f44d 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -396,6 +396,9 @@ INT_PTR CALLBACK NetworkOutputDlgProc( { HANDLE dialogThread; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); context->WindowHandle = hwndDlg; diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 35ef25384a98..fda5277f68f6 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -179,7 +179,7 @@ VOID NTAPI MenuItemCallback( config.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; config.hwndParent = menuItem->OwnerWindow; config.hMainIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, + PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), PH_LOAD_ICON_SIZE_LARGE, GetSystemMetrics(SM_CXICON), diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 803689827131..926bd6dc73ec 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -172,18 +172,6 @@ VOID UploadContextDeleteProcedure( 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->FileName); PhClearReference(&context->BaseFileName); @@ -217,20 +205,8 @@ 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) - ); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 77c0c978bda6..17c421487a64 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -86,10 +86,10 @@ INT_PTR CALLBACK TextDlgProc( { PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)lParam; - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); PhInitializeLayoutManager(&LayoutManager, hwndDlg); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 6cc3cdc1facd..85943fe115c4 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -70,8 +70,8 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index ab8b850a635c..5afc567627b0 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -55,9 +55,6 @@ ((ULONGLONG)(build) << 16) | \ ((ULONGLONG)(revision) << 0)) -#define UT_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define UT_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) - #ifdef _DEBUG // Force update checks to succeed (most of the below flags require this to be defined). //#define FORCE_UPDATE_CHECK From 43dce8aab32983ddb4ea6039eacbc9d99e59826a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 22:07:02 +1000 Subject: [PATCH 149/839] OnlineChecks: Fix window icon --- plugins/OnlineChecks/main.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index fda5277f68f6..36187905b825 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -178,13 +178,7 @@ VOID NTAPI MenuItemCallback( 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( - PhLibImageBase, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); + config.hMainIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); config.cxWidth = 180; config.pszWindowTitle = L"Process Hacker - VirusTotal"; config.pszMainInstruction = L"VirusTotal scanning requires a restart of Process Hacker."; From 904e478f5430d5258d80ba19f04c532aaa055b9e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 22:07:12 +1000 Subject: [PATCH 150/839] Updater: Fix changelog window layout --- plugins/Updater/options.c | 1 + plugins/Updater/updater.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 17c421487a64..2ca69914fc11 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -93,6 +93,7 @@ INT_PTR CALLBACK TextDlgProc( 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); SetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), PhGetString(context->BuildMessage)); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 85943fe115c4..1db671fcfa02 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -428,7 +428,7 @@ BOOLEAN QueryUpdateData( 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")); + Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "changelog")); PhInitializeStringBuilder(&sb, 0x100); for (SIZE_T i = 0; i < Context->BuildMessage->Length; i++) From b1b3a043fe62bb82d299501e5a4361911d70ffcd Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 22:36:10 +1000 Subject: [PATCH 151/839] Fix typo --- plugins/OnlineChecks/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 36187905b825..8bbfe03f7b1a 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -178,7 +178,7 @@ VOID NTAPI MenuItemCallback( 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(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + config.hMainIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); config.cxWidth = 180; config.pszWindowTitle = L"Process Hacker - VirusTotal"; config.pszMainInstruction = L"VirusTotal scanning requires a restart of Process Hacker."; From 7812a4f2a235e54c5c9d75c6e4fa5d5c4c425dd9 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 22 May 2017 22:50:36 +1000 Subject: [PATCH 152/839] Updater: Fix crash --- plugins/Updater/updater.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 1db671fcfa02..eda63f54957e 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -430,15 +430,20 @@ BOOLEAN QueryUpdateData( Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "setup_url")); Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "changelog")); - PhInitializeStringBuilder(&sb, 0x100); - for (SIZE_T i = 0; i < Context->BuildMessage->Length; i++) + if (Context->BuildMessage) { - if (Context->BuildMessage->Data[i] == '\n') - PhAppendStringBuilder2(&sb, L"\r\n"); - else - PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); + PhInitializeStringBuilder(&sb, 0x100); + + for (SIZE_T i = 0; i < Context->BuildMessage->Length; i++) + { + if (Context->BuildMessage->Data[i] == '\n') + PhAppendStringBuilder2(&sb, L"\r\n"); + else + PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); + } + + PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); } - PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); CleanupJsonParser(jsonObject); From 8286d59c400365b14b9a8f9c2d517b2a12547ced Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 00:15:19 +1000 Subject: [PATCH 153/839] Remove relative paths from solutions (improves msbuild logging output) --- ProcessHacker/ProcessHacker.vcxproj | 28 +++++++++---------- phlib/phlib.vcxproj | 8 +++--- plugins/Plugins.props | 6 ++-- .../CustomSetupTool/CustomSetupTool.vcxproj | 12 ++++---- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 5e3e2d24f2de..9168ea48f7b7 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -84,7 +84,7 @@ Disabled - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) _PHLIB_;_PHAPP_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug @@ -100,22 +100,22 @@ Windows MachineX86 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - ..\build\build_sdk.cmd + $(SolutionDir)build\build_sdk.cmd Disabled - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) _PHLIB_;_PHAPP_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug @@ -131,24 +131,24 @@ Windows MachineX64 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - ..\build\build_sdk.cmd + $(SolutionDir)build\build_sdk.cmd MaxSpeed true - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) _PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded @@ -170,20 +170,20 @@ MachineX86 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) MaxSpeed true - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) _PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded @@ -204,13 +204,13 @@ MachineX64 true 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index cf67c6719a83..e395b2239ea8 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -76,7 +76,7 @@ Disabled - ..\phnt\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) DEBUG;_PHLIB_;%(PreprocessorDefinitions) true EnableFastChecks @@ -91,7 +91,7 @@ Disabled - ..\phnt\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) DEBUG;_PHLIB_;%(PreprocessorDefinitions) true EnableFastChecks @@ -107,7 +107,7 @@ MaxSpeed true - ..\phnt\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) true MultiThreaded @@ -125,7 +125,7 @@ MaxSpeed true - ..\phnt\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) WIN64;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) true MultiThreaded diff --git a/plugins/Plugins.props b/plugins/Plugins.props index f47cc8f6e6d0..5069f7f3deee 100644 --- a/plugins/Plugins.props +++ b/plugins/Plugins.props @@ -19,7 +19,7 @@ - ..\include;..\..\sdk\include;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)..\sdk\include;%(AdditionalIncludeDirectories) true Level3 true @@ -76,7 +76,7 @@ StreamingSIMDExtensions - ..\..\sdk\lib\i386;%(AdditionalLibraryDirectories) + $(SolutionDir)..\sdk\lib\i386;%(AdditionalLibraryDirectories) MachineX86 @@ -84,7 +84,7 @@ - ..\..\sdk\lib\amd64;%(AdditionalLibraryDirectories) + $(SolutionDir)..\sdk\lib\amd64;%(AdditionalLibraryDirectories) MachineX64 diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index cd939c79a92a..f158b962a908 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -53,7 +53,7 @@ Level3 Disabled true - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) false StdCall MultiThreadedDebug @@ -65,13 +65,13 @@ true uxtheme.lib;winhttp.lib;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows RequireAdministrator _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) @@ -81,7 +81,7 @@ true true true - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) StdCall _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) MultiThreaded @@ -95,7 +95,7 @@ true true uxtheme.lib;winhttp.lib;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows 5.01 true @@ -103,7 +103,7 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) From cf43d189b118de91a9e141377fe505219f2ae25b Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 02:04:21 +1000 Subject: [PATCH 154/839] Fix setup replacing existing settings file, Add initial setup error page, Add more web-setup support (incomplete) --- .../CustomSetupTool/CustomSetupTool.vcxproj | 1 + .../CustomSetupTool.vcxproj.filters | 3 + .../CustomSetupTool/download.c | 93 ++----------- tools/CustomSetupTool/CustomSetupTool/error.c | 48 +++++++ .../CustomSetupTool/include/setup.h | 73 ++++++++-- tools/CustomSetupTool/CustomSetupTool/main.c | 45 ++++-- tools/CustomSetupTool/CustomSetupTool/page1.c | 16 +-- tools/CustomSetupTool/CustomSetupTool/page2.c | 13 +- tools/CustomSetupTool/CustomSetupTool/page3.c | 12 +- tools/CustomSetupTool/CustomSetupTool/page4.c | 9 +- tools/CustomSetupTool/CustomSetupTool/page5.c | 125 ++++++----------- .../CustomSetupTool/resource.h | 6 +- .../CustomSetupTool/resource.rc | 128 +++++++++--------- .../CustomSetupTool/resources/version.rc | 88 ++++++++---- tools/CustomSetupTool/CustomSetupTool/setup.c | 8 +- .../CustomSetupTool/uninstall.c | 6 +- 16 files changed, 363 insertions(+), 311 deletions(-) create mode 100644 tools/CustomSetupTool/CustomSetupTool/error.c diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index f158b962a908..227ad5d2ce4f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -108,6 +108,7 @@ + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index 85f331c02a3f..6557bf933011 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -105,6 +105,9 @@ Source Files\json + + Source Files\pages + diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 977872b81370..7d050109e1b8 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -5,13 +5,6 @@ #include "json-c\json.h" -typedef struct _PH_SETUP_DOWNLOAD_CONTEXT -{ - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; -} PH_SETUP_DOWNLOAD_CONTEXT, *PPH_SETUP_DOWNLOAD_CONTEXT; - PPH_STRING SetupGetVersion( VOID ) @@ -170,7 +163,7 @@ json_object_ptr json_get_object( BOOLEAN SetupQueryUpdateData( - _Inout_ PPH_SETUP_UNINSTALL_CONTEXT Context + _Inout_ PPH_SETUP_DOWNLOAD_CONTEXT Context ) { BOOLEAN success = FALSE; @@ -372,8 +365,8 @@ BOOLEAN SetupQueryUpdateData( if (httpSessionHandle) WinHttpCloseHandle(httpSessionHandle); - //if (xmlNode) - // mxmlDelete(xmlNode); + if (jsonObject) + json_object_put(jsonObject); if (stringBuffer) PhFree(stringBuffer); @@ -384,82 +377,11 @@ BOOLEAN SetupQueryUpdateData( return 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; -//} - BOOLEAN UpdateDownloadUpdateData( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context ) { BOOLEAN downloadSuccess = FALSE; -// BOOLEAN hashSuccess = FALSE; -// BOOLEAN signatureSuccess = FALSE; HANDLE tempFileHandle = NULL; HINTERNET httpSessionHandle = NULL; HINTERNET httpConnectionHandle = NULL; @@ -470,7 +392,6 @@ BOOLEAN UpdateDownloadUpdateData( 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) }; @@ -546,7 +467,7 @@ BOOLEAN UpdateDownloadUpdateData( httpUrlComponents.dwUrlPathLength = (ULONG)-1; if (!WinHttpCrackUrl( - PhGetStringOrEmpty(Context->SetupFileDownloadUrl), + PhGetString(Context->SetupFileDownloadUrl), 0, 0, &httpUrlComponents @@ -728,6 +649,8 @@ BOOLEAN UpdateDownloadUpdateData( PhDereferenceObject(totalDownloaded); } } + + downloadSuccess = TRUE; } CleanupExit: @@ -767,5 +690,5 @@ BOOLEAN UpdateDownloadUpdateData( // } //} - return STATUS_SUCCESS; + return downloadSuccess; } diff --git a/tools/CustomSetupTool/CustomSetupTool/error.c b/tools/CustomSetupTool/CustomSetupTool/error.c new file mode 100644 index 000000000000..ec3eb5ff1eb1 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/error.c @@ -0,0 +1,48 @@ +/* + * 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 = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + + 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/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index c01ad508d6d5..6c9f40621a2d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -32,6 +32,8 @@ #include #include #include +#include + #include #include #include @@ -40,9 +42,9 @@ #include #include -#include - #include "resource.h" + +// Version Information #include "..\..\ProcessHacker\include\phappres.h" // Win32 PropertySheet Control IDs @@ -82,11 +84,27 @@ typedef enum _SETUP_COMMAND_TYPE SETUP_COMMAND_REPAIR, } SETUP_COMMAND_TYPE; -typedef struct _SETUP_INSTANCE +typedef struct _PH_SETUP_CONTEXT { - ULONG Version; HWND WindowHandle; -} SETUP_INSTANCE, *PSETUP_INSTANCE; + HWND PropSheetHandle; + + HICON IconSmallHandle; + HICON IconLargeHandle; + + ULONG ErrorCode; + PPH_STRING Version; + PPH_STRING RevVersion; + PPH_STRING RelDate; + PPH_STRING Size; + PPH_STRING Hash; + PPH_STRING Signature; + PPH_STRING ReleaseNotesUrl; + + PPH_STRING BinFileDownloadUrl; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFilePath; +} PH_SETUP_CONTEXT, *PPH_SETUP_CONTEXT; VOID SetupLoadImage( _In_ HWND WindowHandle, @@ -128,6 +146,13 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( _Inout_ LPARAM lParam ); +INT_PTR CALLBACK SetupErrorPage_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ); + // page4.c typedef struct _SETUP_PROGRESS_THREAD @@ -185,15 +210,14 @@ VOID SetupUpgradeSettingsFile( // download.c -typedef struct _PH_SETUP_UNINSTALL_CONTEXT +typedef struct _PH_SETUP_DOWNLOAD_CONTEXT { HWND DialogHandle; + HWND PropSheetHandle; HWND MainHeaderHandle; HWND StatusHandle; HWND SubStatusHandle; HWND ProgressHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; ULONG CurrentMajorVersion; ULONG CurrentMinorVersion; @@ -214,14 +238,14 @@ typedef struct _PH_SETUP_UNINSTALL_CONTEXT PPH_STRING BinFileDownloadUrl; PPH_STRING SetupFileDownloadUrl; PPH_STRING SetupFilePath; -} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; +} PH_SETUP_DOWNLOAD_CONTEXT, *PPH_SETUP_DOWNLOAD_CONTEXT; BOOLEAN SetupQueryUpdateData( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context ); BOOLEAN UpdateDownloadUpdateData( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context ); // extract.c @@ -238,6 +262,33 @@ VOID SetupShowUpdateDialog( // uninstall.c +typedef struct _PH_SETUP_UNINSTALL_CONTEXT +{ + HWND DialogHandle; + HICON IconSmallHandle; + HICON IconLargeHandle; + + ULONG CurrentMajorVersion; + ULONG CurrentMinorVersion; + ULONG CurrentRevisionVersion; + ULONG LatestMajorVersion; + ULONG LatestMinorVersion; + ULONG LatestRevisionVersion; + + ULONG ErrorCode; + PPH_STRING Version; + PPH_STRING RevVersion; + PPH_STRING RelDate; + PPH_STRING Size; + PPH_STRING Hash; + PPH_STRING Signature; + PPH_STRING ReleaseNotesUrl; + + PPH_STRING BinFileDownloadUrl; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFilePath; +} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; + VOID SetupShowUninstallDialog( VOID ); diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 9318c1dd44a9..fe2fb4e1d8a1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -82,16 +82,29 @@ 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 = PhAllocate(sizeof(PH_SETUP_CONTEXT)); + memset(context, 0, sizeof(PH_SETUP_CONTEXT)); + + context->PropSheetHandle = hwndDlg; + + SetProp(hwndDlg, L"SetupContext", (HANDLE)context); + } + break; } return FALSE; @@ -140,7 +153,7 @@ INT WINAPI wWinMain( { PROPSHEETPAGE propSheetPage = { sizeof(PROPSHEETPAGE) }; PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; - HPROPSHEETPAGE pages[4]; + HPROPSHEETPAGE pages[6]; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USECALLBACK | PSH_WIZARD_LITE; propSheetHeader.hInstance = PhLibImageBase; @@ -148,6 +161,7 @@ INT WINAPI wWinMain( propSheetHeader.pfnCallback = MainPropSheet_Callback; propSheetHeader.phpage = pages; + // welcome page memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; @@ -156,6 +170,7 @@ INT WINAPI wWinMain( propSheetPage.pfnDlgProc = SetupPropPage1_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + // eula page memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; @@ -164,13 +179,25 @@ INT WINAPI wWinMain( 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.pfnDlgProc = SetupPropPage3_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); -#ifdef PH_BUILD_API + // download page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG5); + propSheetPage.pfnDlgProc = SetupPropPage5_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + // extract page memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; @@ -178,15 +205,15 @@ INT WINAPI wWinMain( propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); propSheetPage.pfnDlgProc = SetupPropPage4_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); -#else + + // error page memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); - propSheetPage.pfnDlgProc = SetupPropPage5_WndProc; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_ERROR); + propSheetPage.pfnDlgProc = SetupErrorPage_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); -#endif PhModalPropertySheet(&propSheetHeader); } diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/page1.c index 4ce65ad5c163..8c98be740860 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/page1.c @@ -73,6 +73,8 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + switch (uMsg) { case WM_INITDIALOG: @@ -98,27 +100,23 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( { case PSN_SETACTIVE: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - // Reset the button state. - PropSheet_SetWizButtons(hwPropSheet, PSWIZB_NEXT); + PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT); // Hide the back button. - //ShowWindow(GetDlgItem(hwPropSheet, IDC_PROPSHEET_BACK), SW_HIDE); + //ShowWindow(GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_BACK), SW_HIDE); // HACK: Focus the next button (reset after changing button state). - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwPropSheet, IDC_PROPSHEET_NEXT), TRUE); + PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_NEXT), TRUE); } break; case PSN_KILLACTIVE: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - // Enable the back button. - PropSheet_SetWizButtons(hwPropSheet, PSWIZB_NEXT | PSWIZB_BACK); + PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT | PSWIZB_BACK); // Show the back button. - //ShowWindow(GetDlgItem(hwPropSheet, IDC_PROPSHEET_BACK), SW_SHOW); + //ShowWindow(GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_BACK), SW_SHOW); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/page2.c index 016a431d7e9e..a987b93b5c80 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page2.c +++ b/tools/CustomSetupTool/CustomSetupTool/page2.c @@ -27,6 +27,8 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + switch (uMsg) { case WM_INITDIALOG: @@ -62,20 +64,15 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( { case PSN_SETACTIVE: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwPropSheet, IDC_PROPSHEET_NEXT), TRUE); + PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_NEXT), TRUE); // Disable the Next button - //PropSheet_SetWizButtons(hwPropSheet, PSWIZB_BACK); + //PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_BACK); } break; case PSN_QUERYINITIALFOCUS: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - HWND hwnd = GetDlgItem(hwPropSheet, IDC_PROPSHEET_CANCEL); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)hwnd); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_CANCEL)); } return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/page3.c b/tools/CustomSetupTool/CustomSetupTool/page3.c index aa1344a04c70..800f73b38a39 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page3.c +++ b/tools/CustomSetupTool/CustomSetupTool/page3.c @@ -41,6 +41,8 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + switch (uMsg) { case WM_INITDIALOG: @@ -123,7 +125,7 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( switch (pageNotify->hdr.code) { - case PSN_KILLACTIVE: + case PSN_WIZNEXT: { SetupInstallPath = PhGetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_DIRECTORY)); SetupCreateDesktopShortcut = Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_CHECK)) == BST_CHECKED; @@ -136,8 +138,14 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( 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; + +#ifdef PH_BUILD_API + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)IDD_DIALOG4); +#else + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)IDD_DIALOG5); +#endif } - break; + return TRUE; case PSN_QUERYINITIALFOCUS: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_FOLDER_BROWSE)); return TRUE; diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index 236086b28f38..f0984811c876 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -85,6 +85,8 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + switch (uMsg) { case WM_INITDIALOG: @@ -119,14 +121,11 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( break; case PSN_SETACTIVE: { - HWND hwPropSheet; HANDLE threadHandle; PSETUP_PROGRESS_THREAD progress; - hwPropSheet = pageNotify->hdr.hwndFrom; - // Disable Next/Back buttons - PropSheet_SetWizButtons(hwPropSheet, 0); + PropSheet_SetWizButtons(context->PropSheetHandle, 0); if (!SetupRunning) { @@ -135,7 +134,7 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( // Setup the progress thread progress = PhCreateAlloc(sizeof(SETUP_PROGRESS_THREAD)); progress->DialogHandle = hwndDlg; - progress->PropSheetHandle = hwPropSheet; + progress->PropSheetHandle = context->PropSheetHandle; if (threadHandle = PhCreateThread(0, SetupProgressThread, progress)) NtClose(threadHandle); diff --git a/tools/CustomSetupTool/CustomSetupTool/page5.c b/tools/CustomSetupTool/CustomSetupTool/page5.c index dd6749c90bdc..7fdd40939b22 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page5.c +++ b/tools/CustomSetupTool/CustomSetupTool/page5.c @@ -22,20 +22,25 @@ #include -ULONG64 DownloadCurrentLength = 0; -ULONG64 DownloadTotalLength = 0; - NTSTATUS SetupDownloadProgressThread( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context ) { - if (SetupQueryUpdateData(Context)) - { - UpdateDownloadUpdateData(Context); - } + if (!SetupQueryUpdateData(Context)) + goto CleanupExit; + + if (!UpdateDownloadUpdateData(Context)) + goto CleanupExit; + PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_DIALOG4); PhDereferenceObject(Context); return STATUS_SUCCESS; + +CleanupExit: + + PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_ERROR); + PhDereferenceObject(Context); + return STATUS_FAIL_CHECK; } INT_PTR CALLBACK SetupPropPage5_WndProc( @@ -45,6 +50,8 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + switch (uMsg) { case WM_INITDIALOG: @@ -59,21 +66,6 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), L"Starting download..."); SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), L""); - // Setup the progress thread - PPH_SETUP_UNINSTALL_CONTEXT progress = PhCreateAlloc(sizeof(PH_SETUP_UNINSTALL_CONTEXT)); - memset(progress, 0, sizeof(PH_SETUP_UNINSTALL_CONTEXT)); - - progress->DialogHandle = hwndDlg; - progress->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); - progress->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); - progress->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); - progress->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); - progress->CurrentMajorVersion = PHAPP_VERSION_MAJOR; - progress->CurrentMinorVersion = PHAPP_VERSION_MINOR; - progress->CurrentRevisionVersion = PHAPP_VERSION_REVISION; - - RtlQueueWorkItem(SetupDownloadProgressThread, progress, 0); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; @@ -88,9 +80,6 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( { if (SetupRunning && DialogPromptExit(hwndDlg)) { - //PropSheet_CancelToClose(GetParent(hwndDlg)); - //EnableMenuItem(GetSystemMenu(GetParent(hwndDlg), FALSE), SC_CLOSE, MF_GRAYED); - //EnableMenuItem(GetSystemMenu(GetParent(hwndDlg), FALSE), SC_CLOSE, MF_ENABLED); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); return TRUE; } @@ -98,73 +87,37 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( break; case PSN_SETACTIVE: { - //HWND hwPropSheet; - //HANDLE threadHandle; - //PSETUP_PROGRESS_THREAD progress; - - //hwPropSheet = pageNotify->hdr.hwndFrom; + HANDLE threadHandle; + PPH_SETUP_DOWNLOAD_CONTEXT progress; - //// Disable Next/Back buttons - //PropSheet_SetWizButtons(hwPropSheet, 0); + // Disable Next/Back buttons + PropSheet_SetWizButtons(context->PropSheetHandle, 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, SetupDownloadProgressThread, progress)) - // NtClose(threadHandle); - //} + if (!SetupRunning) + { + SetupRunning = TRUE; + + progress = PhCreateAlloc(sizeof(PH_SETUP_DOWNLOAD_CONTEXT)); + memset(progress, 0, sizeof(PH_SETUP_DOWNLOAD_CONTEXT)); + + progress->DialogHandle = hwndDlg; + progress->PropSheetHandle = context->PropSheetHandle; + progress->CurrentMajorVersion = PHAPP_VERSION_MAJOR; + progress->CurrentMinorVersion = PHAPP_VERSION_MINOR; + progress->CurrentRevisionVersion = PHAPP_VERSION_REVISION; + progress->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); + progress->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); + progress->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); + progress->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); + + if (threadHandle = PhCreateThread(0, SetupDownloadProgressThread, progress)) + NtClose(threadHandle); + } } break; } } break; - case WM_START_SETUP: - { - //SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), - // PhaFormatString(L"Downloading 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); - } - break; - case WM_UPDATE_SETUP: - { - //PPH_STRING currentFile = (PPH_STRING)lParam; - // - ////SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), - //// PhaConcatStrings2(L"Extracting: ", currentFile->Buffer)->Buffer - //// ); - //SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), 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); - // - //PhDereferenceObject(currentFile); - } - break; - case WM_END_SETUP: - { - 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"); - - if (SetupStartAppAfterExit) - { - SetupExecuteProcessHacker(GetParent(hwndDlg)); - PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH); - } - } - break; } return FALSE; diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.h b/tools/CustomSetupTool/CustomSetupTool/resource.h index 49bdd138ee8f..5a7083fb3d6e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.h +++ b/tools/CustomSetupTool/CustomSetupTool/resource.h @@ -8,11 +8,11 @@ #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 109 +#define IDR_BIN_DATA 111 #define IDC_MAINHEADER 1001 -#define IDC_MAINHEADER2 1002 #define IDC_INSTALL_PROGRESS 1005 #define IDC_SUBHEADER 1047 #define IDC_EDIT1 1048 @@ -46,6 +46,6 @@ #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40002 #define _APS_NEXT_CONTROL_VALUE 1078 -#define _APS_NEXT_SYMED_VALUE 110 +#define _APS_NEXT_SYMED_VALUE 112 #endif #endif diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 51ef2760a531..e8ea912af48c 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -8,16 +8,53 @@ // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" -#include "../../../ProcessHacker/include/phappres.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) ///////////////////////////////////////////////////////////////////////////// @@ -96,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 @@ -140,9 +189,17 @@ BEGIN IDD_ERROR, DIALOG BEGIN - LEFTMARGIN, 22 - RIGHTMARGIN, 378 - TOPMARGIN, 10 + LEFTMARGIN, 15 + RIGHTMARGIN, 385 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 + END + + IDD_DIALOG5, DIALOG + BEGIN + LEFTMARGIN, 15 + RIGHTMARGIN, 385 + TOPMARGIN, 7 BOTTOMMARGIN, 216 END END @@ -166,53 +223,6 @@ 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" - "#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) ///////////////////////////////////////////////////////////////////////////// // @@ -221,12 +231,6 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS IDR_LICENCE_DATA RCDATA "resources\\LICENSE.txt" -#if defined(APSTUDIO_INVOKED) || defined(PH_BUILD_API) - -IDR_BIN_DATA RCDATA "..\\..\\..\\build\\output\\processhacker-build-bin.zip" - -#endif - #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc index 8eced112f05c..aad29825fe0b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc @@ -1,8 +1,16 @@ +// 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" +#include "../../../../ProcessHacker/include/phappres.h" + +///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -12,14 +20,51 @@ 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) + ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO - FILEVERSION PHAPP_VERSION_NUMBER - PRODUCTVERSION PHAPP_VERSION_NUMBER + FILEVERSION 3,0,0,0 + PRODUCTVERSION 3,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -36,12 +81,12 @@ BEGIN BEGIN VALUE "CompanyName", "Process Hacker" VALUE "FileDescription", "Process Hacker Setup" - VALUE "FileVersion", PHAPP_VERSION_STRING + VALUE "FileVersion", "3.0.0.0" 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 + VALUE "ProductVersion", "3.0.0.0" END END BLOCK "VarFileInfo" @@ -50,31 +95,26 @@ BEGIN END END -#ifdef APSTUDIO_INVOKED + ///////////////////////////////////////////////////////////////////////////// // -// TEXTINCLUDE +// RCDATA // -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END +IDR_BIN_DATA RCDATA "../../../../build/output/processhacker-build-bin.zip" -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "#include ""../../../ProcessHacker/include/phappres.h""\r\n" - "\0" -END +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END -#endif // APSTUDIO_INVOKED -#endif // English (United States) 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 a53268c0c517..eb83f76e620f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -556,11 +556,11 @@ VOID SetupUpgradeSettingsFile( settingsFilePath = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); oldSettingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\settings.xml"); - if (RtlDoesFileExists_U(settingsFilePath->Buffer)) - SetupDeleteDirectoryFile(settingsFilePath->Buffer); + if (!RtlDoesFileExists_U(settingsFilePath->Buffer)) + { + CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); + } - CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); - PhDereferenceObject(oldSettingsFileName); PhDereferenceObject(settingsFilePath); } \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 92acc4d711af..08c4009b0a16 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -60,13 +60,13 @@ NTSTATUS SetupUninstallBuild( // Remove the uninstaller. SetupDeleteUninstallFile(); + // Remove the ARP uninstall entry. + SetupDeleteUninstallKey(); + // Remove the previous installation. if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) goto CleanupExit; - // Remove the ARP uninstall entry. - SetupDeleteUninstallKey(); - ShowUninstallCompleteDialog(Context); return STATUS_SUCCESS; From d6b1d872fcf1afbd71fb7e3bb18f233918f6c612 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 02:58:21 +1000 Subject: [PATCH 155/839] Updater: Fix crash --- plugins/Updater/updater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index eda63f54957e..f0ca93c4a37e 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -434,7 +434,7 @@ BOOLEAN QueryUpdateData( { PhInitializeStringBuilder(&sb, 0x100); - for (SIZE_T i = 0; i < Context->BuildMessage->Length; i++) + for (SIZE_T i = 0; i < Context->BuildMessage->Length / sizeof(WCHAR); i++) { if (Context->BuildMessage->Data[i] == '\n') PhAppendStringBuilder2(&sb, L"\r\n"); From d6dc974c2c405958705a96a7df2ab9f9b09ebd41 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 05:20:14 +1000 Subject: [PATCH 156/839] Add web-setup to installer (build CustomSetupTool for web-setup, run build_release.cmd for full-setup) --- .../CustomSetupTool/download.c | 91 ++++---------- tools/CustomSetupTool/CustomSetupTool/error.c | 15 ++- .../CustomSetupTool/CustomSetupTool/extract.c | 27 +++- .../CustomSetupTool/include/appsup.h | 2 +- .../CustomSetupTool/include/setup.h | 110 +++++++--------- tools/CustomSetupTool/CustomSetupTool/main.c | 11 ++ tools/CustomSetupTool/CustomSetupTool/page1.c | 49 ++++---- tools/CustomSetupTool/CustomSetupTool/page2.c | 23 +++- tools/CustomSetupTool/CustomSetupTool/page3.c | 64 +++++----- tools/CustomSetupTool/CustomSetupTool/page4.c | 89 ++++++++----- tools/CustomSetupTool/CustomSetupTool/page5.c | 64 ++++------ tools/CustomSetupTool/CustomSetupTool/setup.c | 77 ++++++------ .../CustomSetupTool/uninstall.c | 10 +- .../CustomSetupTool/CustomSetupTool/update.c | 117 ++++++------------ 14 files changed, 357 insertions(+), 392 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 7d050109e1b8..9b6ee0885a3a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -75,13 +75,13 @@ PPH_STRING UpdateWindowsString( } BOOLEAN ParseVersionString( - _Inout_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _Inout_ PPH_SETUP_CONTEXT Context ) { PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - //PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); @@ -91,9 +91,9 @@ BOOLEAN ParseVersionString( PhStringToInteger64(&minorPart, 10, &minorInteger); PhStringToInteger64(&revisionPart, 10, &revisionInteger); - //Context->MajorVersion = (ULONG)majorInteger; - //Context->MinorVersion = (ULONG)minorInteger; - //Context->RevisionVersion = (ULONG)revisionInteger; + Context->LatestMajorVersion = (ULONG)majorInteger; + Context->LatestMinorVersion = (ULONG)minorInteger; + Context->LatestRevisionVersion = (ULONG)revisionInteger; return TRUE; } @@ -163,7 +163,7 @@ json_object_ptr json_get_object( BOOLEAN SetupQueryUpdateData( - _Inout_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _Inout_ PPH_SETUP_CONTEXT Context ) { BOOLEAN success = FALSE; @@ -196,7 +196,7 @@ BOOLEAN SetupQueryUpdateData( WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG) - ); + ); } if (!(httpConnectionHandle = WinHttpConnect( @@ -213,7 +213,7 @@ BOOLEAN SetupQueryUpdateData( if (!(httpRequestHandle = WinHttpOpenRequest( httpConnectionHandle, NULL, - L"/processhacker/nightly.php?latest", + L"/processhacker/nightly.php?phsetup", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, @@ -288,58 +288,15 @@ BOOLEAN SetupQueryUpdateData( Context->Signature = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "sig"))); Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "forum_url"))); Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "bin_url"))); - Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "setup_url"))); - //Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "message")); - - PH_STRINGREF remaining, majorPart, minorPart, revisionPart; - ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; + //Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "setup_url"))); + //Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "changelog")); - 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->LatestMajorVersion = (ULONG)majorInteger; - Context->LatestMinorVersion = (ULONG)minorInteger; - Context->LatestRevisionVersion = (ULONG)revisionInteger; - - /* 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")); - - PH_STRING_BUILDER sb; - PhInitializeStringBuilder(&sb, 0x100); - for (size_t i = 0; i < Context->BuildMessage->Length; i++) - { - if (Context->BuildMessage->Data[i] == '\n') - PhAppendFormatStringBuilder(&sb, L"\r\n"); - else - PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); - } - PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); - - CleanupJsonParser(jsonObject); - - if (PhIsNullOrEmptyString(Context->Signature)) + if (PhIsNullOrEmptyString(Context->Version)) goto CleanupExit; - if (!ParseVersionString(Context)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Version)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->RevVersion)) + if (PhIsNullOrEmptyString(Context->Signature)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->RelDate)) goto CleanupExit; @@ -349,9 +306,9 @@ BOOLEAN SetupQueryUpdateData( goto CleanupExit; if (PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) - goto CleanupExit;*/ - + if (PhIsNullOrEmptyString(Context->BinFileDownloadUrl)) + goto CleanupExit; + success = TRUE; CleanupExit: @@ -378,7 +335,7 @@ BOOLEAN SetupQueryUpdateData( } BOOLEAN UpdateDownloadUpdateData( - _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { BOOLEAN downloadSuccess = FALSE; @@ -467,13 +424,13 @@ BOOLEAN UpdateDownloadUpdateData( httpUrlComponents.dwUrlPathLength = (ULONG)-1; if (!WinHttpCrackUrl( - PhGetString(Context->SetupFileDownloadUrl), + PhGetString(Context->BinFileDownloadUrl), 0, 0, &httpUrlComponents )) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } @@ -503,7 +460,7 @@ BOOLEAN UpdateDownloadUpdateData( 0 ))) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } @@ -520,7 +477,7 @@ BOOLEAN UpdateDownloadUpdateData( 0 ))) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } @@ -534,7 +491,7 @@ BOOLEAN UpdateDownloadUpdateData( WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) ))) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } @@ -554,13 +511,13 @@ BOOLEAN UpdateDownloadUpdateData( 0 )) { - // context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } else @@ -586,7 +543,7 @@ BOOLEAN UpdateDownloadUpdateData( 0 )) { - //context->ErrorCode = GetLastError(); + Context->ErrorCode = GetLastError(); goto CleanupExit; } diff --git a/tools/CustomSetupTool/CustomSetupTool/error.c b/tools/CustomSetupTool/CustomSetupTool/error.c index ec3eb5ff1eb1..024920e772f7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/error.c +++ b/tools/CustomSetupTool/CustomSetupTool/error.c @@ -29,7 +29,20 @@ INT_PTR CALLBACK SetupErrorPage_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + if (context == NULL) + return FALSE; switch (uMsg) { diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 405c429a6cb0..6a1709ea3746 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -50,7 +50,7 @@ PVOID GetZipResourceData( } BOOLEAN SetupExtractBuild( - _In_ HWND Context + _In_ PPH_SETUP_CONTEXT Context ) { mz_bool status = MZ_FALSE; @@ -58,17 +58,32 @@ BOOLEAN SetupExtractBuild( ULONG64 currentLength = 0; mz_zip_archive zip_archive = { 0 }; PPH_STRING extractPath = NULL; - PVOID resourceBuffer; - ULONG resourceLength; SYSTEM_INFO info; GetNativeSystemInfo(&info); +#ifdef PH_BUILD_API + ULONG resourceLength; + PVOID resourceBuffer; + if (!(resourceBuffer = GetZipResourceData(&resourceLength))) goto CleanupExit; if (!(status = mz_zip_reader_init_mem(&zip_archive, resourceBuffer, resourceLength, 0))) goto CleanupExit; +#else + PPH_BYTES zipPathUtf8; + + if (!Context->SetupFilePath) + goto CleanupExit; + + zipPathUtf8 = PhConvertUtf16ToUtf8(PhGetString(Context->SetupFilePath)); + + if (!(status = mz_zip_reader_init_file(&zip_archive, zipPathUtf8->Buffer, 0))) + goto CleanupExit; + + PhDereferenceObject(zipPathUtf8); +#endif // Remove outdated files //for (ULONG i = 0; i < ARRAYSIZE(SetupRemoveFiles); i++) @@ -98,7 +113,7 @@ BOOLEAN SetupExtractBuild( } InterlockedExchange64(&ExtractTotalLength, totalLength); - SendMessage(Context, WM_START_SETUP, 0, 0); + SendMessage(Context->ExtractPageHandle, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) { @@ -151,7 +166,7 @@ BOOLEAN SetupExtractBuild( if ((zipFileCrc32 = mz_crc32(zipFileCrc32, buffer, bufferLength)) != zipFileStat.m_crc32) goto CleanupExit; - extractPath = PhConcatStrings(3, PhGetString(SetupInstallPath), L"\\", PhGetString(fileName)); + extractPath = PhConcatStrings(3, PhGetString(Context->SetupInstallPath), L"\\", PhGetString(fileName)); if (fullSetupPath = PhGetFullPath(extractPath->Buffer, &indexOfFileName)) { @@ -212,7 +227,7 @@ BOOLEAN SetupExtractBuild( InterlockedExchange64(&ExtractCurrentLength, currentLength); - SendMessage(Context, WM_UPDATE_SETUP, 0, (LPARAM)PhGetBaseName(extractPath)); + SendMessage(Context->ExtractPageHandle, WM_UPDATE_SETUP, 0, (LPARAM)PhGetBaseName(extractPath)); NtClose(fileHandle); mz_free(buffer); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h index e74fa065ef9f..8b71866e1cdf 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h @@ -36,7 +36,7 @@ typedef struct _SETUP_REMOVE_FILE PWSTR FileName; } SETUP_REMOVE_FILE, *PSETUP_REMOVE_FILE; -VOID SetupFindInstallDirectory( +PPH_STRING SetupFindInstallDirectory( VOID ); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 6c9f40621a2d..462141663909 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -64,17 +64,6 @@ #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 { @@ -86,12 +75,34 @@ typedef enum _SETUP_COMMAND_TYPE typedef struct _PH_SETUP_CONTEXT { - HWND WindowHandle; HWND PropSheetHandle; + HWND PropSheetBackHandle; + HWND PropSheetForwardHandle; + HWND PropSheetCancelHandle; + + HWND WelcomePageHandle; + HWND EulaPageHandle; + HWND ConfigPageHandle; + HWND DownloadPageHandle; + HWND ExtractPageHandle; + HWND FinalPageHandle; + HWND ErrorPageHandle; HICON IconSmallHandle; HICON IconLargeHandle; + PPH_STRING SetupInstallPath; + BOOLEAN SetupCreateDesktopShortcut; + BOOLEAN SetupCreateDesktopShortcutAllUsers; + BOOLEAN SetupCreateDefaultTaskManager; + BOOLEAN SetupCreateSystemStartup; + BOOLEAN SetupCreateMinimizedSystemStartup; + BOOLEAN SetupInstallDebuggingTools; + BOOLEAN SetupInstallPeViewAssociations; + BOOLEAN SetupInstallKphService; + BOOLEAN SetupResetSettings; + BOOLEAN SetupStartAppAfterExit; + ULONG ErrorCode; PPH_STRING Version; PPH_STRING RevVersion; @@ -102,8 +113,21 @@ typedef struct _PH_SETUP_CONTEXT PPH_STRING ReleaseNotesUrl; PPH_STRING BinFileDownloadUrl; - PPH_STRING SetupFileDownloadUrl; + //PPH_STRING SetupFileDownloadUrl; PPH_STRING SetupFilePath; + + HWND MainHeaderHandle; + HWND StatusHandle; + HWND SubStatusHandle; + HWND ProgressHandle; + + BOOLEAN SetupRunning; + ULONG CurrentMajorVersion; + ULONG CurrentMinorVersion; + ULONG CurrentRevisionVersion; + ULONG LatestMajorVersion; + ULONG LatestMinorVersion; + ULONG LatestRevisionVersion; } PH_SETUP_CONTEXT, *PPH_SETUP_CONTEXT; VOID SetupLoadImage( @@ -155,21 +179,13 @@ INT_PTR CALLBACK SetupErrorPage_WndProc( // page4.c -typedef struct _SETUP_PROGRESS_THREAD -{ - HWND DialogHandle; - HWND PropSheetHandle; - HICON PropSheetIcon; -} SETUP_PROGRESS_THREAD, *PSETUP_PROGRESS_THREAD; - -extern BOOLEAN SetupRunning; extern ULONG64 ExtractCurrentLength; extern ULONG64 ExtractTotalLength; // setup.c -VOID SetupInstallKph( - VOID +VOID SetupStartKph( + _In_ PPH_SETUP_CONTEXT Context ); ULONG SetupUninstallKph( @@ -177,7 +193,7 @@ ULONG SetupUninstallKph( ); NTSTATUS SetupCreateUninstallKey( - VOID + _In_ PPH_SETUP_CONTEXT Context ); NTSTATUS SetupDeleteUninstallKey( @@ -185,23 +201,23 @@ NTSTATUS SetupDeleteUninstallKey( ); VOID SetupSetWindowsOptions( - VOID + _In_ PPH_SETUP_CONTEXT Context ); VOID SetupDeleteWindowsOptions( - VOID + _In_ PPH_SETUP_CONTEXT Context ); VOID SetupCreateUninstallFile( - VOID + _In_ PPH_SETUP_CONTEXT Context ); VOID SetupDeleteUninstallFile( - VOID + _In_ PPH_SETUP_CONTEXT Context ); BOOLEAN SetupExecuteProcessHacker( - _In_ HWND Parent + _In_ PPH_SETUP_CONTEXT Context ); VOID SetupUpgradeSettingsFile( @@ -210,48 +226,18 @@ VOID SetupUpgradeSettingsFile( // download.c -typedef struct _PH_SETUP_DOWNLOAD_CONTEXT -{ - HWND DialogHandle; - HWND PropSheetHandle; - HWND MainHeaderHandle; - HWND StatusHandle; - HWND SubStatusHandle; - HWND ProgressHandle; - - ULONG CurrentMajorVersion; - ULONG CurrentMinorVersion; - ULONG CurrentRevisionVersion; - ULONG LatestMajorVersion; - ULONG LatestMinorVersion; - ULONG LatestRevisionVersion; - - ULONG ErrorCode; - PPH_STRING Version; - PPH_STRING RevVersion; - PPH_STRING RelDate; - PPH_STRING Size; - PPH_STRING Hash; - PPH_STRING Signature; - PPH_STRING ReleaseNotesUrl; - - PPH_STRING BinFileDownloadUrl; - PPH_STRING SetupFileDownloadUrl; - PPH_STRING SetupFilePath; -} PH_SETUP_DOWNLOAD_CONTEXT, *PPH_SETUP_DOWNLOAD_CONTEXT; - BOOLEAN SetupQueryUpdateData( - _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); BOOLEAN UpdateDownloadUpdateData( - _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); // extract.c BOOLEAN SetupExtractBuild( - _In_ HWND Context + _In_ PPH_SETUP_CONTEXT Context ); // update.c diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index fe2fb4e1d8a1..7d0f039d82c2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -99,8 +99,19 @@ INT CALLBACK MainPropSheet_Callback( context = PhAllocate(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->PropSheetHandle = 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); SetProp(hwndDlg, L"SetupContext", (HANDLE)context); } diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/page1.c index 8c98be740860..3d8dbf6fa0d2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/page1.c @@ -22,23 +22,16 @@ #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->PropSheetHandle, NULL); + SetForegroundWindow(Context->PropSheetHandle); } -static VOID LoadSetupIcons( +static VOID SetupLoadIcons( _In_ HWND hwndDlg ) { @@ -73,15 +66,30 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + 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); @@ -102,21 +110,12 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( { // Reset the button state. PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT); - - // Hide the back button. - //ShowWindow(GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_BACK), SW_HIDE); - - // HACK: Focus the next button (reset after changing button state). - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_NEXT), TRUE); } break; case PSN_KILLACTIVE: { // Enable the back button. PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT | PSWIZB_BACK); - - // Show the back button. - //ShowWindow(GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_BACK), SW_SHOW); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/page2.c index a987b93b5c80..a756f9f1a836 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page2.c +++ b/tools/CustomSetupTool/CustomSetupTool/page2.c @@ -27,7 +27,20 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + if (context == NULL) + return FALSE; switch (uMsg) { @@ -64,15 +77,13 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( { case PSN_SETACTIVE: { - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_NEXT), TRUE); - - // Disable the Next button - //PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_BACK); + // HACK: Prevent the textbox text from being selected (temp fix). + PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->PropSheetForwardHandle, TRUE); } break; case PSN_QUERYINITIALFOCUS: { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(context->PropSheetHandle, IDC_PROPSHEET_CANCEL)); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)context->PropSheetCancelHandle); } return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/page3.c b/tools/CustomSetupTool/CustomSetupTool/page3.c index 800f73b38a39..b4dbe1e2adb2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page3.c +++ b/tools/CustomSetupTool/CustomSetupTool/page3.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,14 +29,25 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + 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); @@ -66,7 +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); - SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetString(SetupInstallPath)); + context->SetupInstallPath = SetupFindInstallDirectory(); + + SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetString(context->SetupInstallPath)); Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_CHECK), TRUE); Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK), TRUE); //Button_SetCheck(GetDlgItem(hwndDlg, IDC_KPH_CHECK), TRUE); @@ -96,8 +97,8 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( PhFreeFileDialog(fileDialog); - PhSwapReference(&SetupInstallPath, fileDialogFolderPath); - SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetStringOrEmpty(SetupInstallPath)); + PhSwapReference(&context->SetupInstallPath, fileDialogFolderPath); + SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetStringOrEmpty(context->SetupInstallPath)); } } break; @@ -127,25 +128,24 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( { case PSN_WIZNEXT: { - 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; + context->SetupInstallPath = PhGetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_DIRECTORY)); + 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; #ifdef PH_BUILD_API SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)IDD_DIALOG4); -#else - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)IDD_DIALOG5); + return TRUE; #endif } - return TRUE; + break; case PSN_QUERYINITIALFOCUS: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_FOLDER_BROWSE)); return TRUE; diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index f0984811c876..40900e620fa1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -27,7 +27,7 @@ ULONG64 ExtractCurrentLength = 0; ULONG64 ExtractTotalLength = 0; NTSTATUS SetupProgressThread( - _In_ PSETUP_PROGRESS_THREAD Context + _In_ PPH_SETUP_CONTEXT Context ) { //if (SetupInstallDebuggingTools) @@ -42,39 +42,37 @@ NTSTATUS SetupProgressThread( goto CleanupExit; // Create the install folder path. - if (!CreateDirectoryPath(PhGetString(SetupInstallPath))) + if (!CreateDirectoryPath(PhGetString(Context->SetupInstallPath))) goto CleanupExit; // Upgrade the 2.x settings file. SetupUpgradeSettingsFile(); // Remove the previous installation. - if (SetupResetSettings) - RemoveDirectoryPath(PhGetString(SetupInstallPath)); + if (Context->SetupResetSettings) + RemoveDirectoryPath(PhGetString(Context->SetupInstallPath)); // Create the ARP uninstall entries. - SetupCreateUninstallKey(); + SetupCreateUninstallKey(Context); // Create the uninstaller. - SetupCreateUninstallFile(); + SetupCreateUninstallFile(Context); // Create autorun and shortcuts. - SetupSetWindowsOptions(); + SetupSetWindowsOptions(Context); // Setup new installation. - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; // Install updated kernel driver - if (SetupInstallKphService) - SetupInstallKph(); + if (Context->SetupInstallKphService) + SetupStartKph(Context); - 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); return STATUS_FAIL_CHECK; } @@ -85,12 +83,27 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + 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); @@ -122,7 +135,15 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( case PSN_SETACTIVE: { 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); + + SetWindowText(context->MainHeaderHandle, L"Installing..."); + SetWindowText(context->StatusHandle, L""); + SetWindowText(context->SubStatusHandle, L"Progress: ~ of ~ (0.0%)"); // Disable Next/Back buttons PropSheet_SetWizButtons(context->PropSheetHandle, 0); @@ -131,12 +152,7 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( { SetupRunning = TRUE; - // Setup the progress thread - progress = PhCreateAlloc(sizeof(SETUP_PROGRESS_THREAD)); - progress->DialogHandle = hwndDlg; - progress->PropSheetHandle = context->PropSheetHandle; - - if (threadHandle = PhCreateThread(0, SetupProgressThread, progress)) + if (threadHandle = PhCreateThread(0, SetupProgressThread, context)) NtClose(threadHandle); } } @@ -146,26 +162,34 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( break; case WM_START_SETUP: { - SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), +#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); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), L"Progress: ~ of ~ (0.0%)"); - SendMessage(GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); +#else + SetWindowText(context->MainHeaderHandle, PhaFormatString( + L"Installing Process Hacker %lu.%lu.%lu", + context->LatestMajorVersion, + context->LatestMinorVersion, + context->LatestRevisionVersion + )->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); } @@ -174,13 +198,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); } } diff --git a/tools/CustomSetupTool/CustomSetupTool/page5.c b/tools/CustomSetupTool/CustomSetupTool/page5.c index 7fdd40939b22..737cab677e7a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page5.c +++ b/tools/CustomSetupTool/CustomSetupTool/page5.c @@ -23,7 +23,7 @@ #include NTSTATUS SetupDownloadProgressThread( - _In_ PPH_SETUP_DOWNLOAD_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { if (!SetupQueryUpdateData(Context)) @@ -33,13 +33,11 @@ NTSTATUS SetupDownloadProgressThread( goto CleanupExit; PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_DIALOG4); - PhDereferenceObject(Context); return STATUS_SUCCESS; CleanupExit: PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_ERROR); - PhDereferenceObject(Context); return STATUS_FAIL_CHECK; } @@ -50,7 +48,20 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( _Inout_ LPARAM lParam ) { - PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)GetProp(GetParent(hwndDlg), L"SetupContext"); + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + if (context == NULL) + return FALSE; switch (uMsg) { @@ -62,10 +73,6 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), -12, FW_SEMIBOLD); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), -12, FW_NORMAL); - SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), L"Starting download..."); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), L"Starting download..."); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), L""); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; @@ -76,43 +83,24 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( switch (pageNotify->hdr.code) { - case PSN_QUERYCANCEL: - { - if (SetupRunning && DialogPromptExit(hwndDlg)) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); - return TRUE; - } - } - break; case PSN_SETACTIVE: { HANDLE threadHandle; - PPH_SETUP_DOWNLOAD_CONTEXT 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); + + SetWindowText(context->MainHeaderHandle, L"Starting download..."); + SetWindowText(context->StatusHandle, L"Starting download..."); + SetWindowText(context->SubStatusHandle, L""); // Disable Next/Back buttons PropSheet_SetWizButtons(context->PropSheetHandle, 0); - if (!SetupRunning) - { - SetupRunning = TRUE; - - progress = PhCreateAlloc(sizeof(PH_SETUP_DOWNLOAD_CONTEXT)); - memset(progress, 0, sizeof(PH_SETUP_DOWNLOAD_CONTEXT)); - - progress->DialogHandle = hwndDlg; - progress->PropSheetHandle = context->PropSheetHandle; - progress->CurrentMajorVersion = PHAPP_VERSION_MAJOR; - progress->CurrentMinorVersion = PHAPP_VERSION_MINOR; - progress->CurrentRevisionVersion = PHAPP_VERSION_REVISION; - progress->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); - progress->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); - progress->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); - progress->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); - - if (threadHandle = PhCreateThread(0, SetupDownloadProgressThread, progress)) - NtClose(threadHandle); - } + if (threadHandle = PhCreateThread(0, SetupDownloadProgressThread, context)) + NtClose(threadHandle); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index eb83f76e620f..43affc76c917 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -27,7 +27,7 @@ PH_STRINGREF UninstallKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ProcessHacker"); NTSTATUS SetupCreateUninstallKey( - VOID + _In_ PPH_SETUP_CONTEXT Context ) { NTSTATUS status; @@ -50,7 +50,7 @@ NTSTATUS SetupCreateUninstallKey( { PPH_STRING tempString; - tempString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker.exe,0"); + tempString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker.exe,0"); PhStringRefToUnicodeString(&tempString->sr, &value); RtlInitUnicodeString(&name, L"DisplayIcon"); PhStringRefToUnicodeString(&tempString->sr, &value); @@ -70,7 +70,7 @@ NTSTATUS SetupCreateUninstallKey( NtSetValueKey(keyHandle, &name, 0, REG_SZ, value.Buffer, (ULONG)value.MaximumLength); RtlInitUnicodeString(&name, L"InstallLocation"); - RtlInitUnicodeString(&value, PhGetString(SetupInstallPath)); + RtlInitUnicodeString(&value, PhGetString(Context->SetupInstallPath)); NtSetValueKey(keyHandle, &name, 0, REG_SZ, value.Buffer, (ULONG)value.MaximumLength); RtlInitUnicodeString(&name, L"Publisher"); @@ -78,7 +78,7 @@ NTSTATUS SetupCreateUninstallKey( NtSetValueKey(keyHandle, &name, 0, REG_SZ, value.Buffer, (ULONG)value.MaximumLength); RtlInitUnicodeString(&name, L"UninstallString"); - tempString = PhFormatString(L"\"%s\\processhacker-setup.exe\" -uninstall", PhGetString(SetupInstallPath)); + tempString = PhFormatString(L"\"%s\\processhacker-setup.exe\" -uninstall", PhGetString(Context->SetupInstallPath)); PhStringRefToUnicodeString(&tempString->sr, &value); NtSetValueKey(keyHandle, &name, 0, REG_SZ, value.Buffer, (ULONG)value.MaximumLength); PhDereferenceObject(tempString); @@ -121,15 +121,15 @@ NTSTATUS SetupDeleteUninstallKey( return status; } -VOID SetupFindInstallDirectory( +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%"); @@ -143,36 +143,38 @@ VOID SetupFindInstallDirectory( { if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programW6432))) { - SetupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); + setupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); } } else { if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programFiles))) { - SetupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); + setupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); } } } - 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)); + PathRemoveBackslash(PhGetString(setupInstallPath)); + + return setupInstallPath; } VOID SetupCreateUninstallFile( - VOID + _In_ PPH_SETUP_CONTEXT Context ) { PPH_STRING backupFilePath; PPH_STRING uninstallFilePath; - backupFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.bak"); - uninstallFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.exe"); + backupFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.bak"); + uninstallFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.exe"); if (RtlDoesFileExists_U(backupFilePath->Buffer)) { @@ -191,13 +193,12 @@ VOID SetupCreateUninstallFile( } VOID SetupDeleteUninstallFile( - VOID + _In_ PPH_SETUP_CONTEXT Context ) { PPH_STRING uninstallFilePath; - // NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer - uninstallFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.exe"); + uninstallFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.exe"); if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { @@ -261,11 +262,11 @@ VOID SetupDeleteUninstallFile( PhDereferenceObject(uninstallFilePath); } -VOID SetupInstallKph( - VOID +VOID SetupStartKph( + _In_ PPH_SETUP_CONTEXT Context ) { - PPH_STRING clientPath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); + PPH_STRING clientPath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\ProcessHacker.exe"); if (RtlDoesFileExists_U(PhGetString(clientPath))) { @@ -336,7 +337,7 @@ ULONG SetupUninstallKph( } 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"); @@ -344,33 +345,33 @@ VOID SetupSetWindowsOptions( PPH_STRING clientPathString; PPH_STRING startmenuFolderString; - clientPathString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); + clientPathString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\ProcessHacker.exe"); if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) { SetupCreateLink( PhGetString(startmenuFolderString), PhGetString(clientPathString), - PhGetString(SetupInstallPath) + PhGetString(Context->SetupInstallPath) ); PhDereferenceObject(startmenuFolderString); } if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk")) { - PPH_STRING peviewPathString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\peview.exe"); + PPH_STRING peviewPathString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\peview.exe"); SetupCreateLink( PhGetString(startmenuFolderString), PhGetString(peviewPathString), - PhGetString(SetupInstallPath) + PhGetString(Context->SetupInstallPath) ); PhDereferenceObject(peviewPathString); PhDereferenceObject(startmenuFolderString); } - if (SetupResetSettings) + if (Context->SetupResetSettings) { PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); @@ -378,7 +379,7 @@ VOID SetupSetWindowsOptions( PhDereferenceObject(settingsFileName); } - if (SetupCreateDefaultTaskManager) + if (Context->SetupCreateDefaultTaskManager) { HANDLE keyHandle; @@ -401,7 +402,7 @@ VOID SetupSetWindowsOptions( } } - if (SetupCreateSystemStartup) + if (Context->SetupCreateSystemStartup) { HANDLE keyHandle; @@ -418,7 +419,7 @@ 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"\""); @@ -428,7 +429,7 @@ VOID SetupSetWindowsOptions( } } - if (SetupCreateDesktopShortcut) + if (Context->SetupCreateDesktopShortcut) { PPH_STRING desktopFolderString; @@ -437,12 +438,12 @@ VOID SetupSetWindowsOptions( SetupCreateLink( PhGetString(desktopFolderString), PhGetString(clientPathString), - PhGetString(SetupInstallPath) + PhGetString(Context->SetupInstallPath) ); PhDereferenceObject(desktopFolderString); } } - else if (SetupCreateDesktopShortcutAllUsers) + else if (Context->SetupCreateDesktopShortcutAllUsers) { PPH_STRING startmenuFolderString; @@ -451,7 +452,7 @@ VOID SetupSetWindowsOptions( SetupCreateLink( PhGetString(startmenuFolderString), PhGetString(clientPathString), - PhGetString(SetupInstallPath) + PhGetString(Context->SetupInstallPath) ); PhDereferenceObject(startmenuFolderString); } @@ -459,7 +460,7 @@ VOID SetupSetWindowsOptions( } VOID SetupDeleteWindowsOptions( - 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"); @@ -521,18 +522,18 @@ VOID SetupDeleteWindowsOptions( } BOOLEAN SetupExecuteProcessHacker( - _In_ HWND Parent + _In_ PPH_SETUP_CONTEXT Context ) { BOOLEAN success = FALSE; PPH_STRING clientPath; - clientPath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); + clientPath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\ProcessHacker.exe"); if (RtlDoesFileExists_U(clientPath->Buffer)) { success = PhShellExecuteEx( - Parent, + Context->PropSheetHandle, clientPath->Buffer, NULL, SW_SHOWDEFAULT, diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 08c4009b0a16..c03dbaf6713f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -44,7 +44,7 @@ NTSTATUS SetupUninstallBuild( _In_ PPH_SETUP_UNINSTALL_CONTEXT Context ) { - SetupFindInstallDirectory(); + //context->SetupInstallPath = SetupFindInstallDirectory(); // Stop Process Hacker. if (!ShutdownProcessHacker()) @@ -55,17 +55,17 @@ NTSTATUS SetupUninstallBuild( goto CleanupExit; // Remove autorun and shortcuts. - SetupDeleteWindowsOptions(); + //SetupDeleteWindowsOptions(); // Remove the uninstaller. - SetupDeleteUninstallFile(); + //SetupDeleteUninstallFile(); // Remove the ARP uninstall entry. SetupDeleteUninstallKey(); // Remove the previous installation. - if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) - goto CleanupExit; + //if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) + // goto CleanupExit; ShowUninstallCompleteDialog(Context); return STATUS_SUCCESS; diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 231bd124a266..277057873142 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -22,28 +22,16 @@ #include #include -typedef struct _PH_SETUP_UPDATE_CONTEXT -{ - 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_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(); + Context->SetupInstallPath = SetupFindInstallDirectory(); if (!ShutdownProcessHacker()) goto CleanupExit; @@ -51,68 +39,42 @@ NTSTATUS SetupUpdateBuild( if (!SetupUninstallKph()) goto CleanupExit; - SetupCreateUninstallKey(); - SetupCreateUninstallFile(); - //SetupSetWindowsOptions(); + SetupCreateUninstallKey(Context); + SetupCreateUninstallFile(Context); - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; - if (SetupInstallKphService) - SetupInstallKph(); + SetupStartKph(Context); - if (!SetupExecuteProcessHacker(Context->DialogHandle)) + if (!SetupExecuteProcessHacker(Context)) goto CleanupExit; - PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); + PostMessage(Context->PropSheetHandle, WM_QUIT, 0, 0); PhDereferenceObject(Context); return STATUS_SUCCESS; CleanupExit: - PostMessage(Context->DialogHandle, IDD_ERROR, 0, 0); + PostMessage(Context->PropSheetHandle, 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 - ) -{ - //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); -} - VOID TaskDialogCreateIcons( - _In_ PPH_SETUP_UPDATE_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { HICON largeIcon; @@ -136,15 +98,8 @@ VOID TaskDialogCreateIcons( 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 TaskDialogLinkClicked( - _In_ PPH_SETUP_UPDATE_CONTEXT Context - ) -{ - + SendMessage(Context->PropSheetHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); + SendMessage(Context->PropSheetHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); } LRESULT CALLBACK TaskDialogSubclassProc( @@ -156,7 +111,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( _In_ ULONG_PTR dwRefData ) { - PPH_SETUP_UPDATE_CONTEXT context = (PPH_SETUP_UPDATE_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -180,7 +135,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); } -HRESULT CALLBACK CheckingForUpdatesCallbackProc( +HRESULT CALLBACK SetupUpdatingTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -188,7 +143,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) { @@ -205,8 +160,8 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( return S_OK; } -VOID ShowCheckForUpdatesDialog( - _In_ PPH_SETUP_UPDATE_CONTEXT Context +VOID SetupShowUpdatingDialog( + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -214,15 +169,20 @@ 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 = PhaFormatString(L"Updating to version %lu.%lu.%lu...", PHAPP_VERSION_MAJOR, PHAPP_VERSION_MINOR, PHAPP_VERSION_REVISION)->Buffer; - config.cxWidth = 200; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + config.pszMainInstruction = PhaFormatString( + L"Updating to version %lu.%lu.%lu...", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION + )->Buffer; + + SendMessage(Context->PropSheetHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -233,22 +193,22 @@ 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) { case TDN_CREATED: { - context->DialogHandle = hwndDlg; + context->PropSheetHandle = 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 + // Subclass the Taskdialog. SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - // Navigate to the first page - ShowCheckForUpdatesDialog(context); + // Navigate to the first page. + SetupShowUpdatingDialog(context); SendMessage(hwndDlg, WM_TASKDIALOGINIT, 0, 0); } @@ -280,6 +240,5 @@ VOID SetupShowUpdateDialog( TaskDialogIndirect(&config, NULL, NULL, NULL); - FreeUpdateContext(context); PhDeleteAutoPool(&autoPool); } \ No newline at end of file From 72dfd061f0b219beb31b06d6882519152a1b3fce Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 05:21:18 +1000 Subject: [PATCH 157/839] Updater: Fix error checking --- plugins/Updater/updater.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index f0ca93c4a37e..65ffb77af54e 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -447,14 +447,11 @@ BOOLEAN QueryUpdateData( CleanupJsonParser(jsonObject); - if (PhIsNullOrEmptyString(Context->Signature)) + if (PhIsNullOrEmptyString(Context->Version)) goto CleanupExit; - if (!ParseVersionString(Context)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Version)) - goto CleanupExit; if (PhIsNullOrEmptyString(Context->RevVersion)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->RelDate)) @@ -467,6 +464,8 @@ BOOLEAN QueryUpdateData( goto CleanupExit; if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) goto CleanupExit; + if (PhIsNullOrEmptyString(Context->Signature)) + goto CleanupExit; success = TRUE; From 6cd629e5e18e3752bd2592321bc82b048769f903 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 05:21:44 +1000 Subject: [PATCH 158/839] Add Information and Log window icons --- ProcessHacker/infodlg.c | 3 +++ ProcessHacker/logwnd.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 41947bfe8f86..176140cc2662 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -46,6 +46,9 @@ static INT_PTR CALLBACK PhpInformationDlgProc( PINFORMATION_CONTEXT context = (PINFORMATION_CONTEXT)lParam; PPH_LAYOUT_MANAGER layoutManager; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); SetDlgItemText(hwndDlg, IDC_TEXT, context->String); diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 0a9b71a94b62..0fde6d46e648 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -156,6 +156,9 @@ INT_PTR CALLBACK PhpLogDlgProc( { case WM_INITDIALOG: { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); PhSetListViewStyle(ListViewHandle, FALSE, TRUE); PhSetControlTheme(ListViewHandle, L"explorer"); From deca2034938e21d2c9b5d106ed38902bf22bd100 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 May 2017 07:38:46 +1000 Subject: [PATCH 159/839] Setup: Fix binary version, Fix update error handling, Fix re-running setup from installation directory, Remove focus workaround --- .../CustomSetupTool/CustomSetupTool.vcxproj | 3 - .../CustomSetupTool.vcxproj.filters | 5 -- .../CustomSetupTool/CustomSetupTool/extract.c | 17 +++++- .../CustomSetupTool/include/setup.h | 2 +- tools/CustomSetupTool/CustomSetupTool/page2.c | 26 +++++--- tools/CustomSetupTool/CustomSetupTool/page4.c | 18 +++--- .../CustomSetupTool/resources/version.rc | 11 ++-- tools/CustomSetupTool/CustomSetupTool/setup.c | 30 ++++++++-- .../CustomSetupTool/CustomSetupTool/update.c | 60 ++++++++++++++++++- 9 files changed, 135 insertions(+), 37 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 227ad5d2ce4f..888e8bbaa14f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -172,9 +172,6 @@ - - - diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index 6557bf933011..b2499c2417c6 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -195,11 +195,6 @@ Resource Files\Content - - - Resource Files\Content - - Resource Files\Content diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 6a1709ea3746..850a2322074c 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -190,7 +190,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, diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 462141663909..1c8852690d86 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -208,7 +208,7 @@ VOID SetupDeleteWindowsOptions( _In_ PPH_SETUP_CONTEXT Context ); -VOID SetupCreateUninstallFile( +BOOLEAN SetupCreateUninstallFile( _In_ PPH_SETUP_CONTEXT Context ); diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/page2.c index a756f9f1a836..87c3bdbe90c1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page2.c +++ b/tools/CustomSetupTool/CustomSetupTool/page2.c @@ -58,7 +58,6 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( if (eulaTextString = PhConvertUtf8ToUtf16(resourceBuffer)) { SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT1), eulaTextString->Buffer); - PhDereferenceObject(eulaTextString); } @@ -75,17 +74,26 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( switch (pageNotify->hdr.code) { - case PSN_SETACTIVE: - { - // HACK: Prevent the textbox text from being selected (temp fix). - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->PropSheetForwardHandle, TRUE); - } - 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: { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)context->PropSheetCancelHandle); + HWND handle = (HWND)lParam; + + // Prevent the edit control text from being selected. + SendMessage(handle, EM_SETSEL, -1, 0); + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); + return TRUE; } - return TRUE; } } break; diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index 40900e620fa1..1c6b89477a95 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -30,9 +30,6 @@ NTSTATUS SetupProgressThread( _In_ PPH_SETUP_CONTEXT Context ) { - //if (SetupInstallDebuggingTools) - // SetupDownloadThread(Arguments) - // Stop Process Hacker. if (!ShutdownProcessHacker()) goto CleanupExit; @@ -52,10 +49,13 @@ NTSTATUS SetupProgressThread( if (Context->SetupResetSettings) RemoveDirectoryPath(PhGetString(Context->SetupInstallPath)); + // Create the uninstaller. + if (!SetupCreateUninstallFile(Context)) + goto CleanupExit; + // Create the ARP uninstall entries. SetupCreateUninstallKey(Context); - // Create the uninstaller. - SetupCreateUninstallFile(Context); + // Create autorun and shortcuts. SetupSetWindowsOptions(Context); @@ -163,8 +163,12 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( case WM_START_SETUP: { #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); + 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 %lu.%lu.%lu", diff --git a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc index aad29825fe0b..780765938665 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc @@ -63,8 +63,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,0,0,0 - PRODUCTVERSION 3,0,0,0 + FILEVERSION PHAPP_VERSION_NUMBER + PRODUCTVERSION PHAPP_VERSION_NUMBER FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -81,12 +81,12 @@ BEGIN BEGIN VALUE "CompanyName", "Process Hacker" VALUE "FileDescription", "Process Hacker Setup" - VALUE "FileVersion", "3.0.0.0" + 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", "3.0.0.0" + VALUE "ProductVersion", PHAPP_VERSION_STRING END END BLOCK "VarFileInfo" @@ -100,9 +100,12 @@ END // // RCDATA // +#if defined(APSTUDIO_INVOKED) || defined(PH_BUILD_API) IDR_BIN_DATA RCDATA "../../../../build/output/processhacker-build-bin.zip" +#endif + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 43affc76c917..9f0a316e62d4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -166,30 +166,50 @@ PPH_STRING SetupFindInstallDirectory( return setupInstallPath; } -VOID SetupCreateUninstallFile( +BOOLEAN SetupCreateUninstallFile( _In_ PPH_SETUP_CONTEXT Context ) { + PH_STRINGREF currentFilePath; PPH_STRING backupFilePath; PPH_STRING uninstallFilePath; + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->ImagePathName, ¤tFilePath); + + // Check if the user has started the setup from the installation folder. + if (PhStartsWithStringRef2(¤tFilePath, PhGetString(Context->SetupInstallPath), TRUE)) + { + // Do nothing, latest version already in the installation folder. + return TRUE; + } + backupFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.bak"); uninstallFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.exe"); if (RtlDoesFileExists_U(backupFilePath->Buffer)) { - // Move to temp directory - //MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer); + // TODO: Move to temp directory } if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { - MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer); + if (!MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer)) + { + Context->ErrorCode = GetLastError(); + PhDereferenceObject(uninstallFilePath); + return FALSE; + } } - CopyFile(NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, uninstallFilePath->Buffer, FALSE); + if (!CopyFile(currentFilePath.Buffer, uninstallFilePath->Buffer, FALSE)) + { + Context->ErrorCode = GetLastError(); + PhDereferenceObject(uninstallFilePath); + return FALSE; + } PhDereferenceObject(uninstallFilePath); + return TRUE; } VOID SetupDeleteUninstallFile( diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 277057873142..1ba229e2cf3e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -39,8 +39,10 @@ NTSTATUS SetupUpdateBuild( if (!SetupUninstallKph()) goto CleanupExit; + if (!SetupCreateUninstallFile(Context)) + goto CleanupExit; + SetupCreateUninstallKey(Context); - SetupCreateUninstallFile(Context); if (!SetupExtractBuild(Context)) goto CleanupExit; @@ -56,7 +58,7 @@ NTSTATUS SetupUpdateBuild( CleanupExit: - PostMessage(Context->PropSheetHandle, IDD_ERROR, 0, 0); + PostMessage(Context->PropSheetHandle, WM_APP + IDD_ERROR, 0, 0); PhDereferenceObject(Context); return STATUS_FAIL_CHECK; } @@ -102,6 +104,55 @@ VOID TaskDialogCreateIcons( SendMessage(Context->PropSheetHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); } +HRESULT CALLBACK SetupErrorTaskDialogCallbackProc( + _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: + { + + } + break; + } + + return S_OK; +} + +VOID SetupShowUpdatingErrorDialog( + _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_SHOW_MARQUEE_PROGRESS_BAR | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS; + 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 = PhaFormatString( + L"Error updating to the latest version...", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION + )->Buffer; + + if (Context->ErrorCode) + config.pszContent = PhGetStatusMessage(0, Context->ErrorCode)->Buffer; + SendMessage(Context->PropSheetHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -130,6 +181,11 @@ LRESULT CALLBACK TaskDialogSubclassProc( RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); } break; + case WM_APP + IDD_ERROR: + { + SetupShowUpdatingErrorDialog(context); + } + break; } return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); From 2973c03390ff0c9e494d55caf8c822d5862347c1 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 24 May 2017 17:21:52 +1000 Subject: [PATCH 160/839] Update ntpsapi.h Add missing PROCESS_HANDLE_TRACING flags --- phnt/include/ntpsapi.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index ead669d1bb9e..62a3e96da340 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -439,21 +439,27 @@ typedef struct _PROCESS_SESSION_INFORMATION ULONG SessionId; } PROCESS_SESSION_INFORMATION, *PPROCESS_SESSION_INFORMATION; +#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 { From 35b0b98971d217ce52cd49cf6112b494f174ad46 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 24 May 2017 17:37:49 +1000 Subject: [PATCH 161/839] Add missing window icons --- ProcessHacker/memrslt.c | 3 +++ plugins/ExtraPlugins/dialog.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index 36d36deaf05e..28c177c91ede 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -278,6 +278,9 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( { HWND lvHandle; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhRegisterDialog(hwndDlg); { diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index b924fe09d2d9..1e0c1b42c6b2 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -232,6 +232,9 @@ INT_PTR CALLBACK CloudPluginsDlgProc( { case WM_INITDIALOG: { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + context->DialogHandle = hwndDlg; context->PluginMenuActiveId = IDC_INSTALLED; context->PluginMenuActive = GetDlgItem(hwndDlg, IDC_INSTALLED); From d57d7da602bc9b3cd404e08703784ee636958c4a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 15:00:36 +1000 Subject: [PATCH 162/839] Fix modules tab LoadCount for x32 (2/2) --- phlib/native.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 056c2a60e809..d00fdb099868 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -3148,8 +3148,8 @@ BOOLEAN NTAPI PhpEnumProcessModulesCallback( NULL ))) { - // HACK: Fixup the module load count (64bit only). - // Temp fix until PhpModuleQueryWorker can be updated with Stage2. + // HACK: Fixup the module load count. + // Temp fix until PhpModuleQueryWorker can be used for 'Stage2'. Entry->ObsoleteLoadCount = (USHORT)ldrDagNode.LoadCount; } } @@ -3529,6 +3529,24 @@ BOOLEAN NTAPI PhpEnumProcessModules32Callback( 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 + ))) + { + // HACK: Fixup the module load count. + // Temp fix until PhpModuleQueryWorker can be used for 'Stage2'. + nativeEntry.ObsoleteLoadCount = (USHORT)ldrDagNode32.LoadCount; + } + } + // Execute the callback. cont = parameters->Callback(&nativeEntry, parameters->Context); From 229741683fe9d7c983e9ec0af19050302bb9bc10 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 15:25:44 +1000 Subject: [PATCH 163/839] Updater: Save changelog window position --- plugins/Updater/main.c | 4 +++- plugins/Updater/options.c | 9 +++++++-- plugins/Updater/updater.h | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index a6a73649f820..a6eb9efcde40 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -90,7 +90,9 @@ LOGICAL DllMain( PH_SETTING_CREATE settings[] = { { IntegerSettingType, SETTING_NAME_AUTO_CHECK, L"1" }, - { StringSettingType, SETTING_NAME_LAST_CHECK, L"0" }, + { 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); diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 2ca69914fc11..5b9deb95b995 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -89,19 +89,24 @@ INT_PTR CALLBACK TextDlgProc( SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + SetWindowText(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); - SetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), PhGetString(context->BuildMessage)); + 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)); SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); } break; case WM_DESTROY: { + PhSaveWindowPlacementToSetting(SETTING_NAME_CHANGELOG_WINDOW_POSITION, SETTING_NAME_CHANGELOG_WINDOW_SIZE, hwndDlg); + PhDeleteLayoutManager(&LayoutManager); } break; diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 5afc567627b0..ae1d6e5e9f46 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -48,6 +48,8 @@ #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) | \ From a9ba83e163fb5a82cb769937c1854254c8121eee Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 17:32:29 +1000 Subject: [PATCH 164/839] Remove unnecessary handle from ProcessQueryStage1 --- ProcessHacker/procprv.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 9445050002f7..510b16268626 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -949,9 +949,7 @@ VOID PhpProcessQueryStage1( NTSTATUS status; PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; HANDLE processId = processItem->ProcessId; - HANDLE processHandleLimited = NULL; - - PhOpenProcess(&processHandleLimited, ProcessQueryAccess, processId); + HANDLE processHandleLimited = processItem->QueryHandle; if (processItem->FileName) { @@ -1167,9 +1165,6 @@ VOID PhpProcessQueryStage1( Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } - if (processHandleLimited) - NtClose(processHandleLimited); - PhpQueueProcessQueryStage2(processItem); } From 6864383ebb268962c49f5569c3db1ebd5d0c0288 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 18:20:35 +1000 Subject: [PATCH 165/839] Update ntpsapi.h with more RS2 types --- phnt/include/ntpsapi.h | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 62a3e96da340..23ae8b712e56 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -172,14 +172,14 @@ typedef enum _PROCESSINFOCLASS ProcessIumChallengeResponse, ProcessChildProcessInformation, // PROCESS_CHILD_PROCESS_INFORMATION ProcessHighGraphicsPriorityInformation, - ProcessSubsystemInformation, // since REDSTONE2 + ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ProcessEnergyValues, - ProcessActivityThrottleState, - ProcessActivityThrottlePolicy, + ProcessActivityThrottleState, // PROCESS_ACTIVITY_THROTTLE_STATE + ProcessActivityThrottlePolicy, // PROCESS_ACTIVITY_THROTTLE_POLICY ProcessWin32kSyscallFilterInformation, ProcessDisableSystemAllowedCpuSets, - ProcessWakeInformation, - ProcessEnergyTrackingState, + ProcessWakeInformation, // PROCESS_WAKE_INFORMATION + ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE MaxProcessInfoClass } PROCESSINFOCLASS; #endif @@ -232,7 +232,7 @@ typedef enum _THREADINFOCLASS ThreadDynamicCodePolicyInfo, ThreadExplicitCaseSensitivity, ThreadWorkOnBehalfTicket, - ThreadSubsystemInformation, // since REDSTONE2 + ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ThreadDbgkWerReportActive, ThreadAttachContainer, MaxThreadInfoClass @@ -701,6 +701,22 @@ typedef struct _PROCESS_CHILD_PROCESS_INFORMATION BOOLEAN EnableAutomaticOverride; } 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; + // end_private #endif From 28d696011fe4277d6a23d197c52243a1caafb588 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 18:55:57 +1000 Subject: [PATCH 166/839] Revert version.rc merge, Fix about window layout --- ProcessHacker/ProcessHacker.rc | 40 +------- ProcessHacker/ProcessHacker.vcxproj | 1 + ProcessHacker/ProcessHacker.vcxproj.filters | 3 + ProcessHacker/version.rc | 101 ++++++++++++++++++++ 4 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 ProcessHacker/version.rc diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index e9bcdd06a676..4f52ab152a7d 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -636,7 +636,7 @@ 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 + LTEXT "Copyright (c) 2008-2017",IDC_STATIC,45,40,80,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 @@ -2535,44 +2535,6 @@ 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", "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 ///////////////////////////////////////////////////////////////////////////// diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 9168ea48f7b7..03188043fdab 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -401,6 +401,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index e4fac5fddc0e..f93d2a3fb8b1 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -576,6 +576,9 @@ Resources + + Resources + diff --git a/ProcessHacker/version.rc b/ProcessHacker/version.rc new file mode 100644 index 000000000000..d9685eada357 --- /dev/null +++ b/ProcessHacker/version.rc @@ -0,0 +1,101 @@ +// 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 + From c583fd2e512c6dafae8a26e4cd3f2a80dce4582a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 22:05:18 +1000 Subject: [PATCH 167/839] Update SYSTEM_INFORMATION_CLASS types --- phnt/include/ntexapi.h | 257 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 239 insertions(+), 18 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 8fad61a03a9d..22ffd1b62362 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1209,8 +1209,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 +1228,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) @@ -1324,10 +1324,10 @@ typedef enum _SYSTEM_INFORMATION_CLASS 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 @@ -1619,30 +1619,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 { @@ -1780,6 +1796,147 @@ typedef struct _SYSTEM_SET_TIME_ADJUST_INFORMATION BOOLEAN Enable; } SYSTEM_SET_TIME_ADJUST_INFORMATION, *PSYSTEM_SET_TIME_ADJUST_INFORMATION; +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 + 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; + ULONGLONG Reserved; + WCHAR Description[1]; +} PROFILE_SOURCE_INFO, *PPROFILE_SOURCE_INFO; + +typedef struct _EVENT_TRACE_PROFILE_LIST_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG Spare; + 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 _SYSTEM_EXCEPTION_INFORMATION { ULONG AlignmentFixupCount; @@ -2331,6 +2488,70 @@ 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_AREA +{ + ULONGLONG BtsBufferBase; + ULONGLONG BtsIndex; + ULONGLONG BtsAbsoluteMaximum; + ULONGLONG BtsInterruptThreshold; + ULONGLONG PebsBufferBase; + ULONGLONG PebsIndex; + ULONGLONG PebsAbsoluteMaximum; + ULONGLONG PebsInterruptThreshold; + ULONGLONG PebsCounterReset0; + ULONGLONG PebsCounterReset1; + ULONGLONG PebsCounterReset2; + ULONGLONG PebsCounterReset3; +} 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 { From 7ce6b0aa13b3b559e227bb3dd7e06fb586407d50 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 22:35:26 +1000 Subject: [PATCH 168/839] Update SYSTEM_INFORMATION_CLASS with more RS2 types --- phnt/include/ntexapi.h | 44 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 22ffd1b62362..8667b40190de 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1384,10 +1384,10 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemPhysicalMemoryInformation, // q: SYSTEM_PHYSICAL_MEMORY_INFORMATION // since REDSTONE2 SystemControlFlowTransition, SystemKernelDebuggingAllowed, - SystemActivityModerationExeState, - SystemActivityModerationUserSettings, + 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 MaxSystemInfoClass @@ -2918,6 +2918,44 @@ typedef struct _SYSTEM_PHYSICAL_MEMORY_INFORMATION ULONGLONG HighestPhysicalAddress; } SYSTEM_PHYSICAL_MEMORY_INFORMATION, *PSYSTEM_PHYSICAL_MEMORY_INFORMATION; +// private +typedef enum _SYSTEM_ACTIVITY_MODERATION_STATE +{ + SystemActivityModerationStateSystemManaged, + SystemActivityModerationStateAlwaysThrottled, + SystemActivityModerationStateNeverThrottled, + MaxSystemActivityModerationState +} SYSTEM_ACTIVITY_MODERATION_STATE; + +// private +typedef struct _SYSTEM_ACTIVITY_MODERATION_EXE_STATE +{ + UNICODE_STRING ExePathNt; + SYSTEM_ACTIVITY_MODERATION_STATE ModerationState; +} SYSTEM_ACTIVITY_MODERATION_EXE_STATE, *PSYSTEM_ACTIVITY_MODERATION_EXE_STATE; + +// 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 Unlockable : 1; + ULONG UnlockApplied : 1; + ULONG Reserved : 29; + }; + }; +} SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION, *PSYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION; + // private typedef struct _SYSTEM_FLUSH_INFORMATION { From 5904915bdd79d5208ba6ffb8afa8e8faed00b35e Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 28 May 2017 23:30:04 +1000 Subject: [PATCH 169/839] Remove PROFILE_SOURCE_INFO due to SDK conflict --- phnt/include/ntexapi.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 8667b40190de..545f81e9ad52 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1902,21 +1902,11 @@ typedef struct _EVENT_TRACE_PROFILE_COUNTER_INFORMATION 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; - ULONGLONG Reserved; - WCHAR Description[1]; -} PROFILE_SOURCE_INFO, *PPROFILE_SOURCE_INFO; - typedef struct _EVENT_TRACE_PROFILE_LIST_INFORMATION { EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; ULONG Spare; - PROFILE_SOURCE_INFO Profile[1]; + struct _PROFILE_SOURCE_INFO* Profile[1]; } EVENT_TRACE_PROFILE_LIST_INFORMATION, *PEVENT_TRACE_PROFILE_LIST_INFORMATION; typedef struct _EVENT_TRACE_STACK_CACHING_INFORMATION From 7e2abf1068fefd6c8637e170e88e7c78641d10a0 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 10:06:58 +1000 Subject: [PATCH 170/839] peview: Tidy up names --- tools/peview/include/pdb.h | 205 +++++++++++--- tools/peview/include/peview.h | 7 +- tools/peview/pdb.c | 511 ++++++++++++++++++---------------- 3 files changed, 437 insertions(+), 286 deletions(-) diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h index b9e7ef2addaa..5b04419d4d39 100644 --- a/tools/peview/include/pdb.h +++ b/tools/peview/include/pdb.h @@ -175,7 +175,10 @@ typedef struct _PointerTypeInfo ULONG64 Length; // Index of the type the pointer points to (DIA: typeId) - ULONG TypeIndex; + ULONG TypeIndex; + + // Reference ("reference" in DIA) + BOOL TypeReference; } PointerTypeInfo; // SymTagUDT symbol (Class or structure) @@ -362,52 +365,176 @@ typedef struct _TypeInfo union { - BaseTypeInfo sBaseTypeInfo; // If Tag == SymTagBaseType - TypedefInfo sTypedefInfo; // If Tag == SymTagTypedef - PointerTypeInfo sPointerTypeInfo; // If Tag == SymTagPointerType - UdtClassInfo sUdtClassInfo; // If Tag == SymTagUDT and UdtKind is "true" - UdtUnionInfo sUdtUnionInfo; // If Tag == SymTagUDT and UdtKind is "false" - BaseClassInfo sBaseClassInfo; // If Tag == SymTagBaseClass - EnumInfo sEnumInfo; // If Tag == SymTagEnum - ArrayTypeInfo sArrayTypeInfo; // If Tag == SymTagArrayType - FunctionTypeInfo sFunctionTypeInfo; // If Tag == SymTagFunctionType - FunctionArgTypeInfo sFunctionArgTypeInfo; // If Tag == SymTagFunctionArgType - DataInfo sDataInfo; // If Tag == SymTagData + 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 SymbolInfo_DumpBasicType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseTypeInfo* Info); -BOOLEAN SymbolInfo_DumpPointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, PointerTypeInfo* Info); -BOOLEAN SymbolInfo_DumpTypedef(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypedefInfo* Info); -BOOLEAN SymbolInfo_DumpEnum(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, EnumInfo* Info); -BOOLEAN SymbolInfo_DumpArrayType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ArrayTypeInfo* Info); -BOOLEAN SymbolInfo_DumpUDT(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); -BOOLEAN SymbolInfo_DumpUDTClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtClassInfo* Info); -BOOLEAN SymbolInfo_DumpUDTUnion(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, UdtUnionInfo* Info); -BOOLEAN SymbolInfo_DumpFunctionType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionTypeInfo* Info); -BOOLEAN SymbolInfo_DumpFunctionArgType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, FunctionArgTypeInfo* Info); -BOOLEAN SymbolInfo_DumpBaseClass(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, BaseClassInfo* Info); -BOOLEAN SymbolInfo_DumpData(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, DataInfo* Info); -BOOLEAN SymbolInfo_DumpType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info); +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 SymbolInfo_CheckTag( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbCheckTagType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _In_ ULONG Tag ); -BOOLEAN SymbolInfo_SymbolSize(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* Size); -BOOLEAN SymbolInfo_ArrayElementTypeIndex(_Inout_ PPDB_SYMBOL_CONTEXT Context, ULONG ArrayIndex, ULONG* ElementTypeIndex); -BOOLEAN SymbolInfo_ArrayDims(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG64* pDims, ULONG* Dims, _In_ ULONG MaxDims); -BOOLEAN SymbolInfo_UdtVariables(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pVars, ULONG* Vars, _In_ ULONG MaxVars); -BOOLEAN SymbolInfo_UdtFunctions(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pFuncs, ULONG* Funcs, _In_ ULONG MaxFuncs); -BOOLEAN SymbolInfo_UdtBaseClasses(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pBases, ULONG* Bases, _In_ ULONG MaxBases); -BOOLEAN SymbolInfo_UdtUnionMembers(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pMembers, ULONG* Members, _In_ ULONG MaxMembers); -BOOLEAN SymbolInfo_FunctionArguments(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pArgs, ULONG* Args, _In_ ULONG MaxArgs); -BOOLEAN SymbolInfo_Enumerators(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* pEnums, ULONG* Enums, _In_ ULONG MaxEnums); -BOOLEAN SymbolInfo_TypeDefType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex); -BOOLEAN SymbolInfo_PointerType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, ULONG* UndTypeIndex, ULONG* NumPointers); +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, @@ -415,13 +542,11 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( _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(enum SymTagEnum Tag); PWSTR SymbolInfo_BaseTypeStr(BasicType Type, ULONG64 Length); PWSTR SymbolInfo_CallConvStr(CV_call_e CallConv); diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 253606b17cdf..10a4cc2c73bc 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -135,7 +135,7 @@ typedef struct _PH_TN_COLUMN_MENU_DATA #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( +VOID PhInitializeTreeNewColumnMenu( _Inout_ PPH_TN_COLUMN_MENU_DATA Data ); @@ -240,6 +240,11 @@ NTSTATUS PeDumpFileSymbols( _In_ PPDB_SYMBOL_CONTEXT Context ); +VOID PdbDumpAddress( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 Address + ); + VOID PvPdbProperties( VOID ); diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 7fac14523a91..61e222ad8daa 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -52,6 +52,13 @@ typedef BOOL (WINAPI *_SymEnumSymbolsW)( _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 *_SymSetSearchPathW)( _In_ HANDLE hProcess, _In_opt_ PCWSTR SearchPath @@ -80,6 +87,13 @@ typedef BOOL (WINAPI *_SymGetModuleInfoW64)( _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, @@ -89,11 +103,13 @@ typedef BOOL (WINAPI *_SymSetContext)( _SymInitialize SymInitialize_I = NULL; _SymCleanup SymCleanup_I = NULL; _SymEnumSymbolsW SymEnumSymbolsW_I = NULL; +_SymEnumTypesW SymEnumTypesW_I = NULL; _SymSetSearchPathW SymSetSearchPathW_I = NULL; _SymGetOptions SymGetOptions_I = NULL; _SymSetOptions SymSetOptions_I = NULL; _SymLoadModuleExW SymLoadModuleExW_I = NULL; _SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; +_SymGetTypeFromNameW SymGetTypeFromNameW_I = NULL; _SymGetTypeInfo SymGetTypeInfo_I = NULL; _SymSetContext SymSetContext_I = NULL; @@ -101,8 +117,8 @@ ULONG SearchResultsAddIndex = 0; PPH_LIST SearchResults = NULL; PH_QUEUED_LOCK SearchResultsLock = PH_QUEUED_LOCK_INIT; -BOOLEAN SymbolInfo_DumpBasicType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolBasicType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ BaseTypeInfo *Info ) @@ -110,54 +126,57 @@ BOOLEAN SymbolInfo_DumpBasicType( ULONG baseType = btNoType; ULONG64 length = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagBaseType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagBaseType)) return FALSE; // Basic type ("basicType" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_BASETYPE, &baseType)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_BASETYPE, &baseType)) return FALSE; // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) return FALSE; Info->BaseType = (BasicType)baseType; Info->Length = length; + return TRUE; } -BOOLEAN SymbolInfo_DumpPointerType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolPointerType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ PointerTypeInfo* Info ) { - ULONG TypeIndex = 0; - ULONG64 Length = 0; - - if (!SymbolInfo_CheckTag(Context, Index, SymTagPointerType)) + 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(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) return FALSE; // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) return FALSE; // Reference ("reference" in DIA) - // This property is not available via SymGetTypeInfo_I?? - // TODO: Figure out how DIA determines if the pointer is a pointer or a reference. + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_IS_REFERENCE, &reference)) + return FALSE; - Info->TypeIndex = TypeIndex; - Info->Length = Length; + Info->TypeIndex = typeIndex; + Info->Length = length; + Info->TypeReference = reference; return TRUE; } -BOOLEAN SymbolInfo_DumpTypedef( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolTypedefType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ TypedefInfo* Info ) @@ -165,15 +184,15 @@ BOOLEAN SymbolInfo_DumpTypedef( ULONG typeIndex = 0; PWSTR symbolName = NULL; - if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) return FALSE; // Type index ("typeId" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) return FALSE; // Name ("name" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) return FALSE; Info->TypeIndex = typeIndex; @@ -185,8 +204,8 @@ BOOLEAN SymbolInfo_DumpTypedef( return TRUE; } -BOOLEAN SymbolInfo_DumpEnum( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolEnumType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ EnumInfo* Info ) @@ -195,11 +214,11 @@ BOOLEAN SymbolInfo_DumpEnum( ULONG Nested = 0; PWSTR symbolName = NULL; - if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagEnum)) return FALSE; // Name ("name" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) return FALSE; wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); @@ -207,15 +226,15 @@ BOOLEAN SymbolInfo_DumpEnum( LocalFree(symbolName); // Type index ("typeId" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) return FALSE; // Nested ("nested" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) return FALSE; // Enumerators - if (!SymbolInfo_Enumerators(Context, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) + if (!PdbGetSymbolEnumerations(BaseAddress, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) return FALSE; Info->TypeIndex = TypeIndex; @@ -224,8 +243,8 @@ BOOLEAN SymbolInfo_DumpEnum( return TRUE; } -BOOLEAN SymbolInfo_DumpArrayType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolArrayType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ArrayTypeInfo* Info ) @@ -235,19 +254,19 @@ BOOLEAN SymbolInfo_DumpArrayType( ULONG indexTypeIndex = 0; // Check if it is really SymTagArrayType - if (!SymbolInfo_CheckTag(Context, Index, SymTagArrayType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagArrayType)) return FALSE; // Element type index - if (!SymbolInfo_ArrayElementTypeIndex(Context, Index, &elementTypeIndex)) + if (!PdbGetSymbolArrayElementTypeIndex(BaseAddress, Index, &elementTypeIndex)) return FALSE; // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) return FALSE; // Type index of the array index element - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_ARRAYINDEXTYPEID, &indexTypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_ARRAYINDEXTYPEID, &indexTypeIndex)) return FALSE; Info->ElementTypeIndex = elementTypeIndex; @@ -256,9 +275,8 @@ BOOLEAN SymbolInfo_DumpArrayType( if (length > 0) { - // Dimensions - if (!SymbolInfo_ArrayDims( - Context, + if (!PdbGetSymbolArrayDimensions( + BaseAddress, Index, Info->Dimensions, &Info->NumDimensions, @@ -277,7 +295,7 @@ BOOLEAN SymbolInfo_DumpArrayType( } BOOLEAN SymbolInfo_DumpUDT( - _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ TypeInfo* Info ) @@ -285,33 +303,33 @@ BOOLEAN SymbolInfo_DumpUDT( ULONG UDTKind = 0; BOOLEAN result = FALSE; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) return FALSE; switch (UDTKind) { case UdtStruct: Info->UdtKind = TRUE; - result = SymbolInfo_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + result = PdbGetSymbolUDTClass(BaseAddress, Index, &Info->UdtClassInfo); break; case UdtClass: Info->UdtKind = TRUE; - result = SymbolInfo_DumpUDTClass(Context, Index, &Info->sUdtClassInfo); + result = PdbGetSymbolUDTClass(BaseAddress, Index, &Info->UdtClassInfo); break; case UdtUnion: Info->UdtKind = FALSE; - result = SymbolInfo_DumpUDTUnion(Context, Index, &Info->sUdtUnionInfo); + result = PdbGetSymbolUDTUnion(BaseAddress, Index, &Info->UdtUnionInfo); break; } return result; } -BOOLEAN SymbolInfo_DumpUDTClass( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUDTClass( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ UdtClassInfo* Info ) @@ -321,18 +339,18 @@ BOOLEAN SymbolInfo_DumpUDTClass( ULONG64 Length = 0; ULONG Nested = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; // Check if it is really a class or structure UDT ? - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + 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(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) return FALSE; wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); @@ -340,23 +358,23 @@ BOOLEAN SymbolInfo_DumpUDTClass( LocalFree(symbolName); // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &Length)) return FALSE; // Nested ("nested" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) return FALSE; // Member variables - if (!SymbolInfo_UdtVariables(Context, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) + if (!PdbGetSymbolUdtVariables(BaseAddress, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) return FALSE; // Member functions - if (!SymbolInfo_UdtFunctions(Context, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) + if (!PdbGetSymbolUdtFunctions(BaseAddress, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) return FALSE; // Base classes - if (!SymbolInfo_UdtBaseClasses(Context, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) + if (!PdbGetSymbolUdtBaseClasses(BaseAddress, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) return FALSE; Info->UDTKind = (UdtKind)UDTKind; @@ -366,8 +384,8 @@ BOOLEAN SymbolInfo_DumpUDTClass( return TRUE; } -BOOLEAN SymbolInfo_DumpUDTUnion( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUDTUnion( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ UdtUnionInfo *Info ) @@ -377,18 +395,18 @@ BOOLEAN SymbolInfo_DumpUDTUnion( ULONG64 Length = 0; ULONG Nested = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; // Check if it is really a union UDT ? - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + 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(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) return FALSE; wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); @@ -396,15 +414,15 @@ BOOLEAN SymbolInfo_DumpUDTUnion( LocalFree(symbolName); // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &Length)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &Length)) return FALSE; // Nested ("nested" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_NESTED, &Nested)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) return FALSE; // Union members - if (!SymbolInfo_UdtUnionMembers(Context, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) + if (!PdbGetSymbolUdtUnionMembers(BaseAddress, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) return FALSE; Info->UDTKind = (UdtKind)UDTKind; @@ -414,8 +432,8 @@ BOOLEAN SymbolInfo_DumpUDTUnion( return TRUE; } -BOOLEAN SymbolInfo_DumpFunctionType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolFunctionType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ FunctionTypeInfo *Info ) @@ -425,16 +443,16 @@ BOOLEAN SymbolInfo_DumpFunctionType( ULONG CallConv = 0; ULONG ClassIndex = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionType)) return FALSE; // Index of the return type symbol ("typeId" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &RetTypeIndex)) + 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(), Context->BaseAddress, Index, TI_GET_COUNT, &NumArgs)) + 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 @@ -443,11 +461,11 @@ BOOLEAN SymbolInfo_DumpFunctionType( // is static or not (if it is a member function) // Calling convention ("callingConvention" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CALLING_CONVENTION, &CallConv)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_CALLING_CONVENTION, &CallConv)) return FALSE; // Parent class type index ("classParent" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CLASSPARENTID, &ClassIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_CLASSPARENTID, &ClassIndex)) { // The function is not a member function Info->ClassIndex = 0; @@ -466,7 +484,7 @@ BOOLEAN SymbolInfo_DumpFunctionType( LONG ThisAdjust = 0; // "this" adjustment ("thisAdjust" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_THISADJUST, &ThisAdjust)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_THISADJUST, &ThisAdjust)) return FALSE; Info->ThisAdjust = ThisAdjust; @@ -482,7 +500,7 @@ BOOLEAN SymbolInfo_DumpFunctionType( */ // Dump function arguments - if (!SymbolInfo_FunctionArguments(Context, Index, Info->Args, &Info->NumArgs, ARRAYSIZE(Info->Args))) + if (!PdbGetSymbolFunctionArguments(BaseAddress, Index, Info->Args, &Info->NumArgs, ARRAYSIZE(Info->Args))) return FALSE; // Is the function static ? (If it is a member function) @@ -499,19 +517,19 @@ BOOLEAN SymbolInfo_DumpFunctionType( return TRUE; } -BOOLEAN SymbolInfo_DumpFunctionArgType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolFunctionArgType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ FunctionArgTypeInfo *Info ) { ULONG typeIndex = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionArgType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionArgType)) return FALSE; // Index of the argument type ("typeId" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) return FALSE; Info->TypeIndex = typeIndex; @@ -519,8 +537,8 @@ BOOLEAN SymbolInfo_DumpFunctionArgType( return TRUE; } -BOOLEAN SymbolInfo_DumpBaseClass( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolBaseClass( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ BaseClassInfo *Info ) @@ -528,13 +546,13 @@ BOOLEAN SymbolInfo_DumpBaseClass( ULONG typeIndex = 0; ULONG virtualBase = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagBaseClass)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagBaseClass)) return FALSE; // Base class UDT if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_TYPEID, &typeIndex @@ -546,7 +564,7 @@ BOOLEAN SymbolInfo_DumpBaseClass( // Is this base class virtual ? if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_VIRTUALBASECLASS, &virtualBase @@ -565,7 +583,7 @@ BOOLEAN SymbolInfo_DumpBaseClass( // Virtual base pointer offset if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_VIRTUALBASEPOINTEROFFSET, &virtualBasePtrOffset @@ -584,7 +602,7 @@ BOOLEAN SymbolInfo_DumpBaseClass( // Offset in the parent UDT if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_OFFSET, &offset @@ -600,8 +618,8 @@ BOOLEAN SymbolInfo_DumpBaseClass( return TRUE; } -BOOLEAN SymbolInfo_DumpData( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolData( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ DataInfo *Info ) @@ -610,11 +628,11 @@ BOOLEAN SymbolInfo_DumpData( ULONG TypeIndex = 0; ULONG dataKind = 0; - if (!SymbolInfo_CheckTag(Context, Index, SymTagData)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagData)) return FALSE; // Name ("name" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) return FALSE; wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); @@ -622,11 +640,11 @@ BOOLEAN SymbolInfo_DumpData( LocalFree(symbolName); // Index of type symbol ("typeId" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) return FALSE; // Data kind ("dataKind" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_DATAKIND, &dataKind)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_DATAKIND, &dataKind)) return FALSE; Info->TypeIndex = TypeIndex; @@ -645,7 +663,7 @@ BOOLEAN SymbolInfo_DumpData( // 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(), Context->BaseAddress, Index, TI_GET_ADDRESS, &address)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_ADDRESS, &address)) return FALSE; Info->Address = address; @@ -661,7 +679,7 @@ BOOLEAN SymbolInfo_DumpData( ULONG offset = 0; // Use Offset; Address is not defined - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_OFFSET, &offset)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_OFFSET, &offset)) return FALSE; Info->Offset = offset; @@ -679,7 +697,7 @@ BOOLEAN SymbolInfo_DumpData( } BOOLEAN SymbolInfo_DumpType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ TypeInfo *Info ) @@ -688,7 +706,7 @@ BOOLEAN SymbolInfo_DumpType( if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_SYMTAG, &tag @@ -702,25 +720,25 @@ BOOLEAN SymbolInfo_DumpType( switch (tag) { case SymTagBaseType: - return SymbolInfo_DumpBasicType(Context, Index, &Info->sBaseTypeInfo); + return PdbGetSymbolBasicType(BaseAddress, Index, &Info->BaseTypeInfo); case SymTagPointerType: - return SymbolInfo_DumpPointerType(Context, Index, &Info->sPointerTypeInfo); + return PdbGetSymbolPointerType(BaseAddress, Index, &Info->PointerTypeInfo); case SymTagTypedef: - return SymbolInfo_DumpTypedef(Context, Index, &Info->sTypedefInfo); + return PdbGetSymbolTypedefType(BaseAddress, Index, &Info->TypedefInfo); case SymTagEnum: - return SymbolInfo_DumpEnum(Context, Index, &Info->sEnumInfo); + return PdbGetSymbolEnumType(BaseAddress, Index, &Info->EnumInfo); case SymTagArrayType: - return SymbolInfo_DumpArrayType(Context, Index, &Info->sArrayTypeInfo); + return PdbGetSymbolArrayType(BaseAddress, Index, &Info->ArrayTypeInfo); case SymTagUDT: - return SymbolInfo_DumpUDT(Context, Index, Info); + return SymbolInfo_DumpUDT(BaseAddress, Index, Info); case SymTagFunctionType: - return SymbolInfo_DumpFunctionType(Context, Index, &Info->sFunctionTypeInfo); + return PdbGetSymbolFunctionType(BaseAddress, Index, &Info->FunctionTypeInfo); case SymTagFunctionArgType: - return SymbolInfo_DumpFunctionArgType(Context, Index, &Info->sFunctionArgTypeInfo); + return PdbGetSymbolFunctionArgType(BaseAddress, Index, &Info->FunctionArgTypeInfo); case SymTagBaseClass: - return SymbolInfo_DumpBaseClass(Context, Index, &Info->sBaseClassInfo); + return PdbGetSymbolBaseClass(BaseAddress, Index, &Info->BaseClassInfo); case SymTagData: - return SymbolInfo_DumpData(Context, Index, &Info->sDataInfo); + return PdbGetSymbolData(BaseAddress, Index, &Info->DataInfo); } return FALSE; @@ -738,25 +756,25 @@ BOOLEAN SymbolInfo_DumpSymbolType( return FALSE; // Dump the type symbol - return SymbolInfo_DumpType(Context, *TypeIndex, Info); + return SymbolInfo_DumpType(Context->BaseAddress, *TypeIndex, Info); } -BOOLEAN SymbolInfo_CheckTag( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbCheckTagType( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _In_ ULONG Tag ) { ULONG symTag = SymTagNull; - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_SYMTAG, &symTag)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMTAG, &symTag)) return FALSE; return symTag == Tag; } -BOOLEAN SymbolInfo_SymbolSize( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolSize( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG64* Size ) @@ -765,7 +783,7 @@ BOOLEAN SymbolInfo_SymbolSize( ULONG64 length = 0; // Does the symbol support "length" property ? - if (SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_LENGTH, &length)) + if (SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) { *Size = length; return TRUE; @@ -773,7 +791,7 @@ BOOLEAN SymbolInfo_SymbolSize( else { // No, it does not - it can be SymTagTypedef - if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) { // No, this symbol does not have length return FALSE; @@ -787,15 +805,15 @@ BOOLEAN SymbolInfo_SymbolSize( { ULONG tempIndex = 0; - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, &tempIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &tempIndex)) return FALSE; index = tempIndex; - } while (SymbolInfo_CheckTag(Context, index, SymTagTypedef)); + } while (PdbCheckTagType(BaseAddress, index, SymTagTypedef)); // And get the length - if (SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_LENGTH, &length)) + if (SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_LENGTH, &length)) { *Size = length; return TRUE; @@ -806,8 +824,8 @@ BOOLEAN SymbolInfo_SymbolSize( return FALSE; } -BOOLEAN SymbolInfo_ArrayElementTypeIndex( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolArrayElementTypeIndex( + _In_ ULONG64 BaseAddress, _In_ ULONG ArrayIndex, _Inout_ ULONG* ElementTypeIndex ) @@ -815,19 +833,19 @@ BOOLEAN SymbolInfo_ArrayElementTypeIndex( ULONG index; ULONG elementIndex = 0; - if (!SymbolInfo_CheckTag(Context, ArrayIndex, SymTagArrayType)) + if (!PdbCheckTagType(BaseAddress, ArrayIndex, SymTagArrayType)) return FALSE; // Get the array element type - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, ArrayIndex, TI_GET_TYPEID, &elementIndex)) + 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 (SymbolInfo_CheckTag(Context, elementIndex, SymTagArrayType)) + while (PdbCheckTagType(BaseAddress, elementIndex, SymTagArrayType)) { - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, &elementIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &elementIndex)) return FALSE; index = elementIndex; @@ -839,8 +857,8 @@ BOOLEAN SymbolInfo_ArrayElementTypeIndex( return TRUE; } -BOOLEAN SymbolInfo_ArrayDims( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolArrayDimensions( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG64* pDims, _Inout_ ULONG* Dims, @@ -850,7 +868,7 @@ BOOLEAN SymbolInfo_ArrayDims( ULONG index; ULONG dimCount; - if (!SymbolInfo_CheckTag(Context, Index, SymTagArrayType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagArrayType)) return FALSE; if (MaxDims <= 0) @@ -866,7 +884,7 @@ BOOLEAN SymbolInfo_ArrayDims( ULONG64 typeSize = 0; // Length ("length" in DIA) - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_LENGTH, &length) || (length == 0)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_LENGTH, &length) || (length == 0)) return FALSE; // Size of the current dimension @@ -874,38 +892,33 @@ BOOLEAN SymbolInfo_ArrayDims( // type symbol, which can be another SymTagArrayType symbol or the array element symbol) // Its type - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, &typeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &typeIndex)) { // No type - we are done break; } // Size of its type - if (!SymbolInfo_SymbolSize(Context, typeIndex, &typeSize) || (typeSize == 0)) + if (!PdbGetSymbolSize(BaseAddress, typeIndex, &typeSize) || (typeSize == 0)) return FALSE; // Size of the dimension pDims[i] = length / typeSize; dimCount++; - // Test only -/* + /* ULONG ElemCount = 0; - if( !SymGetTypeInfo_I( m_hProcess, BaseAddress, CurIndex, TI_GET_COUNT, &ElemCount ) ) + 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.") ); - } - else - { - //_ASSERTE( !_T("TI_GET_COUNT works!") ); - } -*/ + }*/ + // If the type symbol is not SymTagArrayType, we are done - if (!SymbolInfo_CheckTag(Context, typeIndex, SymTagArrayType)) + if (!PdbCheckTagType(BaseAddress, typeIndex, SymTagArrayType)) break; index = typeIndex; @@ -920,7 +933,7 @@ BOOLEAN SymbolInfo_ArrayDims( } BOOLEAN PdbGetSymbolChildren( - _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* Count, _Inout_ TI_FINDCHILDREN_PARAMS **Params @@ -932,7 +945,7 @@ BOOLEAN PdbGetSymbolChildren( if (!SymGetTypeInfo_I( NtCurrentProcess(), - Context->BaseAddress, + BaseAddress, Index, TI_GET_CHILDRENCOUNT, &symbolCount @@ -950,7 +963,7 @@ BOOLEAN PdbGetSymbolChildren( symbols->Count = symbolCount; - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, symbols)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_FINDCHILDREN, symbols)) return FALSE; *Count = symbolCount; @@ -959,8 +972,8 @@ BOOLEAN PdbGetSymbolChildren( return TRUE; } -BOOLEAN SymbolInfo_UdtVariables( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUdtVariables( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* pVars, _Inout_ ULONG* Vars, @@ -970,20 +983,20 @@ BOOLEAN SymbolInfo_UdtVariables( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; if (MaxVars <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Vars = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) { pVars[*Vars] = symbolParams->ChildId[i]; @@ -997,8 +1010,8 @@ BOOLEAN SymbolInfo_UdtVariables( return TRUE; } -BOOLEAN SymbolInfo_UdtFunctions( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUdtFunctions( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* pFuncs, _Inout_ ULONG* Funcs, @@ -1008,20 +1021,20 @@ BOOLEAN SymbolInfo_UdtFunctions( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; if (MaxFuncs <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Funcs = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagFunction)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagFunction)) { pFuncs[*Funcs] = symbolParams->ChildId[i]; @@ -1035,8 +1048,8 @@ BOOLEAN SymbolInfo_UdtFunctions( return TRUE; } -BOOLEAN SymbolInfo_UdtBaseClasses( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUdtBaseClasses( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* pBases, _Inout_ ULONG* Bases, @@ -1046,20 +1059,20 @@ BOOLEAN SymbolInfo_UdtBaseClasses( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; if (MaxBases <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Bases = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagBaseClass)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagBaseClass)) { pBases[*Bases] = symbolParams->ChildId[i]; @@ -1073,8 +1086,8 @@ BOOLEAN SymbolInfo_UdtBaseClasses( return TRUE; } -BOOLEAN SymbolInfo_UdtUnionMembers( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolUdtUnionMembers( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* pMembers, _Inout_ ULONG* Members, @@ -1084,20 +1097,20 @@ BOOLEAN SymbolInfo_UdtUnionMembers( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) return FALSE; if (MaxMembers <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Members = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) { pMembers[*Members] = symbolParams->ChildId[i]; @@ -1111,8 +1124,8 @@ BOOLEAN SymbolInfo_UdtUnionMembers( return TRUE; } -BOOLEAN SymbolInfo_FunctionArguments( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolFunctionArguments( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG* pArgs, _Inout_ ULONG* Args, @@ -1122,20 +1135,20 @@ BOOLEAN SymbolInfo_FunctionArguments( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionType)) return FALSE; if (MaxArgs <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Args = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagFunctionArgType)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagFunctionArgType)) { pArgs[*Args] = symbolParams->ChildId[i]; @@ -1149,8 +1162,8 @@ BOOLEAN SymbolInfo_FunctionArguments( return TRUE; } -BOOLEAN SymbolInfo_Enumerators( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolEnumerations( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG *pEnums, _Inout_ ULONG *Enums, @@ -1160,20 +1173,20 @@ BOOLEAN SymbolInfo_Enumerators( ULONG childrenLength = 0; TI_FINDCHILDREN_PARAMS* symbolParams; - if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagEnum)) return FALSE; if (MaxEnums <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) return FALSE; *Enums = 0; for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) { pEnums[*Enums] = symbolParams->ChildId[i]; @@ -1187,23 +1200,23 @@ BOOLEAN SymbolInfo_Enumerators( return TRUE; } -BOOLEAN SymbolInfo_TypeDefType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolTypeDefTypeIndex( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG *UndTypeIndex ) { ULONG index; - if (!SymbolInfo_CheckTag(Context, Index, SymTagTypedef)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) return FALSE; // Skip to the type behind the type definition index = Index; - while (SymbolInfo_CheckTag(Context, index, SymTagTypedef)) + while (PdbCheckTagType(BaseAddress, index, SymTagTypedef)) { - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) return FALSE; index = *UndTypeIndex; @@ -1212,8 +1225,8 @@ BOOLEAN SymbolInfo_TypeDefType( return TRUE; } -BOOLEAN SymbolInfo_PointerType( - _Inout_ PPDB_SYMBOL_CONTEXT Context, +BOOLEAN PdbGetSymbolPointers( + _In_ ULONG64 BaseAddress, _In_ ULONG Index, _Inout_ ULONG *UndTypeIndex, _Inout_ ULONG *NumPointers @@ -1221,16 +1234,16 @@ BOOLEAN SymbolInfo_PointerType( { ULONG index; - if (!SymbolInfo_CheckTag(Context, Index, SymTagPointerType)) + if (!PdbCheckTagType(BaseAddress, Index, SymTagPointerType)) return FALSE; // Skip to the type pointer points to *NumPointers = 0; index = Index; - while (SymbolInfo_CheckTag(Context, index, SymTagPointerType)) + while (PdbCheckTagType(BaseAddress, index, SymTagPointerType)) { - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) return FALSE; (*NumPointers)++; @@ -1250,7 +1263,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( { TypeInfo Info; - if (!SymbolInfo_DumpType(Obj, Index, &Info)) + if (!SymbolInfo_DumpType(Obj->BaseAddress, Index, &Info)) { return FALSE; } @@ -1263,11 +1276,11 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( ULONG typeIndex = 0; // Yes, get the number of * to show - if (!SymbolInfo_PointerType(Obj, Index, &typeIndex, &numPointers)) + if (!PdbGetSymbolPointers(Obj->BaseAddress, Index, &typeIndex, &numPointers)) return FALSE; // Get more information about the type the pointer points to - if (!SymbolInfo_DumpType(Obj, typeIndex, &Info)) + if (!SymbolInfo_DumpType(Obj->BaseAddress, typeIndex, &Info)) return FALSE; // Save the index of the type the pointer points to @@ -1279,11 +1292,11 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( switch (Info.Tag) { case SymTagBaseType: - PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length)); + PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.BaseTypeInfo.BaseType, Info.BaseTypeInfo.Length)); PhAppendStringBuilder2(TypeName, L" "); break; case SymTagTypedef: - PhAppendStringBuilder2(TypeName, Info.sTypedefInfo.Name); + PhAppendStringBuilder2(TypeName, Info.TypedefInfo.Name); PhAppendStringBuilder2(TypeName, L" "); break; case SymTagUDT: @@ -1291,7 +1304,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( if (Info.UdtKind) { // A class/structure - PhAppendStringBuilder2(TypeName, Info.sUdtClassInfo.Name); + PhAppendStringBuilder2(TypeName, Info.UdtClassInfo.Name); PhAppendStringBuilder2(TypeName, L" "); // Add the UDT and its base classes to the collection of UDT indexes @@ -1300,31 +1313,31 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( else { // A union - PhAppendStringBuilder2(TypeName, Info.sUdtUnionInfo.Name); + PhAppendStringBuilder2(TypeName, Info.UdtUnionInfo.Name); PhAppendStringBuilder2(TypeName, L" "); } } break; case SymTagEnum: - PhAppendStringBuilder2(TypeName, Info.sEnumInfo.Name); + PhAppendStringBuilder2(TypeName, Info.EnumInfo.Name); break; case SymTagFunctionType: { - if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.StaticFunction) + if (Info.FunctionTypeInfo.MemberFunction && Info.FunctionTypeInfo.StaticFunction) PhAppendStringBuilder2(TypeName, L"static "); // return value - if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) return FALSE; // calling convention - PhAppendStringBuilder2(TypeName, SymbolInfo_CallConvStr(Info.sFunctionTypeInfo.CallConv)); + PhAppendStringBuilder2(TypeName, SymbolInfo_CallConvStr(Info.FunctionTypeInfo.CallConv)); PhAppendStringBuilder2(TypeName, L" "); // If member function, save the class type index - if (Info.sFunctionTypeInfo.MemberFunction) + if (Info.FunctionTypeInfo.MemberFunction) { - PhAddItemList(Obj->UdtList, UlongToPtr(Info.sFunctionTypeInfo.ClassIndex)); + 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 @@ -1338,42 +1351,42 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( PhAppendStringBuilder2(TypeName, *VarName); // Print parameters - PhAppendStringBuilder2(TypeName, L" ("); + PhAppendStringBuilder2(TypeName, L"("); - for (ULONG i = 0; i < Info.sFunctionTypeInfo.NumArgs; i++) + for (ULONG i = 0; i < Info.FunctionTypeInfo.NumArgs; i++) { - if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.Args[i], Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionTypeInfo.Args[i], Obj, VarName, TypeName)) return FALSE; - if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) + if (i < (Info.FunctionTypeInfo.NumArgs - 1)) PhAppendStringBuilder2(TypeName, L", "); } PhAppendStringBuilder2(TypeName, L") "); // Print "this" adjustment value - if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.ThisAdjust != 0) - PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.sFunctionTypeInfo.ThisAdjust); + if (Info.FunctionTypeInfo.MemberFunction && Info.FunctionTypeInfo.ThisAdjust != 0) + PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.FunctionTypeInfo.ThisAdjust); } break; case SymTagFunctionArgType: { - if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) return FALSE; } break; case SymTagArrayType: { // Print element type name - if (!SymbolInfo_GetTypeNameHelper(Info.sArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) + if (!SymbolInfo_GetTypeNameHelper(Info.ArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) return FALSE; PhAppendStringBuilder2(TypeName, L" "); // Print dimensions - for (ULONG i = 0; i < Info.sArrayTypeInfo.NumDimensions; i++) - PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); + for (ULONG i = 0; i < Info.ArrayTypeInfo.NumDimensions; i++) + PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.ArrayTypeInfo.Dimensions[i]); } break; default: @@ -1829,10 +1842,7 @@ BOOL CALLBACK EnumCallbackProc( PhReleaseQueuedLockExclusive(&SearchResultsLock); // Enumerate parameters and variables... - IMAGEHLP_STACK_FRAME sf; - sf.InstructionOffset = SymbolInfo->Address; - SymSetContext_I(NtCurrentProcess(), &sf, 0); - SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, context); + PdbDumpAddress(context, SymbolInfo->Address); } break; case SymTagData: @@ -1842,9 +1852,9 @@ BOOL CALLBACK EnumCallbackProc( PWSTR symDataKind; ULONG dataKindType = 0; - if (!SymbolInfo->Address) + if (SymbolInfo->Address == 0) break; - + if (!SymGetTypeInfo_I( NtCurrentProcess(), SymbolInfo->ModBase, @@ -1858,12 +1868,12 @@ BOOL CALLBACK EnumCallbackProc( symDataKind = SymbolInfo_DataKindStr(dataKindType); - if (dataKindType == DataIsLocal || - dataKindType == DataIsParam || - dataKindType == DataIsObjectPtr) - { - break; - } + //if (dataKindType == DataIsLocal || + // dataKindType == DataIsParam || + // dataKindType == DataIsObjectPtr) + //{ + // break; + //} symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); @@ -1872,7 +1882,8 @@ BOOL CALLBACK EnumCallbackProc( { case DataIsLocal: { - SymbolInfo->Address = SymbolInfo->ModBase + SymbolInfo->Address; + // TODO: The address variable is FUNCTION+OFFSET + //SymbolInfo->Address = SymbolInfo->Address; symbol->Type = PV_SYMBOL_TYPE_LOCAL_VAR; } break; @@ -1972,7 +1983,7 @@ VOID PrintClassInfo( { TypeInfo VarInfo; - if (!SymbolInfo_DumpType(Context, Info->Variables[i], &VarInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->Variables[i], &VarInfo)) { // Continue } @@ -1990,7 +2001,7 @@ VOID PrintClassInfo( //symbol->Type = PV_SYMBOL_TYPE_MEMBER; // // Location - switch (VarInfo.sDataInfo.dataKind) + switch (VarInfo.DataInfo.dataKind) { case DataIsGlobal: case DataIsStaticLocal: @@ -2028,7 +2039,7 @@ VOID PrintClassInfo( { TypeInfo VarInfo; - if (!SymbolInfo_DumpType(Context, Info->Variables[i], &VarInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->Variables[i], &VarInfo)) { // Continue } @@ -2044,10 +2055,10 @@ VOID PrintClassInfo( memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); symbol->Type = PV_SYMBOL_TYPE_FUNCTION; - symbol->Address = VarInfo.sDataInfo.Address; - symbol->Size = (ULONG)VarInfo.sDataInfo.Offset; - symbol->Name = PhCreateString(VarInfo.sDataInfo.Name); - symbol->Data = SymbolInfo_GetTypeName(Context, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name); + 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 @@ -2057,12 +2068,7 @@ VOID PrintClassInfo( PhReleaseQueuedLockExclusive(&SearchResultsLock); // Enumerate function parameters and local variables... - IMAGEHLP_STACK_FRAME sf; - - sf.InstructionOffset = VarInfo.sDataInfo.Address; - - SymSetContext_I(NtCurrentProcess(), &sf, 0); - SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); + PdbDumpAddress(Context, VarInfo.DataInfo.Address); } } @@ -2070,7 +2076,7 @@ VOID PrintClassInfo( { TypeInfo baseInfo; - if (!SymbolInfo_DumpType(Context, Info->BaseClasses[i], &baseInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->BaseClasses[i], &baseInfo)) { // Continue } @@ -2083,7 +2089,7 @@ VOID PrintClassInfo( TypeInfo BaseUdtInfo; // Obtain the next base class - if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &BaseUdtInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, baseInfo.BaseClassInfo.TypeIndex, &BaseUdtInfo)) { // Continue } @@ -2093,7 +2099,7 @@ VOID PrintClassInfo( } else { - if (baseInfo.sBaseClassInfo.Virtual) + if (baseInfo.BaseClassInfo.Virtual) { //PhAddItemList(L"VIRTUAL_BASE_CLASS", BaseUdtInfo.sUdtClassInfo.Name) } @@ -2118,21 +2124,21 @@ VOID PrintUserDefinedTypes( { index = PtrToUlong(Context->UdtList->Items[i]); - if (SymbolInfo_DumpType(Context, index, &info)) + if (SymbolInfo_DumpType(Context->BaseAddress, index, &info)) { if (info.Tag == SymTagUDT) { if (info.UdtKind) { // Print information about the class - PrintClassInfo(Context, index, &info.sUdtClassInfo); + PrintClassInfo(Context, index, &info.UdtClassInfo); // Print information about its base classes - for (ULONG i = 0; i < info.sUdtClassInfo.NumBaseClasses; i++) + for (ULONG i = 0; i < info.UdtClassInfo.NumBaseClasses; i++) { TypeInfo baseInfo; - if (!SymbolInfo_DumpType(Context, info.sUdtClassInfo.BaseClasses[i], &baseInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, info.UdtClassInfo.BaseClasses[i], &baseInfo)) { // Continue } @@ -2145,7 +2151,7 @@ VOID PrintUserDefinedTypes( // Obtain information about the base class TypeInfo baseUdtInfo; - if (!SymbolInfo_DumpType(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo)) + if (!SymbolInfo_DumpType(Context->BaseAddress, baseInfo.BaseClassInfo.TypeIndex, &baseUdtInfo)) { // Continue } @@ -2156,7 +2162,7 @@ VOID PrintUserDefinedTypes( else { // Print information about the base class - PrintClassInfo(Context, baseInfo.sBaseClassInfo.TypeIndex, &baseUdtInfo.sUdtClassInfo); + PrintClassInfo(Context, baseInfo.BaseClassInfo.TypeIndex, &baseUdtInfo.UdtClassInfo); } } } @@ -2304,30 +2310,31 @@ NTSTATUS PeDumpFileSymbols( if (!(symsrvPath = PvFindDbghelpPath(TRUE))) return 1; - dbghelpHandle = LoadLibrary(dbghelpPath->Buffer); - symsrvHandle = LoadLibrary(symsrvPath->Buffer); - - if (!(dbghelpHandle = GetModuleHandle(L"dbghelp.dll"))) + if (!(dbghelpHandle = LoadLibrary(dbghelpPath->Buffer))) + return 1; + if (!(symsrvHandle = LoadLibrary(symsrvPath->Buffer))) return 1; - + SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); SymCleanup_I = PhGetProcedureAddress(dbghelpHandle, "SymCleanup", 0); SymEnumSymbolsW_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); + SymEnumTypesW_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumTypesW", 0); SymSetSearchPathW_I = PhGetProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0); SymGetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymGetOptions", 0); SymSetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymSetOptions", 0); SymLoadModuleExW_I = PhGetProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); SymGetModuleInfoW64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetModuleInfoW64", 0); + SymGetTypeFromNameW_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeFromNameW", 0); SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); SymSetContext_I = PhGetProcedureAddress(dbghelpHandle, "SymSetContext", 0); - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME); if (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) return 1; if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*http://msdl.microsoft.com/download/symbols")) - return 1; + goto CleanupExit; if (!NT_SUCCESS(status = PhCreateFileWin32( &fileHandle, @@ -2338,7 +2345,7 @@ NTSTATUS PeDumpFileSymbols( FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) - return 1; + goto CleanupExit; if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) goto CleanupExit; @@ -2361,7 +2368,8 @@ NTSTATUS PeDumpFileSymbols( SymEnumSymbolsW_I(NtCurrentProcess(), Context->BaseAddress, NULL, EnumCallbackProc, Context); // Enumerate user defined types - PrintUserDefinedTypes(Context); + //PrintUserDefinedTypes(Context); + //SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); } CleanupExit: @@ -2375,3 +2383,16 @@ NTSTATUS PeDumpFileSymbols( 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); +} \ No newline at end of file From 3fc71eb033e7ed26216f9fd7e876cc882cd97af3 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 10:09:01 +1000 Subject: [PATCH 171/839] BuildTools: Update minimum framework version --- tools/CustomBuildTool/CustomBuildTool.csproj | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index 83a5df2111b6..86a7da4e9202 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -9,7 +9,7 @@ Properties CustomBuildTool CustomBuildTool - v4.5 + v4.6.1 512 true diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index ef4b4940de3aa5582192417cf4abdbb58da3875c..ae3e4917aa65f38bfe6005a4a561bab2d4bdd05b 100644 GIT binary patch delta 511 zcmZp8!P)SFb3zBROrr9{Zdpd9i7UeyFHB}+RA-&R%*Y@!S@D_9>wUxFxwf(uHkV8vy*}BWjx7Xb~BKDfu|YFUJPXa z;#mx4uVg&T@R;{7P(%etf=oLJ6!{*-`xq$X2P8p4FM&ebe9TPA3>Cc0Odt_Xro#*} ze40Sgh|ig6GeZ`;Cy?#ImkA_e_?nrzfg+tib}`>dAlbop5=hSAdkG}hOnx1$%(!SX zf6US%fy?_$bRO*p^I6bhs#=p*p1k?Uxd3iIUtnx88i6=&w> z*_N5;nduoaGKBDnz$INWiwklREB%0KML@a~;4%te)tlpPS#j$CV}DYQ3{Nwdy%@;; z#5UIK;K`IwoK8A^DWnLr|(OotgH z_%wl}9-lMQW`-c@HI1a14TN4?0mkJK(dAJB#@lK_Yz31nEX0gnQ`7` z{+Ojj0@qq*|B_yNzO?((`i-aVtv9O)bwa%GL=>Eh^5; z&$BHv(KBUa2;mcfOSxng7vv;X`T@zA1fon^o>ePlAC`% d6J?xez|ze3Vmsdp#$TOG3Z~lyU6{Tx0szn>n(hDq diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 65e7ad0ab95c5520814db5dadb6dd0c927b21d5c..7f1693848601036610aee5335abd529cbffc4f95 100644 GIT binary patch delta 380 zcmZpe!_qK^WdjSBgiNAxBqIX@!^-?_;YZc>&bz~>l|I*e<+oXe>y0=EQBB0_tlZ)Plz}b)9Le$y4;hF6B4k5np zT?|}}*?S*2d*1s55PLJ<2MZ>+VA3ZSIQ!5i4>()xGlX6D8Dh!3&ll=7wfE^TFjND5 z1Om(quWmzF3=E76n%b7S3=CJH0_bvldJGJ?IOM88o$x2~6$U%Xj{>y0?aoox#9K3%%R zIJxkH@WcSw%??WMPBJktOg4BE2cjlGs1I+_Ac9hpi{6I7n2+8Dz?gpT;G+B9#lS?( z-uu9q^WG-_nVUYiF~LNVKDoe{hdz10m};NF%!1Eg>o0u1SI@w(Plth_8t4NMV1`g& y667mOT?U4$P%(5lK0OA8TpV)MK)H1|ESq(|UdJ7XTV)uZ@B#qf31FW9 From 88ebde21366f48b49f056557a86d78e9d442255a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 10:32:37 +1000 Subject: [PATCH 172/839] Fix setup crash --- tools/CustomSetupTool/CustomSetupTool/setup.c | 7 +++++++ tools/CustomSetupTool/CustomSetupTool/update.c | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 9f0a316e62d4..495bffc05082 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -193,6 +193,13 @@ BOOLEAN SetupCreateUninstallFile( if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { + if (!DeleteFile(backupFilePath->Buffer)) + { + Context->ErrorCode = GetLastError(); + PhDereferenceObject(uninstallFilePath); + return FALSE; + } + if (!MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer)) { Context->ErrorCode = GetLastError(); diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 1ba229e2cf3e..b1c57350a315 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -149,7 +149,13 @@ VOID SetupShowUpdatingErrorDialog( )->Buffer; if (Context->ErrorCode) - config.pszContent = PhGetStatusMessage(0, Context->ErrorCode)->Buffer; + { + PPH_STRING errorString; + + if (errorString = PhGetStatusMessage(0, Context->ErrorCode)) + config.pszContent = PhGetString(errorString); + } + SendMessage(Context->PropSheetHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } From cc0f158e33235051cd15d6f759eb680b24264016 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 13:19:42 +1000 Subject: [PATCH 173/839] Fix setup hanging on update --- .../CustomSetupTool/include/setup.h | 4 +-- tools/CustomSetupTool/CustomSetupTool/page4.c | 2 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 25 ++++++++----------- .../CustomSetupTool/uninstall.c | 2 +- .../CustomSetupTool/CustomSetupTool/update.c | 2 +- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 1c8852690d86..0696dba7bfd0 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -188,8 +188,8 @@ VOID SetupStartKph( _In_ PPH_SETUP_CONTEXT Context ); -ULONG SetupUninstallKph( - VOID +BOOLEAN SetupUninstallKph( + _In_ PPH_SETUP_CONTEXT Context ); NTSTATUS SetupCreateUninstallKey( diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index 1c6b89477a95..e179d5dedd43 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -35,7 +35,7 @@ NTSTATUS SetupProgressThread( goto CleanupExit; // Stop the kernel driver(s). - if (!SetupUninstallKph()) + if (!SetupUninstallKph(Context)) goto CleanupExit; // Create the install folder path. diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 495bffc05082..4356a30eefec 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -332,35 +332,32 @@ VOID SetupStartKph( PhDereferenceObject(clientPath); } -ULONG SetupUninstallKph( - VOID +BOOLEAN SetupUninstallKph( + _In_ PPH_SETUP_CONTEXT Context ) { - ULONG status = ERROR_SUCCESS; - SC_HANDLE scmHandle; + BOOLEAN deleted = FALSE; SC_HANDLE serviceHandle; SERVICE_STATUS serviceStatus; - if (!(scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT))) - return GetLastError(); - - if (serviceHandle = OpenService(scmHandle, L"KProcessHacker3", SERVICE_STOP | DELETE)) + if (serviceHandle = PhOpenService( + L"KProcessHacker3", + SERVICE_STOP | DELETE + )) { ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - if (!DeleteService(serviceHandle)) - status = GetLastError(); + if (!(deleted = DeleteService(serviceHandle))) + Context->ErrorCode = GetLastError(); CloseServiceHandle(serviceHandle); } else { - status = GetLastError(); + deleted = TRUE; } - CloseServiceHandle(scmHandle); - - return status; + return deleted; } VOID SetupSetWindowsOptions( diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index c03dbaf6713f..c79116583ac8 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -51,7 +51,7 @@ NTSTATUS SetupUninstallBuild( goto CleanupExit; // Stop the kernel driver(s). - if (!SetupUninstallKph()) + if (!SetupUninstallKph(Context)) goto CleanupExit; // Remove autorun and shortcuts. diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index b1c57350a315..0298b54e56c2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -36,7 +36,7 @@ NTSTATUS SetupUpdateBuild( if (!ShutdownProcessHacker()) goto CleanupExit; - if (!SetupUninstallKph()) + if (!SetupUninstallKph(Context)) goto CleanupExit; if (!SetupCreateUninstallFile(Context)) From 5d356872af61bea2fe4672c1f0a190345eb704e3 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 13:41:13 +1000 Subject: [PATCH 174/839] Fix setup build error --- .../CustomSetupTool/include/setup.h | 29 +--------------- tools/CustomSetupTool/CustomSetupTool/main.c | 2 +- tools/CustomSetupTool/CustomSetupTool/page1.c | 8 ++--- tools/CustomSetupTool/CustomSetupTool/page4.c | 4 +-- tools/CustomSetupTool/CustomSetupTool/page5.c | 6 ++-- tools/CustomSetupTool/CustomSetupTool/setup.c | 5 ++- .../CustomSetupTool/uninstall.c | 33 ++++++++++--------- .../CustomSetupTool/CustomSetupTool/update.c | 14 ++++---- 8 files changed, 37 insertions(+), 64 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 0696dba7bfd0..60498e436fd2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -75,7 +75,7 @@ typedef enum _SETUP_COMMAND_TYPE typedef struct _PH_SETUP_CONTEXT { - HWND PropSheetHandle; + HWND DialogHandle; HWND PropSheetBackHandle; HWND PropSheetForwardHandle; HWND PropSheetCancelHandle; @@ -248,33 +248,6 @@ VOID SetupShowUpdateDialog( // uninstall.c -typedef struct _PH_SETUP_UNINSTALL_CONTEXT -{ - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; - - ULONG CurrentMajorVersion; - ULONG CurrentMinorVersion; - ULONG CurrentRevisionVersion; - ULONG LatestMajorVersion; - ULONG LatestMinorVersion; - ULONG LatestRevisionVersion; - - ULONG ErrorCode; - PPH_STRING Version; - PPH_STRING RevVersion; - PPH_STRING RelDate; - PPH_STRING Size; - PPH_STRING Hash; - PPH_STRING Signature; - PPH_STRING ReleaseNotesUrl; - - PPH_STRING BinFileDownloadUrl; - PPH_STRING SetupFileDownloadUrl; - PPH_STRING SetupFilePath; -} PH_SETUP_UNINSTALL_CONTEXT, *PPH_SETUP_UNINSTALL_CONTEXT; - VOID SetupShowUninstallDialog( VOID ); diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 7d0f039d82c2..fe45e4258011 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -104,7 +104,7 @@ INT CALLBACK MainPropSheet_Callback( context->CurrentMinorVersion = PHAPP_VERSION_MINOR; context->CurrentRevisionVersion = PHAPP_VERSION_REVISION; - context->PropSheetHandle = hwndDlg; + context->DialogHandle = hwndDlg; context->PropSheetBackHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_BACK); context->PropSheetForwardHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_NEXT); context->PropSheetCancelHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_CANCEL); diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/page1.c index 3d8dbf6fa0d2..526f37d9d727 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/page1.c @@ -27,8 +27,8 @@ VOID SetupPropSheetCenterWindow( ) { // Center the PropertySheet on the desktop. - PhCenterWindow(Context->PropSheetHandle, NULL); - SetForegroundWindow(Context->PropSheetHandle); + PhCenterWindow(Context->DialogHandle, NULL); + SetForegroundWindow(Context->DialogHandle); } static VOID SetupLoadIcons( @@ -109,13 +109,13 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( case PSN_SETACTIVE: { // Reset the button state. - PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT); + PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT); } break; case PSN_KILLACTIVE: { // Enable the back button. - PropSheet_SetWizButtons(context->PropSheetHandle, PSWIZB_NEXT | PSWIZB_BACK); + PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT | PSWIZB_BACK); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/page4.c index e179d5dedd43..94d2571c126d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/page4.c @@ -72,7 +72,7 @@ NTSTATUS SetupProgressThread( CleanupExit: - PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_ERROR); + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); return STATUS_FAIL_CHECK; } @@ -146,7 +146,7 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( SetWindowText(context->SubStatusHandle, L"Progress: ~ of ~ (0.0%)"); // Disable Next/Back buttons - PropSheet_SetWizButtons(context->PropSheetHandle, 0); + PropSheet_SetWizButtons(context->DialogHandle, 0); if (!SetupRunning) { diff --git a/tools/CustomSetupTool/CustomSetupTool/page5.c b/tools/CustomSetupTool/CustomSetupTool/page5.c index 737cab677e7a..b480f83c671b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page5.c +++ b/tools/CustomSetupTool/CustomSetupTool/page5.c @@ -32,12 +32,12 @@ NTSTATUS SetupDownloadProgressThread( if (!UpdateDownloadUpdateData(Context)) goto CleanupExit; - PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_DIALOG4); + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG4); return STATUS_SUCCESS; CleanupExit: - PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_ERROR); + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); return STATUS_FAIL_CHECK; } @@ -97,7 +97,7 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( SetWindowText(context->SubStatusHandle, L""); // Disable Next/Back buttons - PropSheet_SetWizButtons(context->PropSheetHandle, 0); + PropSheet_SetWizButtons(context->DialogHandle, 0); if (threadHandle = PhCreateThread(0, SetupDownloadProgressThread, context)) NtClose(threadHandle); diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 4356a30eefec..be2a2500c977 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -347,8 +347,7 @@ BOOLEAN SetupUninstallKph( { ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - if (!(deleted = DeleteService(serviceHandle))) - Context->ErrorCode = GetLastError(); + deleted = DeleteService(serviceHandle); CloseServiceHandle(serviceHandle); } @@ -557,7 +556,7 @@ BOOLEAN SetupExecuteProcessHacker( if (RtlDoesFileExists_U(clientPath->Buffer)) { success = PhShellExecuteEx( - Context->PropSheetHandle, + Context->DialogHandle, clientPath->Buffer, NULL, SW_SHOWDEFAULT, diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index c79116583ac8..1fe866ca7671 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -28,20 +28,20 @@ HANDLE UninstallDialogThreadHandle = NULL; PH_EVENT UninstallInitializedEvent = PH_EVENT_INIT; VOID ShowUninstallConfirmDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); VOID ShowUninstallDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); VOID ShowUninstallCompleteDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); VOID ShowUninstallErrorDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ); NTSTATUS SetupUninstallBuild( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { //context->SetupInstallPath = SetupFindInstallDirectory(); @@ -76,7 +76,7 @@ NTSTATUS SetupUninstallBuild( } static VOID TaskDialogCreateIcons( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { HICON largeIcon; @@ -112,7 +112,7 @@ HRESULT CALLBACK TaskDialogUninstallConfirmCallbackProc( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -143,7 +143,7 @@ HRESULT CALLBACK TaskDialogUninstallCallbackProc( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -168,7 +168,7 @@ HRESULT CALLBACK TaskDialogUninstallCompleteCallbackProc( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -181,7 +181,7 @@ HRESULT CALLBACK TaskDialogUninstallCompleteCallbackProc( } VOID ShowUninstallCompleteDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -202,7 +202,7 @@ VOID ShowUninstallCompleteDialog( } VOID ShowUninstallConfirmDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -224,7 +224,7 @@ VOID ShowUninstallConfirmDialog( } VOID ShowUninstallDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -244,7 +244,7 @@ VOID ShowUninstallDialog( } VOID ShowUninstallErrorDialog( - _In_ PPH_SETUP_UNINSTALL_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -271,7 +271,7 @@ HRESULT CALLBACK TaskDialogUninstallBootstrapCallback( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UNINSTALL_CONTEXT context = (PPH_SETUP_UNINSTALL_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -303,8 +303,9 @@ VOID SetupShowUninstallDialog( memset(&config, 0, sizeof(TASKDIALOGCONFIG)); PhInitializeAutoPool(&autoPool); - context = (PPH_SETUP_UNINSTALL_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_UNINSTALL_CONTEXT)); - memset(context, 0, sizeof(PH_SETUP_UNINSTALL_CONTEXT)); + + 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; diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 0298b54e56c2..ae71b4ecb5a7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -52,13 +52,13 @@ NTSTATUS SetupUpdateBuild( if (!SetupExecuteProcessHacker(Context)) goto CleanupExit; - PostMessage(Context->PropSheetHandle, WM_QUIT, 0, 0); + PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); PhDereferenceObject(Context); return STATUS_SUCCESS; CleanupExit: - PostMessage(Context->PropSheetHandle, WM_APP + IDD_ERROR, 0, 0); + PostMessage(Context->DialogHandle, WM_APP + IDD_ERROR, 0, 0); PhDereferenceObject(Context); return STATUS_FAIL_CHECK; } @@ -100,8 +100,8 @@ VOID TaskDialogCreateIcons( Context->IconLargeHandle = largeIcon; Context->IconSmallHandle = smallIcon; - SendMessage(Context->PropSheetHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); - SendMessage(Context->PropSheetHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); } HRESULT CALLBACK SetupErrorTaskDialogCallbackProc( @@ -156,7 +156,7 @@ VOID SetupShowUpdatingErrorDialog( config.pszContent = PhGetString(errorString); } - SendMessage(Context->PropSheetHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } LRESULT CALLBACK TaskDialogSubclassProc( @@ -244,7 +244,7 @@ VOID SetupShowUpdatingDialog( PHAPP_VERSION_REVISION )->Buffer; - SendMessage(Context->PropSheetHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -261,7 +261,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( { case TDN_CREATED: { - context->PropSheetHandle = hwndDlg; + context->DialogHandle = hwndDlg; // Center the window on the desktop. PhCenterWindow(hwndDlg, NULL); From 276005f8acaa3ea3ebb2b363541c6533bd0688f3 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 14:07:14 +1000 Subject: [PATCH 175/839] Fix appveyor build version --- tools/CustomBuildTool/Source Files/Build.cs | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 3cf61d381a22..8c08ea46f01b 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -263,7 +263,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo BuildCount = "0"; BuildVersion = "3.0." + BuildRevision; - BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; + BuildLongVersion = "3.0." + BuildRevision + "." + BuildCount; if (ShowBuildInfo && !GitExportBuild) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index ae3e4917aa65f38bfe6005a4a561bab2d4bdd05b..402eed5c4938f85546126704a750ea62597dce88 100644 GIT binary patch delta 118 zcmV-+0Ez#A=m~)636O{bTb?Vii!ucZ3IG5Ea;E_RaDEB1$OY`k5ZVO;a?QKOM7Kx& z1aH2NPP4?$*cJm@o-31|<{Da;E_RaDEE2$OY`k5YxUiEaJFUOrV4{ zC46poW3$B0*cJmEVIz~DDe6uE0000)I$UsaZ)#;@baRv8 RcXA1Mgax3n$w&F?%Ny=YedcqtL#r-{H}^##FE3Zz>~`&Oi#sY@ z`^42Q_s>xkne-UxR!NYTe@W?7U{T~+>z zJ8Qd}y^>I>1C}U8s-*qpl@m<#V#L5mB%c5(z#OE!7Q27m9&ukK+Q~kRiUok(O8#a&W_l6r)W6PDG79M-!Y*)#+|t`TL{o z`D2iF2jUtElBHFtrotf%n1_KgUfbZD?)?i9ZAwYW2y>xU2Q5R5efdvR0magiD3-AB z*kY+8by@`>w3Kj!Hlk)}&%cyKS!k0KOIUa;ZBmV0nDLiLv9x)LB`iGFDcNVm>1p$X zIu+-vmn-29c* zgw7nfjhwyb*6Tr3vRga#=T*Sk{9uLRe$mF;>JPsP8mnUfBDRDUq@bL)FzQv|waa@H4R)^RX4J`c!j@vtm z3Ce<@4&IaVN?OcuQKL>hSCl(y;swolbsjOD26mCbyUgJE!g-!CAH2#S-U=s<;oW8M z9x!;GXAEKwgV@X9^)qxXU;CsCR!Q9bjAm5V|cAg zhk54*yhI!ET3)C8=bovg zf54|dT1eFTStx8w2Ag&_qgJ1(4KHf^7fcLT&IO2N;n;2ySUC35>DqXH;(sYvK^(tf z(I=xGL2X2RT;9opB|a5RdR%%@qES{NY*6gcS#VYWUBaLEaxO);Yzk>KxiUdc5#ZP4 zgJ>T@iA33tQiHM&r50tc+#$d;@XFl+#6hh5NB~RcI{^y7Sc>pYlya0Zc|#=3hiadr Q9-!W5e*m2`bztK^0g7=-XaE2J delta 2279 zcmaJ?ZD?C%6n@XmZF2KT+SF{;#5USiv!=DR?dU{m!wTCE*F{>H!-8&Ii7V92eyq-J zOLYp0AZ9P@GFt+3S}1PA?jjK<#j2=NWPYO#wo%xGVLvic{WG2CG`CrDcXKa1ob#UN zdET$fy=QDf9Gei|3>6&^7-LDW!Ff0%xCOYZ6pMcqt@Z7=cRL_6wdZxu+~^}4&u%+9 z{73LwhvKt^+@;G0y(wX0{h!~4(nf40xms|9Netr`P?W`RYc3rU- zw;CPT(O=gysM&a%u3mPF;Jj`2L_l}XetEA5)?gW4$|PbQK~EFj?tVlB?|&R|6;X>f z*TusrPdp+O&md``m!m$N@$edO#>1W?m68OXfAsaHAAbP;+;tTDd0S=acAD*e6yG&h z4<|QI)^%e}t8`){u2-Fi0D;T@2iINdCN13uX^G3#T6ja1$4VDKtVP*Ub)i+bDk$rAB}_1>oQp>7 z7#apgXf(BNM1+VE7%i7@6h3^PfTP{7Y;(bkGRsGbu(;vadsMtKzSRm%O7pu(D;DQZ zN1a%F{Nq6jj?R6y)g6zBitneAGG6U)^VWsPN=&HnzlA5%x`Piac4hOq#f+|X!NEJ0 zZq;X&y0e<~sYkPUBK0)oitD;pfEHer4rHmF>Go{?X?k-uuU^jNhn6!-U0nVy7Oa^G zF*b_VLyw7FyO7Bk0~V~=P|MiQYC+4n>KN-Uz>OkzT9;!i_yA-3R8CZC|2ZYZ7~@U& ze$=8>jTG?JvA2+u)S8xi+rT$v;G53%;riK!7W~k__(`rY>&k5OO9S6|17GYH1LH*l z;}rwnyn!#B@4Iz=I{gg;qp7*@tFR+i=oOY2_{y4h7B&V9j6s71>ovX;EjNUXm+^Ep zc*G&ZCd~El2G&Wp3esqQ|MY{_)~M0s*H(I!rq-qzH*#tv`MySuz378p7>9V|;UhNj z&O$BJFZ%_k*J~^*%`l|LH0TvqnwPiVKpaFILd4`*GkD}*&ESKQWXcSk;M|S%XAn_DpZu5v YHPh`cL(xk(Je(}H!J;WyVFQQoKg!qH{{R30 From fa5aa92c2575339a13d2bd327412bb5a6dad869f Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 19:25:04 +1000 Subject: [PATCH 176/839] Remove extra gitattributes --- .gitattributes | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 0fb12ca3d9a1..b895d0391026 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,9 +15,7 @@ *.config eol=crlf *.cmd eol=crlf *.csproj eol=crlf -*.filters eol=crlf *.h eol=crlf -*.md eol=crlf *.manifest eol=crlf *.rc eol=crlf *.sln eol=crlf From e8718db3fd333e2bcba0b53099de34ceb75a26b1 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 19:45:47 +1000 Subject: [PATCH 177/839] Backport deprecated v2 debug code --- .../bin-signed/amd64/kprocesshacker.sys | Bin 45208 -> 40088 bytes .../bin-signed/i386/kprocesshacker.sys | Bin 41624 -> 36376 bytes ProcessHacker/main.c | 15 +- phlib/include/kphapi.h | 55 +-- phlib/include/kphuser.h | 51 ++- phlib/kph.c | 411 +++++------------- phlib/native.c | 61 +-- phlib/phlib.vcxproj | 3 +- phlib/phlib.vcxproj.filters | 3 - 9 files changed, 167 insertions(+), 432 deletions(-) diff --git a/KProcessHacker/bin-signed/amd64/kprocesshacker.sys b/KProcessHacker/bin-signed/amd64/kprocesshacker.sys index 7d4695902a56f21840791f0efcdf74d35bad3c34..4da0af31c218fe951f263f863345fdc66545eced 100644 GIT binary patch delta 19905 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 Uc5kunx$lO(f8lPu>jT;U0^YzbjsO4v delta 22087 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 diff --git a/KProcessHacker/bin-signed/i386/kprocesshacker.sys b/KProcessHacker/bin-signed/i386/kprocesshacker.sys index 4f5372a8bd5b0e3530c2871c4dc771a9e4d6550d..6771d7266b67b245778ed488935031d585c92f48 100644 GIT binary patch literal 36376 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 Z5f6WBy*w?#JL%zwcX$1j^YQD<{{d(wt0w>e delta 21033 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 diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 3d8b82255a96..688a815cf533 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -553,32 +553,19 @@ VOID PhInitializeKph( ) { 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( @@ -980,7 +967,7 @@ VOID PhpProcessStartupParameters( kprocesshackerFileName = PhConcatStrings2(PhApplicationDirectory->Buffer, L"\\kprocesshacker.sys"); - parameters.SecurityLevel = KphSecuritySignatureCheck; + parameters.SecurityLevel = KphSecurityPrivilegeCheck; parameters.CreateDynamicConfiguration = TRUE; status = KphInstallEx(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index f905de9a09d3..36d03742e9e9 100644 --- a/phlib/include/kphapi.h +++ b/phlib/include/kphapi.h @@ -114,7 +114,7 @@ typedef struct _ETWREG_BASIC_INFORMATION // Device -#define KPH_DEVICE_SHORT_NAME L"KProcessHacker3" +#define KPH_DEVICE_SHORT_NAME L"KProcessHacker2" #define KPH_DEVICE_TYPE 0x9999 #define KPH_DEVICE_NAME (L"\\Device\\" KPH_DEVICE_SHORT_NAME) @@ -124,8 +124,6 @@ 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; @@ -135,7 +133,7 @@ typedef struct _KPH_DYN_STRUCT_DATA SHORT EpObjectTable; SHORT Reserved0; SHORT Reserved1; - SHORT Reserved2; + SHORT EpRundownProtect; SHORT EreGuidEntry; SHORT HtHandleContentionEvent; SHORT OtName; @@ -154,7 +152,7 @@ typedef struct _KPH_DYN_PACKAGE KPH_DYN_STRUCT_DATA StructData; } KPH_DYN_PACKAGE, *PKPH_DYN_PACKAGE; -#define KPH_DYN_CONFIGURATION_VERSION 3 +#define KPH_DYN_CONFIGURATION_VERSION 2 #define KPH_DYN_MAXIMUM_PACKAGES 64 typedef struct _KPH_DYN_CONFIGURATION @@ -164,33 +162,6 @@ typedef struct _KPH_DYN_CONFIGURATION 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. @@ -201,26 +172,24 @@ typedef enum _KPH_KEY_LEVEL // 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_OPENPROCESS KPH_CTL_CODE(50) +#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(51) #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_SUSPENDPROCESS KPH_CTL_CODE(53) +#define KPH_RESUMEPROCESS KPH_CTL_CODE(54) +#define KPH_TERMINATEPROCESS KPH_CTL_CODE(55) #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_READVIRTUALMEMORYUNSAFE KPH_CTL_CODE(58) #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_OPENTHREAD KPH_CTL_CODE(100) #define KPH_OPENTHREADPROCESS KPH_CTL_CODE(101) -#define KPH_RESERVED102 KPH_CTL_CODE(102) +#define KPH_TERMINATETHREAD 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) @@ -232,7 +201,7 @@ typedef enum _KPH_KEY_LEVEL #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) +#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(153) // Misc. #define KPH_OPENDRIVER KPH_CTL_CODE(200) diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index 102534b55a62..a6905cb2f5ab 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -51,13 +51,6 @@ KphIsConnected( VOID ); -PHLIBAPI -BOOLEAN -NTAPI -KphIsVerified( - VOID - ); - PHLIBAPI NTSTATUS NTAPI @@ -97,14 +90,6 @@ KphGetFeatures( _Out_ PULONG Features ); -PHLIBAPI -NTSTATUS -NTAPI -KphVerifyClient( - _In_reads_bytes_(SignatureSize) PUCHAR Signature, - _In_ ULONG SignatureSize - ); - PHLIBAPI NTSTATUS NTAPI @@ -132,6 +117,20 @@ KphOpenProcessJob( _Out_ PHANDLE JobHandle ); +PHLIBAPI +NTSTATUS +NTAPI +KphSuspendProcess( + _In_ HANDLE ProcessHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphResumeProcess( + _In_ HANDLE ProcessHandle + ); + PHLIBAPI NTSTATUS NTAPI @@ -190,6 +189,14 @@ KphOpenThreadProcess( _Out_ PHANDLE ProcessHandle ); +PHLIBAPI +NTSTATUS +NTAPI +KphTerminateThread( + _In_ HANDLE ThreadHandle, + _In_ NTSTATUS ExitStatus + ); + PHLIBAPI NTSTATUS NTAPI @@ -264,12 +271,24 @@ KphSetInformationObject( _In_ ULONG ObjectInformationLength ); +PHLIBAPI +NTSTATUS +NTAPI +KphDuplicateObject( + _In_ HANDLE SourceProcessHandle, + _In_ HANDLE SourceHandle, + _In_opt_ HANDLE TargetProcessHandle, + _Out_opt_ PHANDLE TargetHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG HandleAttributes, + _In_ ULONG Options + ); + PHLIBAPI NTSTATUS NTAPI KphOpenDriver( _Out_ PHANDLE DriverHandle, - _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes ); diff --git a/phlib/kph.c b/phlib/kph.c index 0acd7d0baa27..8311273e632e 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -22,11 +22,8 @@ #include #include -#include HANDLE PhKphHandle = NULL; -BOOLEAN PhKphVerified; -KPH_KEY PhKphL1Key; NTSTATUS KphConnect( _In_opt_ PWSTR DeviceName @@ -79,8 +76,6 @@ NTSTATUS KphConnect( ); PhKphHandle = kphHandle; - PhKphVerified = FALSE; - PhKphL1Key = 0; } return status; @@ -245,8 +240,6 @@ NTSTATUS KphDisconnect( status = NtClose(PhKphHandle); PhKphHandle = NULL; - PhKphVerified = FALSE; - PhKphL1Key = 0; return status; } @@ -258,52 +251,42 @@ BOOLEAN KphIsConnected( return PhKphHandle != NULL; } -BOOLEAN KphIsVerified( - VOID - ) -{ - return PhKphVerified; -} - NTSTATUS KphSetParameters( _In_opt_ PWSTR DeviceName, _In_ PKPH_PARAMETERS Parameters ) { + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\kph2\\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( + if (!NT_SUCCESS(status = PhCreateKey( ¶metersKeyHandle, KEY_WRITE | DELETE, PH_KEY_LOCAL_MACHINE, - ¶metersKeyName->sr, + &keyName, 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)) + if (!NT_SUCCESS(status = NtSetValueKey( + parametersKeyHandle, + &valueName, + 0, + REG_DWORD, + &Parameters->SecurityLevel, + sizeof(ULONG) + ))) + { goto SetValuesEnd; + } if (Parameters->CreateDynamicConfiguration) { @@ -316,10 +299,14 @@ NTSTATUS KphSetParameters( 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; + status = NtSetValueKey( + parametersKeyHandle, + &valueName, + 0, + REG_BINARY, + &configuration, + sizeof(KPH_DYN_CONFIGURATION) + ); } } @@ -449,6 +436,28 @@ NTSTATUS KphUninstall( return status; } +NTSTATUS KphpDeviceIoControl( + _In_ ULONG KphControlCode, + _In_ PVOID InBuffer, + _In_ ULONG InBufferLength + ) +{ + IO_STATUS_BLOCK isb; + + return NtDeviceIoControlFile( + PhKphHandle, + NULL, + NULL, + NULL, + &isb, + KphControlCode, + InBuffer, + InBufferLength, + NULL, + 0 + ); +} + NTSTATUS KphGetFeatures( _Out_ PULONG Features ) @@ -465,52 +474,24 @@ NTSTATUS KphGetFeatures( ); } -NTSTATUS KphVerifyClient( - _In_reads_bytes_(SignatureSize) PUCHAR Signature, - _In_ ULONG SignatureSize +NTSTATUS KphOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId ) { - NTSTATUS status; struct { - PVOID CodeAddress; - PUCHAR Signature; - ULONG SignatureSize; - } input = { KphpWithKeyApcRoutine, Signature, SignatureSize }; + PHANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PCLIENT_ID ClientId; + } input = { ProcessHandle, DesiredAccess, ClientId }; - status = KphpDeviceIoControl( - KPH_VERIFYCLIENT, + return KphpDeviceIoControl( + KPH_OPENPROCESS, &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( @@ -519,21 +500,18 @@ NTSTATUS KphOpenProcessToken( _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 + struct { - return KphpWithKey(KphKeyLevel2, KphpOpenProcessTokenContinuation, &input); - } + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PHANDLE TokenHandle; + } input = { ProcessHandle, DesiredAccess, TokenHandle }; + + return KphpDeviceIoControl( + KPH_OPENPROCESSTOKEN, + &input, + sizeof(input) + ); } NTSTATUS KphOpenProcessJob( @@ -562,11 +540,20 @@ NTSTATUS KphTerminateProcess( ) { NTSTATUS status; - KPH_TERMINATE_PROCESS_INPUT input = { ProcessHandle, ExitStatus, 0 }; + struct + { + HANDLE ProcessHandle; + NTSTATUS ExitStatus; + } input = { ProcessHandle, ExitStatus }; - status = KphpWithKey(KphKeyLevel2, KphpTerminateProcessContinuation, &input); + status = KphpDeviceIoControl( + KPH_TERMINATEPROCESS, + &input, + sizeof(input) + ); - // Check if we're trying to terminate the current process, because kernel-mode can't do it. + // 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); @@ -583,9 +570,20 @@ NTSTATUS KphReadVirtualMemoryUnsafe( _Out_opt_ PSIZE_T NumberOfBytesRead ) { - KPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead, 0 }; + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + SIZE_T BufferSize; + PSIZE_T NumberOfBytesRead; + } input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead }; - return KphpWithKey(KphKeyLevel2, KphpReadVirtualMemoryUnsafeContinuation, &input); + return KphpDeviceIoControl( + KPH_READVIRTUALMEMORYUNSAFE, + &input, + sizeof(input) + ); } NTSTATUS KphQueryInformationProcess( @@ -640,21 +638,18 @@ NTSTATUS KphOpenThread( _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 + struct { - return KphpWithKey(KphKeyLevel2, KphpOpenThreadContinuation, &input); - } + PHANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + PCLIENT_ID ClientId; + } input = { ThreadHandle, DesiredAccess, ClientId }; + + return KphpDeviceIoControl( + KPH_OPENTHREAD, + &input, + sizeof(input) + ); } NTSTATUS KphOpenThreadProcess( @@ -865,16 +860,14 @@ NTSTATUS KphSetInformationObject( 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 }; + } input = { DriverHandle, ObjectAttributes }; return KphpDeviceIoControl( KPH_OPENDRIVER, @@ -905,198 +898,4 @@ NTSTATUS KphQueryInformationDriver( &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) - ); -} +} \ No newline at end of file diff --git a/phlib/native.c b/phlib/native.c index d00fdb099868..236d5a8a23c1 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -116,16 +116,15 @@ NTSTATUS PhOpenProcess( _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) + if (KphIsConnected()) { - status = KphOpenProcess( + return KphOpenProcess( ProcessHandle, DesiredAccess, &clientId @@ -134,24 +133,14 @@ NTSTATUS PhOpenProcess( else { InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - status = NtOpenProcess( + + return NtOpenProcess( ProcessHandle, DesiredAccess, &objectAttributes, &clientId ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenProcess( - ProcessHandle, - DesiredAccess, - &clientId - ); - } } - - return status; } /** Limited API for untrusted/external code. */ @@ -189,16 +178,15 @@ NTSTATUS PhOpenThread( _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) + if (KphIsConnected()) { - status = KphOpenThread( + return KphOpenThread( ThreadHandle, DesiredAccess, &clientId @@ -207,24 +195,14 @@ NTSTATUS PhOpenThread( else { InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - status = NtOpenThread( + + return NtOpenThread( ThreadHandle, DesiredAccess, &objectAttributes, &clientId ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenThread( - ThreadHandle, - DesiredAccess, - &clientId - ); - } } - - return status; } /** Limited API for untrusted/external code. */ @@ -296,11 +274,9 @@ NTSTATUS PhOpenProcessToken( _Out_ PHANDLE TokenHandle ) { - NTSTATUS status; - - if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) + if (KphIsConnected()) { - status = KphOpenProcessToken( + return KphOpenProcessToken( ProcessHandle, DesiredAccess, TokenHandle @@ -308,23 +284,12 @@ NTSTATUS PhOpenProcessToken( } else { - status = NtOpenProcessToken( + return NtOpenProcessToken( ProcessHandle, DesiredAccess, TokenHandle ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenProcessToken( - ProcessHandle, - DesiredAccess, - TokenHandle - ); - } } - - return status; } NTSTATUS PhGetObjectSecurity( @@ -403,7 +368,7 @@ NTSTATUS PhTerminateProcess( { NTSTATUS status; - if (KphIsVerified()) + if (KphIsConnected()) { status = KphTerminateProcess( ProcessHandle, @@ -2583,7 +2548,7 @@ BOOLEAN NTAPI PhpOpenDriverByBaseAddressCallback( NULL ); - status = KphOpenDriver(&driverHandle, SYNCHRONIZE, &objectAttributes); + status = KphOpenDriver(&driverHandle, &objectAttributes); PhDereferenceObject(driverName); if (!NT_SUCCESS(status)) diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index e395b2239ea8..042c17ad6cc6 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,4 +1,4 @@ - + @@ -202,7 +202,6 @@ - diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index dee92e1b386b..cfd2a295a596 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -346,9 +346,6 @@ Header Files - - Header Files - Header Files From 0ae8713d7cf455fa97e4292896169b911431bc33 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 May 2017 19:48:18 +1000 Subject: [PATCH 178/839] Update gitattributes --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index b895d0391026..ba1245642f8e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,11 +12,13 @@ # Project files *.bat eol=crlf *.c eol=crlf +*.cs eol=crlf *.config eol=crlf *.cmd eol=crlf *.csproj eol=crlf *.h eol=crlf *.manifest eol=crlf +*.resx eol=crlf *.rc eol=crlf *.sln eol=crlf *.txt eol=crlf From 1a3b2fbe9e506ec5ac92462ae140d011c14ea323 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 30 May 2017 07:33:28 +1000 Subject: [PATCH 179/839] Fix sdk postbuild --- ProcessHacker/ProcessHacker.vcxproj | 6 +++++ tools/CustomBuildTool/Source Files/Build.cs | 24 +++++++++--------- tools/CustomBuildTool/Source Files/Program.cs | 20 +++++++++------ .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 03188043fdab..2220ef6efe98 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -178,6 +178,9 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + $(SolutionDir)build\build_sdk.cmd + @@ -212,6 +215,9 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + $(SolutionDir)build\build_sdk.cmd + diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 8c08ea46f01b..48b75f1d9eec 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -529,26 +529,26 @@ public static bool BuildPublicHeaderFiles() return true; } - public static bool CopyKProcessHacker(bool DebugBuild) + public static bool CopyKProcessHacker(BuildFlags Flags) { - Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan); + Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan, true, Flags); if (!File.Exists(CustomSignToolPath)) return true; if (!File.Exists("build\\kph.key")) return true; - if (DebugBuild) + if (Flags.HasFlag(BuildFlags.BuildDebug)) { if (!File.Exists("bin\\Debug32\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Debug32\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + Program.PrintColorMessage("[SKIPPED] Debug32\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); return true; } if (!File.Exists("bin\\Debug64\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Debug64\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + Program.PrintColorMessage("[SKIPPED] Debug64\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); return true; } @@ -563,14 +563,14 @@ public static bool CopyKProcessHacker(bool DebugBuild) 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); + Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow, true, Flags); return false; } 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); + Program.PrintColorMessage("[WARN] (Debug64) " + output, ConsoleColor.Yellow, true, Flags); return false; } } @@ -578,13 +578,13 @@ public static bool CopyKProcessHacker(bool DebugBuild) { if (!File.Exists("bin\\Release32\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Release32\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + Program.PrintColorMessage("[SKIPPED] Release32\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); return true; } if (!File.Exists("bin\\Release64\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Release64\\ProcessHacker.exe not found.", ConsoleColor.Yellow); + Program.PrintColorMessage("[SKIPPED] Release64\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); return true; } @@ -599,21 +599,21 @@ public static bool CopyKProcessHacker(bool DebugBuild) 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("[ERROR] (Release32) " + output, ConsoleColor.Red); + Program.PrintColorMessage("[ERROR] (Release32) " + output, ConsoleColor.Red, true, Flags); return false; } output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"); if (!string.IsNullOrEmpty(output)) { - Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red); + Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red, true, Flags); return false; } } try { - if (DebugBuild) + if (Flags.HasFlag(BuildFlags.BuildDebug)) { Win32.CopyIfNewer( "KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index f123314e63a1..2633a5abf563 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -86,15 +86,16 @@ public static void Main(string[] args) Build.ShowBuildStats(); } - else if (ProgramArgs.ContainsKey("-sign")) - { - Build.CopyKProcessHacker(false); - } 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 @@ -129,7 +130,8 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyKProcessHacker(false)) + if (!Build.CopyKProcessHacker( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) @@ -160,7 +162,9 @@ public static void Main(string[] args) BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; - if (!Build.CopyKProcessHacker(true)) + if (!Build.CopyKProcessHacker( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) return; if (!BuildSdk( @@ -196,7 +200,7 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyKProcessHacker(false)) + if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) @@ -270,7 +274,7 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.CopyKProcessHacker(false)) + if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 402eed5c4938f85546126704a750ea62597dce88..fb97a2ee1ecc1d7046c6727053448910267db157 100644 GIT binary patch delta 7515 zcmai(33wD$w#Uz{s;=(p?sTfVlit!RA&_JOA;=aMArc4(8X$x$RiFe7)FL5BOnuvh>GBXIA}B|ndgTf^OWE+bIz?Y-T1yYul@Yz{?57g zoOA2mu3OdATvTcO?x6tl)zwdAZ^RhuYS<_;@W=AL%!y5AU0FD`ibW$o zN(|^T0{<7l0AEi7*z_a-S+ObCG(?1Sv%OLPPyp;z>s(M7vbcp>X9#G&cNud?5g3`c<;}=T5s-i zXII_gi~H`wHjlI+iDJmMZoXe)fbi==VuNgVN+6V!H0-v$6k^9?le>Qy@wzf`R&qC% z9mup|qf00`vyA>`uj~XMRXrWMBe##`R!Alqq;ffu} z3;o|U4D6DFQ_6J33+eJppq$C^C<7^q^@jRO^0Ly0cl7OEMeWR)7$=8icU-5c*|F_; zVYc``7tuAA*3%v9)w3&G9V_d3jy>4W@1}{2ZED!qtAs^1QIg5D8+yog>G7&P9USKT zQfy(fi=Yj-Q1t4v86Y^ot!-iD$^D`Mu|qrRYPK>gCi_EcJJURtT6W8;QrmLm^8g) zan;=UcQ3*PguZ908r7EUQ{}=5dXd#%OCP^)TGi^46A@<+i0H*(~?Oy zB-wn)@TrgN?K&&-Gtcn*BNp%_Eb~V+Sv;4Zr3xRCD{6+}KWH_Xy0%3IBoEBlE;a0LR89ED(Mb> zUB7lwmJb!u;}O#6HHF+kKKsvkvJ<>hBU)^*+tU&>!<&IRy!d}bm0_hyi}D>ViW#IZ zEzJjx5Ze7b&Ugm}?T-;`aG#kL=%G~7>weN?7cJH!))jsS*p=sT?189vF%Guz75dgg zrJUnyc0p-z*g$dHd^Sim(=6MJbTL)alGIKxJg_9y1#d^_!ghBq!OeJ`?;fKkL-awq z$tU<=IM*$~2XF)a%$I2>mKcOG?=kFO6sBD7`19yecTOR_m1~}f+I@LD%!gG*Xt%%P zrU76M#I6i=Mmrg&I>+MSME|Li~Hud$(O|`1w1UO;ho%h28eQD0jCiWjTUms}HBTs+DB*C}#%VXsw*@93(LHOouOMwKJ_c+95epp$5L-3u zrfBc*Z6-T-jbl-(Bc$E!E5Qym9@5+psX3LjGgn`j+6PlepEi;1a*W4ObzGH|r_Rg()c;hJeo znC|+UVSPwY7)W?-5dBxEMG|1SPOpZHiU8%EHD5)rMKZvLuLMht3%A=z5bMvnvOEj(7KG=tx!nBbFc z8eLB&THalw@fgJfpXoG{(?_|QFFJzb-X0nb1=x>%jy| zbt+a{Bol1vt@(Chp(fbhSEEXo2dOMyqiLp)Vum3@H2NlrK`qel77Ya}(OaDAJRf>n zBn#X>jC^Z6r@S&)VB-yRZ8-T>!Kt(i95<=x7wV%};SHSz`fpW|!84M4YvB20JeuKa zJoITCgXm<1PJIAT)l%3w0W=x*hG!^oTI^Dv^d={o(5_q*XdQ*mDf5*6Em}B0a1Vl{OaspwrJOD$-Lr zEy9H3V7E^9Iq0D!`ht$Xb(Z6fIiS;0r;2n$r)}sP4{zwS2Yut=luq}fuN2Pev=M!! z@VQQp;29713;Dp9$!+b0 zv{a{bYk#B~or=TtPz{<_DZU{OX2I{`wu!ZK?@MoRAW0RFT;3Oxl znDQIOmBU%|Vg6dYA*{eXOh0Rd(nzP`fh)jaJeoxK43)vp{$J}7vFw3HQ>ElaC7=I-VQKAGL2i%c3FOY_T7cRcM)LMt)B#WrAB0E4LSY`h=~WAT0ux{#PqPosgl526 z;R*cSR0|t}bD&OG8=MbYg~rGtZdr;dcUp-S&Hij;6}G>yK7#gPc<-Z8+n_os{2ZCj zVJD_3wba8-VU%SNCcc#V5*&qB{D)8@zC*mvt8kR%t0yqo;V>06*hpjh8n2?IIou9A z;T+yM>oCnC)a_Oy+b7Ji%Gkb?+>Yr<58}jB)H#kWtQn#-`(_wo$!1hh4_1#MMzW&< z1+oGi$<4wW$y3=WelbtM-vcw*Sz(#f8(IWOxQn%6xC-=UNlRF}AcbUnTAvuwV_!4+pjdP27>3XhO>>Ftxjd!^-k9$C11@oX2{t)!zYGFKIgpc(Jw6WGgpjumI~ z9gyqaE-B(HGwqwoP&w>}fsuJabh0?udQ{qv30{|I#wXZ*oX2};x$b;V+9_0eKSpiG z4YgCK!xzJP_TQmP(mqW8Pt;G8AEbpM?V+=5N>DJIWwP01s1|eZd$CZ=H>;@2f}{)| zQ_iydu+6Yg_{8W&y(N@pP@>{BUeN=At_DRs?9M@LL~X_dgAAuohZ*X`8l~8{6k9sqB@`sWtL(eSbV_c~LG9L*aMit;}I*MU9}=!%fmxsORxkR?R9RSLLx7 zp&hkVEnvoKp_`gyd`k~8*m*+8l0u=N@eI_kM)3^T*joNAIL#{H<>a2mJZZai0FKJ{ zmO}+xX*fN(NQz2IEuKS{tfWbn2T;d5NbgJ~{m4vO?jmiplRnP%Hr}UHB1^6HA=D<` z)0gOMonvC#X5F@i%@TV`vK7_U~{XGjw5dkE24)84nq#8QW5L#kG_si zi7~)`pt#ZYVr*!l8y&|wvaIM*770rR z2J|Vx{{=9>>4^Yao&g{$Hs$3lVIke@7%l)P0FLU1T`)IjaR;KKtZBZ2R_s;%jskPD z(gDn!)as+;wxN5IHH8JDZ(A3l_DJYj5XhR0(OfyzgRHUMUwF0N+~>}z?(d8F{)lZJ z>DdH|;j(oyUylJobfRslbcqefZ`o(t7Gy`GwVglBH@UK`n4?3^k~9FR`uSp*JLd^3 zE7$4D!s*DJSWeKMRT0q^GL4w{wW|JJF5ZGouAP z8|07BQ+>4MuV)wroLD&W*8YRBx>O8dmia}M+f%N7pKMdHVv8{=mv&T0 z$}v_~3_&YywOQ+DMN9PiLVxz}?1`p1|A9ke-4p(v`gd=lRJ}Huf6Gy}zeVmnNWe?z z_S*`XKf3(3L-w9SVQ$KtwUlV6Ts~ng<~_h_U4v!TazloH_3OW$}rAGdRM}QunZ5{ zN#6-%MUrI5vt*%Wx-&%?M!2Z`SQ2Tf=dF+#9tkdU%Wx2KQqAzDX_?yw?^~ApZSbO( zbPikYm%+$vk`sOdn^X@+yj1jVY|C(wtJTXRW1scV1254HypIz~*iLsuhQ5rF6>v2t z(CnYE&^+?_JT~*vgZ*G~ z0Ge$FG2GCE6CN4v!)=mbljmjB&s}ezeYZ6|(FU(1q$gsWAeHh@m2@*-*B@P!GE?D#HquHidF>$5?~OtZXdq{U1z)g`AvS!!Lf3r>bD6tdO2c1nm!3Skrp9?&W z7og&!$ai3g0hsPRi2X-|$a2b`M@!u$iF6d#-X?1I=k1&L?k*0~)qcuN11#pOC@V^g zbTUp$EW?wC{!?LtLga;1PHU5h{15=9s?aq+lm;2F3BP`ShR8DVJTDLYNA$X z1u(oL{Oq5W*b3=zC->F3$(PM32|O&K;Z*KC4n$pG4yW?4M)Nr3^MPGp0biNfeAT+Z zLQeODsOK%Pj3>04DB&Pj*b+X|zR=$_T=8S-hqN$Anw(79g==Pr+V3TizG5PM)j1MJJ4&~ zFS!1~)#6+NdC~)W3cV^lO9*d za?MSe;avhQ@TOLoWciYKMjl-^-?G3ox-Kb58sys9LG39muMYCEy!n=TgtJc4rzFxB zxTXfE{fo68XEQ(PA=EInR-HkaK+?;EcnSZJcLUNjqqep zP#8#fZV+t?wn_rrr_%>Pqar|MhUQxsXq61`u}*^DrWl|kQ}b;|aUz8}XtWv$7osDl z2y9QaDMpa8HRlyK(ITBb=X6G=y(#4Do1^vI>k*K);IT@jshH3NZ*|hhpb}Md)@U+D zF~MJTx`)#nT{K^$oZ};1H7w?INvGEo8u)xS&G)s1s9(ND$JHhq3I`{EeTE&O$x4hOZU#y1zH0#)lG|yCWu_9N<-T|-3Cxc3b#vCl(L&$Mh=NBh z|Lilg`D)|Hk`nK$k}C1raJ}bU$?>$%9Zz4F;%T5Go?f!V(-e#u^$#^ijwY>QvEXUR z55$%6t?6OK-JW;^4#CGB{@G_(X^ko|+HQ{b1)T9T6bp{^{K9;U#i%OcI>u7kgYI<< zX8v}fg!oX&KU%^VQNkEe!WdD)7*WC)RbjqBd(6F#$Y2(1$FHsHlu$bba1&zG8hAF2 zMq$F3uS4KZaX!1Uip6|w)_9tp@={#S=Ny&MKMnInXiH-wXiH;6TN)$U(iqW}#)!5w zMzpUnO7*`Im-jvUHYE&6!vH@Ga3bP#Drqp1V7uk#H2MiY?+!g)PmwmOOpgp$!Twh=n2X7eD}$f*I`iK@96&g;HjA-A~% z>c(h2yCMw*&sdHAsFcASutujkM-!C7Fnqm`kCCfmJ32*4sg1(WkN2k^38wCe+T93X_a8##f z@Y^&RPU*DSts;G;(~DT>82Cp-$Ce}&@v2Vq)Jhl&zv@(r53FOsfG_B@Yfs>pavUV+ zw8^9*xpZ>k0*{A)P9cYi6n~CQ(5arEY7-!yG|c8pHEq31-nqW63r`k5z!G7%_$JvkZz{Z-VK!#3Vvr6-wHw3*4a&_NLY@YcpctNKh*f`~Gc$MX5bnoon0~{`NgU<|4Vk4k8Xpov~rDxIW@l1ZlhMxuspVI<$K)B}l@=*UQc1MQeJ(L}hLAs;UT87FCt>(jyP!iNv5g)+j)K4^{o7 znJgDk1t|Ns?Zv#0mLMM=X9OUN2>bb{{WyC8KId!DlWSjmZWUk>mcpPf_fCp%xD}UF zfb=jqw01Rafe8I1{9z8Z@;N=um;Eii9Y6A-m+|zAcuLKp74&-xgNc0XK|962QE~r& zZ~Ff&ck`mDI0k=nlJ3Aql^b;(FHzH4deX<2kP!YP8p4Ok-e807*goyW{deFWeAh-b zo%VKw z2Gq{5liMSC0=Wyg_CxIfgL!)<>MhU&t)XEsPgsC&dbL8o)X~t)(=@~9!O5^fcoDxh zwZi(q3}_J61ZKlJVQ;vGTNa@@(w3k_v%eNzitX>MkD+}y-ur0OdZ>*EO4@7=n=s83 zOA~AoMq6qyaa-~>*aPqTccJF`cJV&DVGk=(Kg4AFLsZZZBaQ8Eyn>eFp*GkAXYtP2 zfN5$_U$z=qvruD|vE62G!}O#(abhZJjk5zg01=w~0T^n@VN_99)`TGjvpoU@vH~5+ z2ZUqx3G4`8%p-6VQxPe9FxPv|4^e9(PMTSpdJLgoDw#@B&#(*F^wa0t6AyW< z!e#g-=nx9Ty@5);PA=gBT(kSp-V$2HHt`buTr>3cA@49D7e}pNxx7yXl%&oQGGMVZ zOYoyc<_g_;*WO%<;E?yv!Z1FdRygQ9g9Tl)?+|+P0lhKVENoMVk=TE)&`)d#e~1xM zLc8#aUM$TO28&cmh3-8PC=@I8_EX_Xu?jBwSBvv8%M0RtnB~ogxQx3W(L1eiUl7;8 zuZdylY2C6UR3bgEw@(BXO84=3WaHI~XS*mRkdC&bACf>`hfI|NU zNfE2ebloftR>F2D4$l%IJ<@he@V-Pd{*Z0Qc^pNHA?c{JNm%SXg(@Y~;DSGm zFNRI*hv4^8Gp4_adQSO8nkUjVbdpUC2!@l)VKy0R#hdYaF;6TqtEdkKNEtq+oMc5I zn_-@C&ge$HJ(yxpBBE%aiXKnxXi&uc?p)Nps0T2?t%f700}T!0N~P2=PkhCmjDgmM zNDbj+3@5uQF=Y`h_(}FaXqKT_%r{n|eGp!Tt*lYG&u{@F)Eh3drKT0AIi|-^Z#S(o zw6d9a<+ZYi6x;=u*-Fz!!vS%rx)~i8?9GM-q0q9^&;)f!yRrR5_

    oF#)=Hbwc& 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 K;O9tAvi}E5>yOp| delta 8601 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 Date: Tue, 30 May 2017 07:34:26 +1000 Subject: [PATCH 180/839] WindowExplorer: reset default settings --- plugins/WindowExplorer/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index c31834ae619d..293fddfb2e29 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -223,7 +223,7 @@ LOGICAL DllMain( PPH_PLUGIN_INFORMATION info; PH_SETTING_CREATE settings[] = { - { IntegerSettingType, SETTING_NAME_SHOW_DESKTOP_WINDOWS, L"1" }, + { IntegerSettingType, SETTING_NAME_SHOW_DESKTOP_WINDOWS, L"0" }, { 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" } From ec0e81656ec7c11939c03bca3aca54df2a3adedd Mon Sep 17 00:00:00 2001 From: Steven G Date: Tue, 30 May 2017 07:40:33 +1000 Subject: [PATCH 181/839] Add module filter options, Add modules tab search support (#143) * Add modules tab search support * Update search keywords * Update options text --- ProcessHacker/ProcessHacker.rc | 30 +-- ProcessHacker/include/modlist.h | 32 +++- ProcessHacker/include/procprpp.h | 4 + ProcessHacker/modlist.c | 57 +++++- ProcessHacker/prpghndl.c | 3 + ProcessHacker/prpgmod.c | 306 ++++++++++++++++++++++++++++++- ProcessHacker/resource.h | 5 +- ProcessHacker/settings.c | 1 + 8 files changed, 416 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 4f52ab152a7d..3df2a8103555 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -560,7 +560,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 @@ -1130,10 +1132,10 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + PUSHBUTTON "Strings...",IDC_STRINGS,155,2,50,14 + PUSHBUTTON "Refresh",IDC_REFRESH,208,2,50,14 + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE + CONTROL "Hide free regions",IDC_HIDEFREEREGIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,5,71,10 END IDD_CHOOSE DIALOGEX 0, 0, 199, 73 @@ -1940,10 +1942,10 @@ BEGIN IDD_PROCMODULES, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 END IDD_PROCTHREADS, DIALOG @@ -1958,7 +1960,7 @@ BEGIN BEGIN LEFTMARGIN, 2 RIGHTMARGIN, 258 - TOPMARGIN, 3 + TOPMARGIN, 2 BOTTOMMARGIN, 258 END @@ -2176,10 +2178,10 @@ BEGIN IDD_PROCMEMORY, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 END IDD_CHOOSE, DIALOG diff --git a/ProcessHacker/include/modlist.h b/ProcessHacker/include/modlist.h index d665a6564a06..099cf56fd4e3 100644 --- a/ProcessHacker/include/modlist.h +++ b/ProcessHacker/include/modlist.h @@ -55,18 +55,36 @@ typedef struct _PH_MODULE_NODE } 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 + 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 Spare : 27; + }; + }; + PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; - - BOOLEAN EnableStateHighlighting; PPH_POINTER_LIST NodeStateList; HFONT BoldFont; @@ -90,6 +108,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 +134,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/procprpp.h b/ProcessHacker/include/procprpp.h index 3d5538b7d97b..e0976375888c 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -224,6 +224,7 @@ typedef struct _PH_MODULES_CONTEXT PH_CALLBACK_REGISTRATION UpdatedEventRegistration; HWND WindowHandle; + HWND SearchboxHandle; // end_phapppub union @@ -238,6 +239,9 @@ typedef struct _PH_MODULES_CONTEXT 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 diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 93704252428f..69ed34407c4d 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -121,6 +121,8 @@ VOID PhInitializeModuleList( TreeNew_SetSort(hwnd, 0, NoSortOrder); PhCmInitializeManager(&Context->Cm, hwnd, PHMOTLC_MAXIMUM, PhpModuleTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList); } VOID PhDeleteModuleList( @@ -187,6 +189,28 @@ VOID PhSaveSettingsModuleList( 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; + } +} + PPH_MODULE_NODE PhAddModuleNode( _Inout_ PPH_MODULE_LIST_CONTEXT Context, _In_ PPH_MODULE_ITEM ModuleItem, @@ -221,6 +245,9 @@ PPH_MODULE_NODE PhAddModuleNode( 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); @@ -324,6 +351,30 @@ VOID PhUpdateModuleNode( 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 ) @@ -745,7 +796,11 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( { PWSTR string = L""; - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE) + 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) { diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index 36d1d027dd3c..e17c8b313b58 100644 --- a/ProcessHacker/prpghndl.c +++ b/ProcessHacker/prpghndl.c @@ -693,6 +693,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/prpgmod.c b/ProcessHacker/prpgmod.c index d7e49874ca36..2dfe11953520 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -37,6 +37,7 @@ #include #include #include +#include static PH_STRINGREF EmptyModulesText = PH_STRINGREF_INIT(L"There are no modules to display."); @@ -178,6 +179,216 @@ 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 (PhIsNullOrEmptyString(Context->SearchboxText)) + return TRUE; + + //PVOID BaseAddress; + //ULONG Size; + //ULONG Flags; + //ULONG Type; + //ULONG ImageTimeDateStamp; + //USHORT ImageCharacteristics; + //USHORT ImageDllCharacteristics; + //LARGE_INTEGER LoadTime; + //LARGE_INTEGER FileLastWriteTime; + //LARGE_INTEGER FileEndOfFile; + + // 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; + } + + 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, @@ -248,6 +459,9 @@ 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); @@ -256,6 +470,8 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( 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); @@ -271,6 +487,8 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhLoadSettingsModuleList(&modulesContext->ListContext); + modulesContext->ListContext.Flags = PhGetIntegerSetting(L"ModuleListFlags"); + PhSetEnabledProvider(&modulesContext->ProviderRegistration, TRUE); PhBoostProvider(&modulesContext->ProviderRegistration, NULL); @@ -325,10 +543,9 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( { 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); + dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_SEARCH), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, modulesContext->ListContext.TreeNewHandle, dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); @@ -338,6 +555,34 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( 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 = PhGetWindowText(modulesContext->SearchboxHandle); + + if (!PhEqualString(modulesContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhMoveReference(&modulesContext->SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(modulesContext->SearchboxText)) + { + // Expand any hidden nodes to make search results visible. + PhExpandAllModuleNodes(&modulesContext->ListContext, TRUE); + } + + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + } + } + break; + } + switch (LOWORD(wParam)) { case ID_SHOWCONTEXTMENU: @@ -415,6 +660,56 @@ 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 selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_FILTEROPTIONS), &rect); + + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, dynamicItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_DYNAMIC_OPTION, L"Hide dynamic", NULL, NULL), -1); + PhInsertEMenuItem(menu, mappedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MAPPED_OPTION, L"Hide mapped", NULL, NULL), -1); + PhInsertEMenuItem(menu, staticItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_STATIC_OPTION, L"Hide static", NULL, NULL), -1); + PhInsertEMenuItem(menu, verifiedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_SIGNED_OPTION, L"Hide verified", NULL, NULL), -1); + + 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; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) + { + PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); + PhSetIntegerSetting(L"ModuleListFlags", modulesContext->ListContext.Flags); + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + } + + PhDestroyEMenu(menu); + } + break; } } break; @@ -497,6 +792,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/resource.h b/ProcessHacker/resource.h index d5f721adb148..aa9abe355ad4 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -533,6 +533,8 @@ #define IDC_EDIT 1383 #define IDC_NEW 1384 #define IDC_HANDLESEARCH 1385 +#define IDC_SEARCH 1387 +#define IDC_FILTEROPTIONS 1389 #define ID_MAINWND_PROCESSTL 2001 #define ID_MAINWND_SERVICETL 2002 #define ID_MAINWND_NETWORKTL 2003 @@ -744,8 +746,9 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 228 +#define _APS_NEXT_RESOURCE_VALUE 231 #define _APS_NEXT_COMMAND_VALUE 40295 -#define _APS_NEXT_CONTROL_VALUE 1387 +#define _APS_NEXT_CONTROL_VALUE 1390 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 91077674310b..705ffe6b8ac7 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -103,6 +103,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerPairSetting(L"MiniInfoWindowPosition", L"200,200"); PhpAddIntegerSetting(L"MiniInfoWindowRefreshAutomatically", L"1"); PhpAddScalableIntegerPairSetting(L"MiniInfoWindowSize", L"@96|10,200"); + PhpAddIntegerSetting(L"ModuleListFlags", L"0"); PhpAddStringSetting(L"ModuleTreeListColumns", L""); PhpAddStringSetting(L"ModuleTreeListSort", L"0,0"); // 0, NoSortOrder PhpAddStringSetting(L"NetworkTreeListColumns", L""); From 45e30c61a0fd4f62c92c899dcc1c53f1c4d7fa47 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 3 Jun 2017 01:05:22 +1000 Subject: [PATCH 182/839] Fix legacy config path --- phlib/kph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/kph.c b/phlib/kph.c index 8311273e632e..07008b4c39b6 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -256,7 +256,7 @@ NTSTATUS KphSetParameters( _In_ PKPH_PARAMETERS Parameters ) { - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\kph2\\Parameters"); + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker2\\Parameters"); NTSTATUS status; HANDLE parametersKeyHandle = NULL; ULONG disposition; From 110d15bff897435f76b239bbc79da2c76137430b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 3 Jun 2017 13:22:56 +1000 Subject: [PATCH 183/839] Add 'combine memory lists' option to memory list window --- ProcessHacker/ProcessHacker.rc | 2 +- ProcessHacker/memlists.c | 34 ++++++++++++ ProcessHacker/resource.h | 4 +- phlib/include/phutil.h | 10 ++++ phlib/util.c | 94 ++++++++++++++++++++++------------ 5 files changed, 109 insertions(+), 35 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 3df2a8103555..a05d04a0ca30 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -460,6 +460,7 @@ IDR_EMPTYMEMLISTS MENU BEGIN POPUP "Empty" BEGIN + MENUITEM "Combine memory lists", ID_EMPTY_COMBINEMEMORYLISTS MENUITEM "Empty working sets", ID_EMPTY_EMPTYWORKINGSETS MENUITEM "Empty modified page list", ID_EMPTY_EMPTYMODIFIEDPAGELIST MENUITEM "Empty standby list", ID_EMPTY_EMPTYSTANDBYLIST @@ -2536,7 +2537,6 @@ IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" - #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 6f73a3f3d918..537e318f8c55 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -232,6 +232,40 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( case ID_EMPTY_EMPTYPRIORITY0STANDBYLIST: command = MemoryPurgeLowPriorityStandbyList; break; + case ID_EMPTY_COMBINEMEMORYLISTS: + { + NTSTATUS status; + HANDLE tokenHandle; + MEMORY_COMBINE_INFORMATION_EX combineInfo = { 0 }; + + if (NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) + { + PhSetTokenPrivilege(tokenHandle, SE_PROF_SINGLE_PROCESS_NAME, NULL, SE_PRIVILEGE_ENABLED); + NtClose(tokenHandle); + } + + 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; } } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index aa9abe355ad4..07b320db4642 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -738,6 +738,7 @@ #define ID_ENVIRONMENT_COPY 40291 #define ID_ENVIRONMENT_DELETE 40292 #define IDC_MAXSCREEN 40293 +#define ID_EMPTY_COMBINEMEMORYLISTS 40295 #define IDDYNAMIC 50000 #define IDPLUGINS 55000 @@ -745,9 +746,8 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 228 #define _APS_NEXT_RESOURCE_VALUE 231 -#define _APS_NEXT_COMMAND_VALUE 40295 +#define _APS_NEXT_COMMAND_VALUE 40297 #define _APS_NEXT_CONTROL_VALUE 1390 #define _APS_NEXT_SYMED_VALUE 169 #endif diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 0ee65e7c0fd3..7eff8ea6b88d 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -236,6 +236,16 @@ PhShowMessage_V( #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 +NTAPI +PhShowInformation2( + _In_ HWND hWnd, + _In_opt_ PWSTR Title, + _In_ PWSTR Format, + ... + ); + PHLIBAPI INT NTAPI diff --git a/phlib/util.c b/phlib/util.c index 117339b56462..f20f04a60f26 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -406,48 +406,78 @@ INT PhShowMessage_V( return result; } +INT PhShowInformation2( + _In_ HWND hWnd, + _In_opt_ PWSTR Title, + _In_ PWSTR Format, + ... + ) +{ + va_list argptr; + INT button; + PPH_STRING message; + TASKDIALOGCONFIG config = { sizeof(config) }; + + va_start(argptr, Format); + message = PhFormatString_V(Format, argptr); + va_end(argptr); + + 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_INFORMATION_ICON; + config.pszMainInstruction = Title; + config.pszContent = message->Buffer; + + if (TaskDialogIndirect( + &config, + &button, + NULL, + NULL + ) == S_OK) + { + PhDereferenceObject(message); + return button; + } + else + { + PhDereferenceObject(message); + return FALSE; + } +} + INT PhShowError2( _In_ HWND hWnd, _In_opt_ PWSTR Title, _In_opt_ PWSTR Message ) { - if (TaskDialogIndirect_Import()) + INT button; + TASKDIALOGCONFIG config = { sizeof(config) }; + + 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( + &config, + &button, + NULL, + NULL + ) == S_OK) { - INT button; - TASKDIALOGCONFIG config = { sizeof(config) }; - - 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; - } + return button; } else { - return PhShowMessage( - hWnd, - MB_OK | MB_ICONERROR, - Title, - Message - ) == IDOK; + return FALSE; } } From c24b0ed369aed541af48267fae4b2c8e89ed5655 Mon Sep 17 00:00:00 2001 From: Steven G Date: Sat, 3 Jun 2017 20:16:19 +1000 Subject: [PATCH 184/839] Add memory tab search support (#144) --- ProcessHacker/ProcessHacker.rc | 6 +- ProcessHacker/include/memlist.h | 28 ++++- ProcessHacker/include/procprpp.h | 5 + ProcessHacker/memlist.c | 154 ++++++++++++++---------- ProcessHacker/prpghndl.c | 3 +- ProcessHacker/prpgmem.c | 199 +++++++++++++++++++++++++++---- ProcessHacker/prpgmod.c | 1 + ProcessHacker/settings.c | 1 + 8 files changed, 309 insertions(+), 88 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index a05d04a0ca30..8a95c77ff329 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1133,10 +1133,10 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "Memory" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - PUSHBUTTON "Strings...",IDC_STRINGS,155,2,50,14 - PUSHBUTTON "Refresh",IDC_REFRESH,208,2,50,14 + 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 - CONTROL "Hide free regions",IDC_HIDEFREEREGIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,5,71,10 + 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 diff --git a/ProcessHacker/include/memlist.h b/ProcessHacker/include/memlist.h index c5f3dcdf2dda..ed3712834558 100644 --- a/ProcessHacker/include/memlist.h +++ b/ProcessHacker/include/memlist.h @@ -53,14 +53,29 @@ 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 + 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 Spare : 30; + }; + }; PPH_LIST AllocationBaseNodeList; // Allocation base nodes (list should always be sorted by base address) PPH_LIST RegionNodeList; // Memory region nodes @@ -86,7 +101,7 @@ VOID PhSaveSettingsMemoryList( VOID PhSetOptionsMemoryList( _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ BOOLEAN HideFreeRegions + _In_ ULONG Options ); VOID PhReplaceMemoryList( @@ -99,6 +114,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/procprpp.h b/ProcessHacker/include/procprpp.h index e0976375888c..500573f100f7 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -288,6 +288,7 @@ typedef struct _PH_MEMORY_CONTEXT { HANDLE ProcessId; HWND WindowHandle; + HWND SearchboxHandle; // end_phapppub union @@ -303,6 +304,10 @@ typedef struct _PH_MEMORY_CONTEXT BOOLEAN MemoryItemListValid; NTSTATUS LastRunStatus; PPH_STRING ErrorMessage; + + PPH_STRING SearchboxText; + PPH_TN_FILTER_ENTRY AllocationFilterEntry; + PPH_TN_FILTER_ENTRY FilterEntry; // begin_phapppub } PH_MEMORY_CONTEXT, *PPH_MEMORY_CONTEXT; // end_phapppub diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index 90f4ec268941..cd69766e2e9b 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -29,6 +29,7 @@ #include #include #include +#include VOID PhpClearMemoryList( _Inout_ PPH_MEMORY_LIST_CONTEXT Context @@ -96,6 +97,9 @@ VOID PhInitializeMemoryList( TreeNew_SetSort(hwnd, 0, NoSortOrder); PhCmInitializeManager(&Context->Cm, hwnd, PHMMTLC_MAXIMUM, PhpMemoryTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->AllocationTreeFilterSupport, hwnd, Context->AllocationBaseNodeList); + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->RegionNodeList); } VOID PhpClearMemoryList( @@ -117,6 +121,9 @@ VOID PhDeleteMemoryList( _In_ PPH_MEMORY_LIST_CONTEXT Context ) { + PhDeleteTreeNewFilterSupport(&Context->AllocationTreeFilterSupport); + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + PhCmDeleteManager(&Context->Cm); PhpClearMemoryList(Context); @@ -128,12 +135,17 @@ 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); } @@ -146,57 +158,28 @@ VOID PhSaveSettingsMemoryList( 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_ BOOLEAN HideFreeRegions + _In_ ULONG Options ) { - ULONG i; - ULONG k; - BOOLEAN modified; - - if (Context->HideFreeRegions != HideFreeRegions) + switch (Options) { - 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); - } + case PH_MEMORY_FLAGS_FREE_OPTION: + Context->HideFreeRegions = !Context->HideFreeRegions; + break; + case PH_MEMORY_FLAGS_RESERVED_OPTION: + Context->HideReservedRegions = !Context->HideReservedRegions; + break; } } @@ -250,6 +233,9 @@ PPH_MEMORY_NODE PhpAddAllocationBaseNode( PhAddItemList(Context->AllocationBaseNodeList, memoryNode); + if (Context->AllocationTreeFilterSupport.FilterList) + memoryNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->AllocationTreeFilterSupport, &memoryNode->Node); + PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); return memoryNode; @@ -276,6 +262,9 @@ PPH_MEMORY_NODE PhpAddRegionNode( PhAddItemList(Context->RegionNodeList, memoryNode); + if (Context->TreeFilterSupport.FilterList) + memoryNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &memoryNode->Node); + PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); return memoryNode; @@ -349,13 +338,12 @@ VOID PhReplaceMemoryList( 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->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); @@ -385,7 +373,41 @@ VOID PhUpdateMemoryNode( TreeNew_InvalidateNode(Context->TreeNewHandle, &MemoryNode->Node); } -PPH_STRING PhpGetMemoryRegionUseText( +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 ) { @@ -438,7 +460,7 @@ VOID PhpUpdateMemoryNodeUseText( ) { if (!MemoryNode->UseText) - MemoryNode->UseText = PhpGetMemoryRegionUseText(MemoryNode->MemoryItem); + MemoryNode->UseText = PhGetMemoryRegionUseText(MemoryNode->MemoryItem); } PPH_STRING PhpFormatSizeIfNonZero( @@ -751,22 +773,34 @@ BOOLEAN NTAPI PhpMemoryTreeNewCallback( 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)) + 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 ( + // 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->RegionType == CfgBitmapRegion || memoryItem->RegionType == CfgBitmap32Region) + // getNodeColor->BackColor = PhCsColorProtectedHandles; + //else if (memoryItem->Type & MEM_PRIVATE) + // getNodeColor->BackColor = PhCsColorOwnProcesses; + //else if (memoryItem->Type & MEM_MAPPED) // getNodeColor->BackColor = PhCsColorSystemProcesses; + //else if (memoryItem->Type & SEC_IMAGE) + // getNodeColor->BackColor = PhCsColorImmersiveProcesses; - //getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; } return TRUE; case TreeNewSortChanged: diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index e17c8b313b58..855c069953cb 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 dmex * * This file is part of Process Hacker. * @@ -468,7 +469,7 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( PPH_LAYOUT_ITEM dialogItem; dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_HANDLESEARCH), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, handlesContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index 1449dcebe0ed..0e70f9caadab 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -196,6 +196,98 @@ 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.HideReservedRegions && + (memoryItem->Type & MEM_PRIVATE || memoryItem->Type & MEM_MAPPED) && + memoryItem->State & MEM_RESERVE && + memoryItem->AllocationBaseItem // Ignore root nodes + ) + { + return FALSE; + } + + if (PhIsNullOrEmptyString(memoryContext->SearchboxText)) + 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; +} + INT_PTR CALLBACK PhpProcessMemoryDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -231,6 +323,9 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( memset(memoryContext, 0, sizeof(PH_MEMORY_CONTEXT)); 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); @@ -238,6 +333,9 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( 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,9 +350,6 @@ 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); } @@ -290,14 +385,9 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( { 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); + dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, memoryContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, memoryContext->ListContext.TreeNewHandle, dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); @@ -307,6 +397,32 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( break; case WM_COMMAND: { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != memoryContext->SearchboxHandle) + break; + + newSearchboxText = PhGetWindowText(memoryContext->SearchboxHandle); + + if (!PhEqualString(memoryContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhMoveReference(&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 (LOWORD(wParam)) { case ID_SHOWCONTEXTMENU: @@ -537,21 +653,60 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( PhDereferenceObject(text); } break; - case IDC_HIDEFREEREGIONS: + case IDC_REFRESH: + PhpRefreshProcessMemoryList(hwndDlg, propPageContext); + break; + case IDC_FILTEROPTIONS: { - BOOLEAN hide; + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM freeItem; + PPH_EMENU_ITEM reservedItem; + PPH_EMENU_ITEM selectedItem; - hide = Button_GetCheck(GetDlgItem(hwndDlg, IDC_HIDEFREEREGIONS)) == BST_CHECKED; - PhSetIntegerSetting(L"HideFreeRegions", hide); - PhSetOptionsMemoryList(&memoryContext->ListContext, hide); + GetWindowRect(GetDlgItem(hwndDlg, IDC_FILTEROPTIONS), &rect); + + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, freeItem = PhCreateEMenuItem(0, PH_MEMORY_FLAGS_FREE_OPTION, L"Hide free", NULL, NULL), -1); + PhInsertEMenuItem(menu, reservedItem = PhCreateEMenuItem(0, PH_MEMORY_FLAGS_RESERVED_OPTION, L"Hide reserved", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_STRINGS, L"Strings...", NULL, NULL), -1); + + if (memoryContext->ListContext.HideFreeRegions) + freeItem->Flags |= PH_EMENU_CHECKED; + + if (memoryContext->ListContext.HideReservedRegions) + reservedItem->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 == IDC_STRINGS) + { + PhShowMemoryStringDialog(hwndDlg, processItem); + } + else + { + PhSetOptionsMemoryList(&memoryContext->ListContext, selectedItem->Id); + PhSaveSettingsMemoryList(&memoryContext->ListContext); + + PhApplyTreeNewFilters(&memoryContext->ListContext.AllocationTreeFilterSupport); + PhApplyTreeNewFilters(&memoryContext->ListContext.TreeFilterSupport); + } + } + + PhDestroyEMenu(menu); } break; - case IDC_STRINGS: - PhShowMemoryStringDialog(hwndDlg, processItem); - break; - case IDC_REFRESH: - PhpRefreshProcessMemoryList(hwndDlg, propPageContext); - break; } } break; diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 2dfe11953520..718ed4023619 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. * diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 705ffe6b8ac7..547591b0f0fd 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -93,6 +93,7 @@ VOID PhAddDefaultSettings( 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"); From bcca76e5e1e8393acd0703a4f482b8868c7926ff Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 3 Jun 2017 20:24:36 +1000 Subject: [PATCH 185/839] Fix search memory leak, Fix module tab settings, Fix memory window opening behind properties window --- ProcessHacker/hndllist.c | 2 ++ ProcessHacker/memrslt.c | 1 + ProcessHacker/modlist.c | 10 ++++++++++ ProcessHacker/prpgmod.c | 13 +++++-------- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index 6777cefe7159..d3a09bd54bbe 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -121,6 +121,8 @@ VOID PhDeleteHandleList( { ULONG i; + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + PhCmDeleteManager(&Context->Cm); for (i = 0; i < Context->NodeList->Count; i++) diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index 28c177c91ede..3ea48045760c 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -81,6 +81,7 @@ VOID PhShowMemoryResultsDialog( (LPARAM)context ); ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); } static PPH_STRING PhpGetStringForSelectedResults( diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 69ed34407c4d..15df0c622cac 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -131,6 +131,8 @@ VOID PhDeleteModuleList( { ULONG i; + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + if (Context->BoldFont) DeleteObject(Context->BoldFont); @@ -165,12 +167,17 @@ VOID PhLoadSettingsModuleList( _Inout_ PPH_MODULE_LIST_CONTEXT Context ) { + ULONG flags; PPH_STRING settings; PPH_STRING sortSettings; + flags = PhGetIntegerSetting(L"ModuleListFlags"); 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); } @@ -183,8 +190,11 @@ VOID PhSaveSettingsModuleList( PPH_STRING sortSettings; settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"ModuleListFlags", Context->Flags); PhSetStringSetting2(L"ModuleTreeListColumns", &settings->sr); PhSetStringSetting2(L"ModuleTreeListSort", &sortSettings->sr); + PhDereferenceObject(settings); PhDereferenceObject(sortSettings); } diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 718ed4023619..2b2e217827c4 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -488,8 +488,6 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhLoadSettingsModuleList(&modulesContext->ListContext); - modulesContext->ListContext.Flags = PhGetIntegerSetting(L"ModuleListFlags"); - PhSetEnabledProvider(&modulesContext->ProviderRegistration, TRUE); PhBoostProvider(&modulesContext->ProviderRegistration, NULL); @@ -572,11 +570,8 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( // Cache the current search text for our callback. PhMoveReference(&modulesContext->SearchboxText, newSearchboxText); - if (!PhIsNullOrEmptyString(modulesContext->SearchboxText)) - { - // Expand any hidden nodes to make search results visible. - PhExpandAllModuleNodes(&modulesContext->ListContext, TRUE); - } + // Expand any hidden nodes to make search results visible. + PhExpandAllModuleNodes(&modulesContext->ListContext, TRUE); PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); } @@ -704,7 +699,9 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( if (selectedItem && selectedItem->Id) { PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); - PhSetIntegerSetting(L"ModuleListFlags", modulesContext->ListContext.Flags); + + PhSaveSettingsModuleList(&modulesContext->ListContext); + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); } From 7feef2167de90ce906f71670a2cc8bb5a0ca1f40 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 3 Jun 2017 21:34:53 +1000 Subject: [PATCH 186/839] Update memory tab options --- ProcessHacker/ProcessHacker.rc | 1 - ProcessHacker/include/memlist.h | 10 +- ProcessHacker/memlist.c | 44 +++++++-- ProcessHacker/prpgmem.c | 166 +++++++++++++++++++------------- ProcessHacker/resource.h | 2 - 5 files changed, 144 insertions(+), 79 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 8a95c77ff329..b372627fd3a0 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -440,7 +440,6 @@ BEGIN 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 diff --git a/ProcessHacker/include/memlist.h b/ProcessHacker/include/memlist.h index ed3712834558..482f485a62cd 100644 --- a/ProcessHacker/include/memlist.h +++ b/ProcessHacker/include/memlist.h @@ -55,6 +55,10 @@ typedef struct _PH_MEMORY_NODE #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 typedef struct _PH_MEMORY_LIST_CONTEXT { @@ -73,7 +77,11 @@ typedef struct _PH_MEMORY_LIST_CONTEXT { ULONG HideFreeRegions : 1; ULONG HideReservedRegions : 1; - ULONG Spare : 30; + ULONG HighlightPrivatePages : 1; + ULONG HighlightSystemPages : 1; + ULONG HighlightCfgPages : 1; + ULONG HighlightExecutePages : 1; + ULONG Spare : 26; }; }; diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index cd69766e2e9b..078103a54ede 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -180,6 +180,18 @@ VOID PhSetOptionsMemoryList( 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; } } @@ -780,7 +792,29 @@ BOOLEAN NTAPI PhpMemoryTreeNewCallback( memoryItem = node->MemoryItem; if (!memoryItem) - ; // Dummy + 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 & MEM_MAPPED) + { + 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 || @@ -791,16 +825,10 @@ BOOLEAN NTAPI PhpMemoryTreeNewCallback( //{ // getNodeColor->BackColor = PhCsColorElevatedProcesses; //} - //else if (memoryItem->RegionType == CfgBitmapRegion || memoryItem->RegionType == CfgBitmap32Region) - // getNodeColor->BackColor = PhCsColorProtectedHandles; - //else if (memoryItem->Type & MEM_PRIVATE) - // getNodeColor->BackColor = PhCsColorOwnProcesses; - //else if (memoryItem->Type & MEM_MAPPED) - // getNodeColor->BackColor = PhCsColorSystemProcesses; //else if (memoryItem->Type & SEC_IMAGE) // getNodeColor->BackColor = PhCsColorImmersiveProcesses; - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + getNodeColor->Flags = TN_AUTO_FORECOLOR; } return TRUE; case TreeNewSortChanged: diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index 0e70f9caadab..b6fed9159545 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -129,8 +129,6 @@ VOID PhpInitializeMemoryMenu( if (numberOfAllocationBase == 0 || numberOfAllocationBase == NumberOfMemoryNodes) PhEnableEMenuItem(Menu, ID_MEMORY_SAVE, TRUE); } - - PhEnableEMenuItem(Menu, ID_MEMORY_READWRITEADDRESS, TRUE); } VOID PhShowMemoryContextMenu( @@ -590,60 +588,6 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( } } break; - case ID_MEMORY_READWRITEADDRESS: - { - PPH_STRING selectedChoice = NULL; - - if (!memoryContext->MemoryItemListValid) - break; - - while (PhaChoiceDialog( - hwndDlg, - L"Read/Write Address", - L"Enter an address:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - L"MemoryReadWriteAddressChoices" - )) - { - 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; @@ -662,22 +606,52 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( PPH_EMENU menu; PPH_EMENU_ITEM freeItem; PPH_EMENU_ITEM reservedItem; + 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); menu = PhCreateEMenu(); - PhInsertEMenuItem(menu, freeItem = PhCreateEMenuItem(0, PH_MEMORY_FLAGS_FREE_OPTION, L"Hide free", NULL, NULL), -1); - PhInsertEMenuItem(menu, reservedItem = PhCreateEMenuItem(0, PH_MEMORY_FLAGS_RESERVED_OPTION, L"Hide reserved", NULL, NULL), -1); + typedef enum _PH_MEMORY_FILTER_MENU_ITEM + { + 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_READ_ADDRESS, + PH_MEMORY_FILTER_MENU_STRINGS, + } PH_MEMORY_FILTER_MENU_ITEM; + + PhInsertEMenuItem(menu, freeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_FREE, L"Hide free pages", NULL, NULL), -1); + PhInsertEMenuItem(menu, reservedItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_RESERVED, L"Hide reserved pages", NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_STRINGS, L"Strings...", NULL, NULL), -1); - - if (memoryContext->ListContext.HideFreeRegions) - freeItem->Flags |= PH_EMENU_CHECKED; + PhInsertEMenuItem(menu, privateItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_PRIVATE, L"Highlight private pages", NULL, NULL), -1); + PhInsertEMenuItem(menu, systemItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_SYSTEM, L"Highlight system pages", NULL, NULL), -1); + PhInsertEMenuItem(menu, cfgItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_CFG, L"Highlight CFG pages", NULL, NULL), -1); + PhInsertEMenuItem(menu, typeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_EXECUTE, L"Highlight executable pages", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_READ_ADDRESS, L"Read/Write &address...", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_STRINGS, L"Strings...", NULL, NULL), -1); + if (memoryContext->ListContext.HideFreeRegions) + freeItem->Flags |= PH_EMENU_CHECKED; if (memoryContext->ListContext.HideReservedRegions) reservedItem->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, @@ -690,11 +664,12 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( if (selectedItem && selectedItem->Id) { - if (selectedItem->Id == IDC_STRINGS) - { - PhShowMemoryStringDialog(hwndDlg, processItem); - } - else + if (selectedItem->Id == PH_MEMORY_FILTER_MENU_HIDE_FREE || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIDE_RESERVED || + 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) { PhSetOptionsMemoryList(&memoryContext->ListContext, selectedItem->Id); PhSaveSettingsMemoryList(&memoryContext->ListContext); @@ -702,6 +677,63 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( 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 (!memoryContext->MemoryItemListValid) + break; + + while (PhaChoiceDialog( + hwndDlg, + L"Read/Write Address", + L"Enter an address:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + L"MemoryReadWriteAddressChoices" + )) + { + 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."); + } + } + } + } } PhDestroyEMenu(menu); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 07b320db4642..0b906b03259b 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -405,7 +405,6 @@ #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 @@ -681,7 +680,6 @@ #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 From 466d598f48b229463b8ee7fa31e0e775db8e73e0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 04:24:59 +1000 Subject: [PATCH 187/839] Update token properties splitter control --- ProcessHacker/include/splitter.h | 48 +--- ProcessHacker/splitter.c | 440 +++++++++++++++++-------------- ProcessHacker/tokprp.c | 9 - 3 files changed, 256 insertions(+), 241 deletions(-) diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h index c1d6fcb4a3dd..9be37f0023cd 100644 --- a/ProcessHacker/include/splitter.h +++ b/ProcessHacker/include/splitter.h @@ -9,25 +9,23 @@ typedef struct _PH_HSPLITTER_CONTEXT struct { ULONG Hot : 1; - ULONG Pushed : 1; - ULONG Moved : 1; - ULONG DragMode : 1; - ULONG Spare : 28; + ULONG HasFocus : 1; + ULONG Spare : 30; }; }; LONG SplitterOffset; - LONG SplitterPosition; - PH_LAYOUT_MANAGER LayoutManager; - PPH_LAYOUT_ITEM Topitem; - PPH_LAYOUT_ITEM Bottomitem; + HWND Window; + HWND ParentWindow; + HWND TopWindow; + HWND BottomWindow; } PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( - _In_ HWND Parent, - _In_ HWND TopChild, - _In_ HWND BottomChild + _In_ HWND ParentWindow, + _In_ HWND TopWindow, + _In_ HWND BottomWindow ); VOID PhDeleteHSplitter( @@ -40,32 +38,4 @@ VOID PhHSplitterHandleWmSize( _In_ INT Height ); -VOID PhHSplitterHandleLButtonDown( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleLButtonUp( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleMouseMove( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhHSplitterHandleMouseLeave( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - #endif diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index f07d3a0d45f5..930ebb1d450a 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -27,244 +27,298 @@ #include #define SPLITTER_PADDING 6 -#define SPLITTER_MIN_HEIGHT 200 -VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height); - -PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( - _In_ HWND Parent, - _In_ HWND TopChild, - _In_ HWND BottomChild - ) +LONG GetWindowWidth(HWND hwnd) { - PPH_HSPLITTER_CONTEXT context; - - context = PhAllocate(sizeof(PH_HSPLITTER_CONTEXT)); - memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); - - PhInitializeLayoutManager(&context->LayoutManager, Parent); - - context->SplitterOffset = -4; - context->SplitterPosition = 250;// PhGetIntegerSetting(L"TokenSplitterPosition"); - context->Topitem = PhAddLayoutItem(&context->LayoutManager, TopChild, NULL, PH_ANCHOR_ALL); - context->Bottomitem = PhAddLayoutItem(&context->LayoutManager, BottomChild, NULL, PH_ANCHOR_ALL); + RECT Rect; - return context; + GetWindowRect(hwnd, &Rect); + return (Rect.right - Rect.left); } -VOID PhDeleteHSplitter( - _Inout_ PPH_HSPLITTER_CONTEXT Context - ) +LONG GetWindowHeight(HWND hwnd) { - PhSetIntegerSetting(L"TokenSplitterPosition", Context->SplitterPosition); - - PhDeleteLayoutManager(&Context->LayoutManager); -} + RECT Rect; -VOID PhHSplitterHandleWmSize( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ INT Width, - _In_ INT Height - ) -{ - // HACK: Use the PH layout manager as the 'splitter' control by abusing layout margins. - - // BUG: If the window is maximized and you move the splitter to the bottom, restoring the window causes - // the bottom control to get moved outside the visible area... Just move the splitter back up. - if ((Context->Bottomitem->Rect.bottom - Context->Bottomitem->Rect.top) <= 100) - Context->SplitterPosition = Context->Topitem->Rect.bottom - Context->Topitem->Rect.top - SPLITTER_PADDING; - - // Set the bottom margin of the top control. - Context->Topitem->Margin.bottom = Height - Context->SplitterPosition - SPLITTER_PADDING; - // Set the top margin of the bottom control. - Context->Bottomitem->Margin.top = Context->SplitterPosition + SPLITTER_PADDING * 2; - - PhLayoutManagerLayout(&Context->LayoutManager); + GetWindowRect(hwnd, &Rect); + return (Rect.bottom - Rect.top); } -VOID PhHSplitterHandleLButtonDown( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) +LONG GetClientWindowWidth(HWND hwnd) { - POINT pt; - HDC hdc; - RECT rect; - - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); + RECT Rect; - GetWindowRect(hwnd, &rect); - ClientToScreen(hwnd, &pt); - - pt.x -= rect.left; - pt.y -= rect.top; - - // Adjust the coordinates (start from 0,0). - OffsetRect(&rect, -rect.left, -rect.top); - - if (pt.y < Context->Topitem->OrigRect.top * 2) - pt.y = Context->Topitem->OrigRect.top * 2; - if (pt.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) - pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; - - Context->DragMode = TRUE; - SetCapture(hwnd); - - hdc = GetWindowDC(hwnd); - DrawXorBar(hdc, 1, pt.y - 2, rect.right - 2, 4); - ReleaseDC(hwnd, hdc); - - Context->SplitterOffset = pt.y; + GetClientRect(hwnd, &Rect); + return (Rect.right - Rect.left); } -VOID PhHSplitterHandleLButtonUp( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) +LONG GetClientWindowHeight(HWND hwnd) { - HDC hdc; - RECT rect; - POINT pt; - - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - - if (!Context->DragMode) - return; - - GetWindowRect(hwnd, &rect); - ClientToScreen(hwnd, &pt); - - pt.x -= rect.left; - pt.y -= rect.top; - - // Adjust the coordinates (start from 0,0). - OffsetRect(&rect, -rect.left, -rect.top); + RECT Rect; - if (pt.y < Context->Topitem->OrigRect.top * 2) - pt.y = Context->Topitem->OrigRect.top * 2; - if (pt.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) - pt.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; - - hdc = GetWindowDC(hwnd); - DrawXorBar(hdc, 1, Context->SplitterOffset - 2, rect.right - 2, 4); - ReleaseDC(hwnd, hdc); - - Context->SplitterOffset = pt.y; - - Context->DragMode = FALSE; - - GetWindowRect(hwnd, &rect); - pt.x += rect.left; - pt.y += rect.top; - ScreenToClient(hwnd, &pt); - GetClientRect(hwnd, &rect); - - Context->SplitterPosition = pt.y; - - // position the child controls - PhHSplitterHandleWmSize(Context, rect.right, rect.bottom); - - ReleaseCapture(); + GetClientRect(hwnd, &Rect); + return (Rect.bottom - Rect.top); } -VOID PhHSplitterHandleMouseMove( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) +LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - HDC hdc; - RECT windowRect; - POINT windowPoint; + PPH_HSPLITTER_CONTEXT context = NULL; - windowPoint.x = GET_X_LPARAM(lParam); - windowPoint.y = GET_Y_LPARAM(lParam); + if (uMsg == WM_CREATE) + { + LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam; + context = cs->lpCreateParams; + SetProp(hwnd, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPH_HSPLITTER_CONTEXT)GetProp(hwnd, PhMakeContextAtom()); + } - GetWindowRect(hwnd, &windowRect); - ClientToScreen(hwnd, &windowPoint); + if (!context) + return DefWindowProc(hwnd, uMsg, wParam, lParam); - if (Context->DragMode) + switch (uMsg) { - windowPoint.x -= windowRect.left; - windowPoint.y -= windowRect.top; + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + RECT clientRect; + HDC hdc; - OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + if (hdc = BeginPaint(hwnd, &paintStruct)) + { + GetClientRect(hwnd, &clientRect); - if (windowPoint.y < Context->Topitem->OrigRect.top * 2) - windowPoint.y = Context->Topitem->OrigRect.top * 2; - if (windowPoint.y > Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT) - windowPoint.y = Context->Bottomitem->Rect.bottom - SPLITTER_MIN_HEIGHT; + if (context->HasFocus) + FillRect(hdc, &clientRect, CreateSolidBrush(RGB(0x0, 0x0, 0x0))); + else if (context->Hot) + FillRect(hdc, &clientRect, CreateSolidBrush(RGB(0x44, 0x44, 0x44))); + else + FillRect(hdc, &clientRect, GetSysColorBrush(COLOR_WINDOW)); - if (windowPoint.y != Context->SplitterOffset) + EndPaint(hwnd, &paintStruct); + } + } + return 0; + case WM_ERASEBKGND: + return 1; + case WM_LBUTTONDOWN: { - hdc = GetWindowDC(hwnd); + context->HasFocus = TRUE; - if (wParam & MK_LBUTTON) + SetCapture(hwnd); + + InvalidateRect(hwnd, NULL, TRUE); + } + break; + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + { + if (GetCapture() == hwnd) { - DrawXorBar(hdc, 1, Context->SplitterOffset - 2, windowRect.right - 2, 4); - DrawXorBar(hdc, 1, windowPoint.y - 2, windowRect.right - 2, 4); + ReleaseCapture(); + + context->HasFocus = FALSE; + + InvalidateRect(hwnd, NULL, TRUE); } - else + } + break; + case WM_MOUSELEAVE: + { + context->Hot = FALSE; + InvalidateRect(hwnd, NULL, TRUE); + } + 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_MOUSEMOVE: + { + if (!context->Hot) { - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) }; + context->Hot = TRUE; + InvalidateRect(hwnd, NULL, TRUE); trackMouseEvent.dwFlags = TME_LEAVE; trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; - - SetCursor(LoadCursor(NULL, IDC_SIZENS)); - TrackMouseEvent(&trackMouseEvent); } - ReleaseDC(hwnd, hdc); - Context->SplitterOffset = windowPoint.y; + if (!context->HasFocus) + break; + + int Width = GetClientWindowWidth(context->ParentWindow); + int NewPos; + HDWP deferHandle; + POINT cursorPos; + + GetCursorPos(&cursorPos); + ScreenToClient(context->ParentWindow, &cursorPos); + NewPos = cursorPos.y; + + if (NewPos < 200) + break; + if (NewPos > GetClientWindowHeight(context->ParentWindow) - 80) + break; + context->SplitterOffset = NewPos; + + deferHandle = BeginDeferWindowPos(3); + DeferWindowPos( + deferHandle, + context->TopWindow, + NULL, + SPLITTER_PADDING, + 90, + Width - SPLITTER_PADDING * 2, + cursorPos.y - 90, + SWP_NOZORDER | SWP_NOACTIVATE + ); + DeferWindowPos( + deferHandle, + context->Window, + NULL, + 0, + cursorPos.y, + Width, + SPLITTER_PADDING, + SWP_NOZORDER | SWP_NOACTIVATE + ); + DeferWindowPos( + deferHandle, + context->BottomWindow, + NULL, + SPLITTER_PADDING, + cursorPos.y + SPLITTER_PADDING, + Width - SPLITTER_PADDING * 2, + GetClientWindowHeight(context->ParentWindow) - (cursorPos.y + SPLITTER_PADDING) - 65, + SWP_NOZORDER | SWP_NOACTIVATE + ); + + EndDeferWindowPos(deferHandle); } + break; } -} -VOID PhHSplitterHandleMouseLeave( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ HWND hwnd, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - // Reset the original cursor. - SetCursor(LoadCursor(NULL, IDC_ARROW)); + return DefWindowProc(hwnd, uMsg, wParam, lParam); } -// http://www.catch22.net/tuts/splitter-windows -VOID DrawXorBar(HDC hdc, INT x1, INT y1, INT width, INT height) +PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( + _In_ HWND ParentWindow, + _In_ HWND TopWindow, + _In_ HWND BottomWindow + ) { - static WORD _dotPatternBmp[8] = - { - 0x00aa, 0x0055, 0x00aa, 0x0055, - 0x00aa, 0x0055, 0x00aa, 0x0055 - }; - - HBITMAP hbm; - HBRUSH hbr, hbrushOld; + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_HSPLITTER_CONTEXT context; - hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp); - hbr = CreatePatternBrush(hbm); + context = PhAllocate(sizeof(PH_HSPLITTER_CONTEXT)); + memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); + + context->ParentWindow = ParentWindow; + context->TopWindow = TopWindow; + context->BottomWindow = BottomWindow; + context->SplitterOffset = PhGetIntegerSetting(L"TokenSplitterPosition"); - SetBrushOrgEx(hdc, x1, y1, 0); - hbrushOld = (HBRUSH)SelectObject(hdc, hbr); + if (PhBeginInitOnce(&initOnce)) + { + WNDCLASSEX c = { sizeof(c) }; + + c.style = CS_GLOBALCLASS; + c.lpfnWndProc = HSplitterWindowProc; + c.cbClsExtra = 0; + c.cbWndExtra = sizeof(PVOID); + c.hInstance = PhLibImageBase; + c.hIcon = NULL; + c.hCursor = LoadCursor(NULL, IDC_SIZENS); + c.hbrBackground = NULL; + c.lpszMenuName = NULL; + c.lpszClassName = L"PhHSplitter"; + c.hIconSm = NULL; + + RegisterClassEx(&c); + + PhEndInitOnce(&initOnce); + } - PatBlt(hdc, x1, y1, width, height, PATINVERT); + context->Window = CreateWindowEx( + WS_EX_CONTROLPARENT | WS_EX_TRANSPARENT, + L"PhHSplitter", + NULL, + WS_CHILD | WS_VISIBLE, + 5, + 5, + 465, + 10, + ParentWindow, + NULL, + PhLibImageBase, + context + ); + + ShowWindow(context->Window, SW_SHOW); + UpdateWindow(context->Window); - SelectObject(hdc, hbrushOld); + return context; +} - DeleteObject(hbr); - DeleteObject(hbm); +VOID PhDeleteHSplitter( + _Inout_ PPH_HSPLITTER_CONTEXT Context + ) +{ + PhSetIntegerSetting(L"TokenSplitterPosition", Context->SplitterOffset); } +VOID PhHSplitterHandleWmSize( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ INT Width, + _In_ INT Height + ) +{ + HDWP deferHandle; + + deferHandle = BeginDeferWindowPos(3); + DeferWindowPos( + deferHandle, + Context->TopWindow, + NULL, + SPLITTER_PADDING, + 90, + Width - SPLITTER_PADDING * 2, + Context->SplitterOffset - 90 - 65, + SWP_NOZORDER | SWP_NOACTIVATE + ); + + DeferWindowPos( + deferHandle, + Context->Window, + NULL, + 0, + Context->SplitterOffset - 65, + Width, + SPLITTER_PADDING, + SWP_NOZORDER | SWP_NOACTIVATE + ); + + DeferWindowPos( + deferHandle, + Context->BottomWindow, + NULL, + SPLITTER_PADDING, + Context->SplitterOffset + SPLITTER_PADDING - 65, + Width - SPLITTER_PADDING * 2, + GetClientWindowHeight(Context->ParentWindow) - (Context->SplitterOffset + SPLITTER_PADDING), + SWP_NOZORDER | SWP_NOACTIVATE + ); + EndDeferWindowPos(deferHandle); +} \ No newline at end of file diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 88ec93705383..6a0f419656db 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -934,15 +934,6 @@ INT_PTR CALLBACK PhpTokenPageProc( case WM_SIZE: PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); break; - case WM_LBUTTONDOWN: - PhHSplitterHandleLButtonDown(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - break; - case WM_LBUTTONUP: - PhHSplitterHandleLButtonUp(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - break; - case WM_MOUSEMOVE: - PhHSplitterHandleMouseMove(tokenPageContext->HSplitterContext, hwndDlg, wParam, lParam); - break; } REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->GroupsListViewHandle, uMsg, wParam, lParam); From 2261ebc37147ffdfe2e0dae95daf72278b24dd92 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 19:52:40 +1000 Subject: [PATCH 188/839] Fix typo --- plugins/ExtraPlugins/setup/updater.c | 4 ++-- plugins/OnlineChecks/page1.c | 4 ++-- plugins/OnlineChecks/upload.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 0cc2c26e9d32..19bd41c7b45c 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -60,8 +60,8 @@ VOID TaskDialogCreateIcons( ) { // Load the Process Hacker window icon - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); // Set the TaskDialog window icons SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); 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/upload.c b/plugins/OnlineChecks/upload.c index 926bd6dc73ec..0fb00a62503b 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -205,8 +205,8 @@ VOID TaskDialogCreateIcons( _In_ PUPLOAD_CONTEXT Context ) { - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); From fd455f696e22fa7daf05dad58709fc2904d3de34 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 19:56:08 +1000 Subject: [PATCH 189/839] Add TaskDialog message macros --- ProcessHacker/ProcessHacker.def | 1 + ProcessHacker/plugin.c | 8 +++-- phlib/include/phutil.h | 17 +++++------ phlib/util.c | 54 ++++++++------------------------- 4 files changed, 26 insertions(+), 54 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 97692aee76bf..0258a7b3b934 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -377,6 +377,7 @@ EXPORTS PhShellProperties PhShowConfirmMessage PhShowContinueStatus + PhShowMessage2 PhShowFileDialog PhShowMessage PhShowMessage_V diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 8f30f4e9da33..167a919d1454 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -270,7 +270,6 @@ VOID PhLoadPlugins( 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++) { @@ -283,9 +282,12 @@ VOID PhLoadPlugins( PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); - if (PhShowMessage( + if (PhShowMessage2( NULL, - MB_ICONERROR | MB_YESNO, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_ERROR_ICON, + L"Unable to load the following plugin(s)", + L"%s", sb.String->Buffer ) == IDYES) { diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 7eff8ea6b88d..9b6a440e9bbc 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -237,23 +237,20 @@ PhShowMessage_V( #define PhShowInformation(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONINFORMATION, Format, __VA_ARGS__) PHLIBAPI -INT +INT NTAPI -PhShowInformation2( +PhShowMessage2( _In_ HWND hWnd, + _In_ ULONG Buttons, + _In_opt_ PWSTR Icon, _In_opt_ PWSTR Title, _In_ PWSTR Format, ... ); -PHLIBAPI -INT -NTAPI -PhShowError2( - _In_ HWND hWnd, - _In_opt_ PWSTR Title, - _In_opt_ PWSTR Message - ); +#define PhShowError2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, Format, __VA_ARGS__) +#define PhShowWarning2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_WARNING_ICON, Format, __VA_ARGS__) +#define PhShowInformation2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_INFORMATION_ICON, Format, __VA_ARGS__) PHLIBAPI PPH_STRING diff --git a/phlib/util.c b/phlib/util.c index f20f04a60f26..ff2386b4a3b0 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -406,15 +406,17 @@ INT PhShowMessage_V( return result; } -INT PhShowInformation2( +INT PhShowMessage2( _In_ HWND hWnd, + _In_ ULONG Buttons, + _In_opt_ PWSTR Icon, _In_opt_ PWSTR Title, _In_ PWSTR Format, ... ) { + INT result; va_list argptr; - INT button; PPH_STRING message; TASKDIALOGCONFIG config = { sizeof(config) }; @@ -422,62 +424,32 @@ INT PhShowInformation2( message = PhFormatString_V(Format, argptr); va_end(argptr); + if (!message) + return -1; + 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.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0) | TDF_SIZE_TO_CONTENT; + config.dwCommonButtons = Buttons; config.pszWindowTitle = PhApplicationName; - config.pszMainIcon = TD_INFORMATION_ICON; + config.pszMainIcon = Icon; config.pszMainInstruction = Title; config.pszContent = message->Buffer; if (TaskDialogIndirect( &config, - &button, + &result, NULL, NULL ) == S_OK) { PhDereferenceObject(message); - return button; + return result; } else { PhDereferenceObject(message); - return FALSE; - } -} - -INT PhShowError2( - _In_ HWND hWnd, - _In_opt_ PWSTR Title, - _In_opt_ PWSTR Message - ) -{ - INT button; - TASKDIALOGCONFIG config = { sizeof(config) }; - - 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( - &config, - &button, - NULL, - NULL - ) == S_OK) - { - return button; - } - else - { - return FALSE; + return -1; } } From 60abdf1ae8807b95a3434ba5e4bc8cb6003628d5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 23:31:34 +1000 Subject: [PATCH 190/839] Fix system memory highlighting, Fix memory error dialog, Remove legacy node filtering --- ProcessHacker/include/mainwnd.h | 1 + ProcessHacker/include/phapp.h | 1 + ProcessHacker/mainwnd.c | 1 + ProcessHacker/memedit.c | 14 +++++++++----- ProcessHacker/memlist.c | 23 ++++++++++++----------- ProcessHacker/prpgmem.c | 29 +++++++++++++---------------- phlib/util.c | 2 +- 7 files changed, 38 insertions(+), 33 deletions(-) diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index d7607624acd1..bd6259c0c169 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -105,6 +105,7 @@ extern BOOLEAN PhMainWndExiting; typedef struct _PH_SHOW_MEMORY_EDITOR { + HWND OwnerWindow; HANDLE ProcessId; PVOID BaseAddress; SIZE_T RegionSize; diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 5a9914f8a514..c213449420d2 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -439,6 +439,7 @@ VOID PhShowLogDialog( #define PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION 0x1 VOID PhShowMemoryEditorDialog( + _In_ HWND OwnerWindow, _In_ HANDLE ProcessId, _In_ PVOID BaseAddress, _In_ SIZE_T RegionSize, diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 26533a980e4c..4ae19d682a3e 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1973,6 +1973,7 @@ ULONG_PTR PhMwpOnUserMessage( PPH_SHOW_MEMORY_EDITOR showMemoryEditor = (PPH_SHOW_MEMORY_EDITOR)LParam; PhShowMemoryEditorDialog( + showMemoryEditor->OwnerWindow, showMemoryEditor->ProcessId, showMemoryEditor->BaseAddress, showMemoryEditor->RegionSize, diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index 1dfbef18a22c..8d30e574cfcc 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -47,8 +47,10 @@ typedef struct _MEMORY_EDITOR_CONTEXT }; HANDLE ProcessHandle; HWND WindowHandle; - PH_LAYOUT_MANAGER LayoutManager; + HWND OwnerHandle; HWND HexEditHandle; + PH_LAYOUT_MANAGER LayoutManager; + PUCHAR Buffer; ULONG SelectOffset; PPH_STRING Title; @@ -74,6 +76,7 @@ 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, @@ -98,6 +101,7 @@ VOID PhShowMemoryEditorDialog( 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; @@ -209,7 +213,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( if (context->RegionSize > 1024 * 1024 * 1024) // 1 GB { - PhShowError(NULL, L"Unable to edit the memory region because it is too large."); + PhShowError(context->OwnerHandle, L"Unable to edit the memory region because it is too large."); return TRUE; } @@ -219,7 +223,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( context->ProcessId ))) { - PhShowStatus(NULL, L"Unable to open the process", status, 0); + PhShowStatus(context->OwnerHandle, L"Unable to open the process", status, 0); return TRUE; } @@ -227,7 +231,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( if (!context->Buffer) { - PhShowError(NULL, L"Unable to allocate memory for the buffer."); + PhShowError(context->OwnerHandle, L"Unable to allocate memory for the buffer."); return TRUE; } @@ -239,7 +243,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( NULL ))) { - PhShowStatus(NULL, L"Unable to read memory", status, 0); + PhShowStatus(context->OwnerHandle, L"Unable to read memory", status, 0); return TRUE; } diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index 078103a54ede..c41417218ba2 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -327,9 +327,6 @@ VOID PhReplaceMemoryList( 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)) @@ -358,9 +355,6 @@ VOID PhReplaceMemoryList( 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 { @@ -793,21 +787,28 @@ BOOLEAN NTAPI PhpMemoryTreeNewCallback( if (!memoryItem) NOTHING; - else if (context->HighlightExecutePages && ( + else if ( + context->HighlightExecutePages && ( memoryItem->Protect & PAGE_EXECUTE || memoryItem->Protect & PAGE_EXECUTE_READ || memoryItem->Protect & PAGE_EXECUTE_READWRITE || - memoryItem->Protect & PAGE_EXECUTE_WRITECOPY)) + memoryItem->Protect & PAGE_EXECUTE_WRITECOPY + )) { getNodeColor->BackColor = PhCsColorPacked; } - else if (context->HighlightCfgPages && ( + else if ( + context->HighlightCfgPages && ( memoryItem->RegionType == CfgBitmapRegion || - memoryItem->RegionType == CfgBitmap32Region)) + memoryItem->RegionType == CfgBitmap32Region + )) { getNodeColor->BackColor = PhCsColorElevatedProcesses; } - else if (context->HighlightSystemPages && memoryItem->Type & MEM_MAPPED) + else if ( + context->HighlightSystemPages && ( + memoryItem->Type & SEC_IMAGE + )) { getNodeColor->BackColor = PhCsColorSystemProcesses; } diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index b6fed9159545..0dec99fbd5df 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -434,22 +434,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; diff --git a/phlib/util.c b/phlib/util.c index ff2386b4a3b0..7e84cbb10dad 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -429,7 +429,7 @@ INT PhShowMessage2( config.hwndParent = hWnd; config.hInstance = PhLibImageBase; - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0) | TDF_SIZE_TO_CONTENT; + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); config.dwCommonButtons = Buttons; config.pszWindowTitle = PhApplicationName; config.pszMainIcon = Icon; From 276e51ddf490a7067b7c4e98fb3afbc334293d3a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 23:33:05 +1000 Subject: [PATCH 191/839] Fix build file caching --- tools/CustomBuildTool/Source Files/Build.cs | 84 +++++++----------- tools/CustomBuildTool/Source Files/Utils.cs | 29 +++++- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160256 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 4 files changed, 59 insertions(+), 54 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 48b75f1d9eec..b6cff3c57df3 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -540,74 +540,54 @@ public static bool CopyKProcessHacker(BuildFlags Flags) if (Flags.HasFlag(BuildFlags.BuildDebug)) { - if (!File.Exists("bin\\Debug32\\ProcessHacker.exe")) + if (File.Exists("bin\\Debug32\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Debug32\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); - return true; - } + File.WriteAllText("bin\\Debug32\\ProcessHacker.sig", string.Empty); - if (!File.Exists("bin\\Debug64\\ProcessHacker.exe")) - { - Program.PrintColorMessage("[SKIPPED] Debug64\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); - return true; + 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); + return false; + } } - if (File.Exists("bin\\Debug32\\ProcessHacker.sig")) - File.Delete("bin\\Debug32\\ProcessHacker.sig"); - if (File.Exists("bin\\Debug64\\ProcessHacker.sig")) - File.Delete("bin\\Debug64\\ProcessHacker.sig"); - - File.Create("bin\\Debug32\\ProcessHacker.sig").Dispose(); - File.Create("bin\\Debug64\\ProcessHacker.sig").Dispose(); - - string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"); - if (!string.IsNullOrEmpty(output)) + if (File.Exists("bin\\Debug64\\ProcessHacker.exe")) { - Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow, true, Flags); - return false; - } + File.WriteAllText("bin\\Debug64\\ProcessHacker.sig", string.Empty); - 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); - return false; + 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); + return false; + } } } else { - if (!File.Exists("bin\\Release32\\ProcessHacker.exe")) + if (File.Exists("bin\\Release32\\ProcessHacker.exe")) { - Program.PrintColorMessage("[SKIPPED] Release32\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); - return true; - } + File.WriteAllText("bin\\Release32\\ProcessHacker.sig", string.Empty); - if (!File.Exists("bin\\Release64\\ProcessHacker.exe")) - { - Program.PrintColorMessage("[SKIPPED] Release64\\ProcessHacker.exe not found.", ConsoleColor.Yellow, true, Flags); - return true; + 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); + return false; + } } - if (File.Exists("bin\\Release32\\ProcessHacker.sig")) - File.Delete("bin\\Release32\\ProcessHacker.sig"); - if (File.Exists("bin\\Release64\\ProcessHacker.sig")) - File.Delete("bin\\Release64\\ProcessHacker.sig"); - - File.Create("bin\\Release32\\ProcessHacker.sig").Dispose(); - File.Create("bin\\Release64\\ProcessHacker.sig").Dispose(); - - string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"); - if (!string.IsNullOrEmpty(output)) + if (File.Exists("bin\\Release64\\ProcessHacker.exe")) { - Program.PrintColorMessage("[ERROR] (Release32) " + output, ConsoleColor.Red, true, Flags); - return false; - } + File.WriteAllText("bin\\Release64\\ProcessHacker.sig", string.Empty); - output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"); - if (!string.IsNullOrEmpty(output)) - { - Program.PrintColorMessage("[ERROR] (Release64) " + output, ConsoleColor.Red, true, Flags); - return false; + 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); + return false; + } } } diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 3f3c9a5008a2..55a870ce71e8 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -78,8 +78,33 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) if (!File.Exists(CurrentFile)) return; - if (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) - File.Copy(CurrentFile, NewFile, true); + 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 const int SW_HIDE = 0; diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index fb97a2ee1ecc1d7046c6727053448910267db157..604822da66d7cf6ba84cfa11fd1f7a9e9515ee69 100644 GIT binary patch delta 16925 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 delta 16942 zcmbtc33yc1**}vvqUD-DQWr+)j4xE660+~cyiVA{P zQ4tZu1>7p)Qa~%Xps0wbRS`r|(Mqc*?qJ3LJ?Gw;OlbS}dHz4`yyttr?d<2CxkKA@ z+|u-gW%VE0eST|VGBN&+k#@u;bR^_+C~g+GHfUP&@Px|OWPyxiJSLD2SLhaQPQz}I z*dec$1QOCt9ZzVJh4$9iXDkAr&$Szc3gHXw8KJ#EwWC72(tjc$`x}CfNKE9*X@uN% z4x5T_ZL%D0aaUlfFq+dZ<06#>%?f-qj&r1*sK+yA88Y!c^*+8}%XR0|^M zpDkowyoeBMrIWtnBlDsTzZkz5`TXUyqrcTQ7jm}k^|UQvrCjcb98}3SBB13m*K&I= z*pt08PQ6IV;Ei5FWZIuaUV?h9N<6`u)x(FTkoFVv%WkB?Y{)j zLH5>O9D_$XS&DVBf3Vef4dk?u*^Z}e=nR*6XNn7q2xz&?wcOs?MQ+GYz0LJ3koC}} z@*eg<7yThJPu06(SK>RY~rMnp|jTp2dLb<&@ ztguoqhpgj+d|@w)-P|-hDYoMSr=aTySDYvDC(n6$5@Ovx1H?IR%B45EpG1 zMI~Y0b0#*HLZ9pgu+m}N1d&Uv78{+DvCN5TD;&|(ols!Fje5)VVM$;ew$+>Ya6xa5 zwecskHx%?4tXehRzy@F(;|l^90|f4q#1{>UsflMVI7 z5V>2tFY{W$D0l6}`6E)giLG_dWJAj`Qx>epu@D~2DerGzr&X(Qc|&)=lo`!N7X~kV zzPwSS&3EMqCT)}38#5KB`^I{~{b)bv^giw=)^@qBD*6G6OfFhhk~VjMq>o=cAHPLz zZ{OL_WqkE4TQhFi z&xwE@*{WTDv21c=+%w7AxVLJ#doJ7?YS9*}Lcbo1E+zYelq$5M6yw2?#j99z2LRHh zJ}{+a?#7aw>y{oQ?Q_qR)IvnG4j(&{)6=$%S<>4S=OWu+Y1`?xwsX5{JXm_Q&&rTK z;I4yG_umC6$eYg(ye6DHvcG(Sz9xQauYHs^IPq;9m0p6;GnNW1Gq|l`9keAWJO^d! zDu;HUcD^@QQ^!VQ1v61Ew8@*ecm>2pmASU7fGZ!emFHNxbBp$nx4SS%JLZkshcQJM z`RK3;Ezj33wEzm9ML)XJM~5#y7)}j%LR&p1mnk`vg**XYmz*31*=4er!}!lDIQilcNBYQF@$(8mb;p>9yJY+S9&V`)DYR91VC>xM^?u`lk>n zl=#k>f%{7Qx+b%x?kmaHHJNR2pEa;DJJ^?KGe(uDD3hku#z#rF@q`YUwi>%8w0r#t zaa=-k7UyX#{rsj7QrFS-5cIk402g{cD1F8->RX)6dg;-S z$4uWzXv_0UJ20ODqsdgMB=cDF%$QX4jdtcd(5P@8q&+LO5A$aZ?GLpWAH%9(9=yh= z%_s3^9-RPH)S6}&F=q4YwnjG+MbfohUa5@=2C83%M}>*LkAl^CoR@({JR8x5AYEC- z^aYI;&O`M1Ds6wD=YMI#D+v03v|&H5^M7r_x?sQQv;*&({>-Hxg7KAYjV9=@j_D1# z0g^uUmtyRrow7`?w=u=m;zp2V{jsCgqad6ruEr$x1@Jq8O{Ie%^BK|=`U1QZN?wZ^ z?~Zwiv8!yuSV(p=UB!a=F=wU+Ep-EqDUi%VX|_B8DbTS0O>Y?Ud{zO8f~=rB%G+&lw(LaTWU==4@Pz?@mZ ztbbU+E)!cybKr7cNdV3iQhWj`N=^i@WyKxi6->p4#wGhFd1RMtSv9-g)Zc#lO&`|P z=cSBsH7EJ0!3MW64@)sqOUFJSqyCg&s`1tc4VvutzX2SYMuR_O;{B!YxV+|52`N!KL0a8v*v#+Xx97>bg7v^ zO)mgUa_a)H(nObY7l1u`1!ygi0&!Ru?QA4yoc;9NdA%GNu$VIQx{C4t)4ZM=*}7Rh zHkU1xJxjK%S^9`|A#0YhZNKZ<#-=ZZylzmo!-93$vKBADq9xKM{jOwOhPgJyBKVcO z?K)qIko2RAjqRDp;#m7^WH&uX^6S;NyaM2E4_ z(lxIA-Yu? zRphPx_(N$I*H5_{zU{ar6@bdk6Fm5m;paC9Zy z^fBNqXG@PCVL2B4twO1Vz5tgb-r!g(1N2uGeCY)jf>{-RatcZ%ITe_k2C!9BtI3NX z*v(1260uTm?{qg3etHRru8&WLQ86cHpiq-DHB)h2dPlOad{_gt8P044-o?uReVUgz z(s@}x|G~=;HT7YBVcLzAO+IufnqFD-I&SmR+j*H!xAHPTcknVu_w%xVwxCS0F*?N+ zVXF3JNh37C%czA`>VlQ_(FGeFtqXQ~nJ(neCA#3CYjh!(KA;OuxE(B=>FAHcNUWVxTybRM>yo}Igyu^^K)CDWOR~Kw_ zyDr%2K3&M6hjqb0zt)9ZD%A2$I;lq&Tr{i;DL3t)OCCB{7xL&dUGUPRF8Jtmy5Ogq zbRnN^(}e)tqYFX$t}YbN6S@$hQh#1+nC9w2gcj*Sl=jetSc(qUr9wJQ7uwM)b)kqh z>q0Trn9!7uAuW%H@mW~D<=B*-jwUi0cQN*-Yeo79JeNh`DAEH=3^CE4o`zpoo@HUC zGnQ+r#7A=mux9#cn3wsq11|%#j+a3?nU@9h3SNfjGG2!1Exe4-&Ai0iJgW;%lU7gEt&TB)m?bbv0n=tNy`(<^nsLz{IWk7~N$r4Q+Xk3O#p zetJL`^696#5THNlLXhUv@mdRLp)Q1I7ef=KLz%QJ!pH1l$jf3}Ts26W7SHclkHNM! z;H!<*CM9vpw%RLPZjrsmv*w73YKg;Jf#Px~DnCE!aEoVscmNb6M{9qK$A`~`Iw6$H zrsNz*rKTkXNY3+Ek%#a+Hc^sQJr8NkL(+K=cSJHJuh80+6@})4SsShd+I)5?c@>15 z?%!bnSdvL?Mp?Vm=Yw!+&VxEpq`FK?a2(1$%9Q#Qbv`Uf3MlQq5mAN*J(G!wwMH>z zRutacSxYOf6qm5|qn4ai@zTTEun-W(BCPNz%wi|9%XC)Rgh6Ros5#1gHNS&H!lvji z7#D#k_?FE|pS{9q)SqP6=StCUwM7!qeAH@5C>9K3eR-Ymd+p)!aMe=SN}6V( z`nZG|mti_&WeLM%BTUH@AgB95^vf;9o3tOwqe8Rht!Q6;4G+Y=k>i+~3VcC8D}Bjp zp>D(k%O#cm(#Gi!&MFVFmS3k`SrHMg)o!lH7jDoVsE7?)0oR^ItXU4Xbk>*4XBfI8 zqPT=D9p$X@2#fqZba4@N#PJ=oB95Ji*T{UnJ!(bRt;R3YaucBxUo9UYmzq7x;u5x9 zsdX^E0xw~km|w{nZbVMISzHo>hP@uavx_Z6kEMGv>#Bt>B9GOayct?Ebs#*sl8bo| zUDhQ5mWq9)=643z@Wm_Jj|ajp$SPRHQroaNZSh-}Xg9m;JRKCPq2P<$cGl<33;|le z`qQ&PA0A6~m(c9ObhFLLHE_epC1_=pgL<%TO1y{oDdARF;wpyB)_~=ulqhixn&fSO zWvuxn>N3{4@-s9vSld!L*)fFm@^+XL?dg!gT5eUjav@h~{i+K5S7)rl{cLqAl|nha zcCa?Ts!q96cYCg?Sh-IZKd-7*p3%i{hk@em!P=0Hk#;h|${TUZlIa(jbW>Z?{Ww_L z+~GXt{b7f~usFnMPmk*{DXm^U=>1xChmLyM;la|hV`6fvS8u~BVx4L<7HR*|Fruw<0bn5%bd<;y7%oh$E4~3btI}{U z3Y!{>VG|`cYR#SU`rt6Jmc;J^CO0#ld<&rTs8o8C#J4j3 zu=Y-;ikTy^=E~6uDUMBQbqbPx$uvv+;HeONR>fD{UP=^8ihpwQQTQ}I2B^ww<&QHi zEB;AVan)@8JjDTA;Z)6{d7YA+s9k(sR84;a4R?v8{s@1&+28O4O7?c5VhIL!UG{5< zdalTNF)@}avc8%2=ZdWFrqx`L_3bpq6}PeOPew(RP770D!phhA;@hApO}~3k@a|y= z+n{;+u?pJ}a{`y)(b|a4owA!A`In~q(pgzjZBOTX+vu$D<88zLwdWIgBU`_cCu+`V zbw|#(WO||Z>d6ex^y=Sqs?&Yv^hjb|dcI57*?igfl24+kCH~}gR*3qt@h6`G<7)+n z^V5(ezL3=9GmuNw*AaocuE@6rWE`z5q#0?gl3JFj0=b$mEv*ME9H z%W}lu10pgSOa28?@_hi9Q)eGAW}GSG(V2G0Bgg0CxU`D#4_TxX?}fr{R+5KM`Wrq1 z=BSqBVU$PX#Gxp zZdQ|@pv0HtV<`9`-ulC>M?RYOFUw|T&#LLgNJ+a6#em|`iY2YTm+7j|T}*cgM#%W3 zJ0a$kt8~;bXfb+L-Orw-DbFh>@v9MFHz&%?$=^eQyY@{_uO}0gb2N-!J7jwEFfG4V zN1;R;+H1DZqHXWhRecVb@zBw}>2;f0%KJG>yQ#O<{x~X|V^fxVhSIJ^V>8WHP0A51 z@z24R^~4fA>L?ZCf79yvd{T(j;A)%(U1f~!MZUK~?8^^ZugNa}4JWj^zNG`4!|{6I zB6ubL4kcEF2Tz)@SQT+rEx#30b~%#t8__@DWL3Q9;b&ys$WHXf`-X)twKw}lgp=Ao z`i4_q!zu5qR34F%->{@8nX24_AKb4*>N2F3`1!!EEjT*L(F)@sJ6xePoMG`;6MaN} zYsJQ%0E$P>DHJ~L-+JM83(0SpvsqH(4nnjW`W4vtrRaNTlRs#W^~+ZVB4={4_C~+5 zK5xbdDQYC7J2sFV(^oWh0iAu-WesQ<890EDrNf~cKbGuXg@1nnUD4q2LkFUK8~kOh zx~6^LdaxKiv+;QxpL%>u`23*Fuki?F+I2OxsS!biOv`1sD#sDbC7%Sb2FQV&U%d)h z;b1t2XD&!OIq_9FRa4?*hbF@$)OL7@*kA(9S zQfgxu^p~V0g$(vFa%~<%cmB4Rl{7~e`xUaEM8j6{NAqI8gFI(j7IKjF0ftFoSx6zO z;E>(qSK^SDlf41f=ocU>EdT- z3jXYjklH|_Pa#JntZH$ol+I!H~<{(ObZ$7CpE zZT`K$_k52*|CBx7;~@9t}O%9`*uXE=poBOksmeXN=fEDT>RWcag%IYoF^|H>)|yR;y(iw(&%JE|09OpO5_-ufcM=|=tDfuI5*4L6B!)j8Y`QI zo4m_HDF-mJ<#qS6@h}X(` zc|t9beE26+3%%{gpzuPkkGxgL#f$^$;KMjpo0;5=%P}4`uaK?j1KNoJbrmI%DW1`I#uSrH z*i7rZQ#|a6Q^MIz!EAPyHhEw-@w0sG)`4SkzYb0Dw2&e)gEZsc?Od*XH?S;sNFhtr zfz0Q+xK>eDAWYW=*Of^R25Ys3y4vAgV&|J(B%qE4E-qwPZ$| z&l8!?7x3&#o{Nc#&0WbRj;lGQfQ6*cG#Yq|X|#55P%q(pEoboH)KUc3Nh#p@9P2r* z;CMI3t2xdUna_BR9k}LuF8`6suW~%dH5M-Sg3OxE^3S#{2Of4aTqiSpfTKIIoF)Cn zz8qsWJMRWy2m8$!p;eJa(v{?hYs@}!H@O|yL^c5L6}A9(3x5J07G8trUkeVg5fwf~ zhjYa5ppTQE&7)#CRmoa%b5yhlM8;vw*hA44StLsh_Ci#(h$K~CtQ+S>T4WP>+h9Kx zI4mYIq(ryfR^SGU;fc!9TnDC*ad=KLwl(aqs6@uIiLo#Ij4d+Qdz>9N*iPhBNe!Nr z%yVbH8|=*#E{=?vLJo_W9KqF*u`{`hEv?eoIHWO?V+Nbf8Gf(kosUuUA7u{Swelm9`WMYfUa>X_~N{DT37*vRTO?B_vvQ9?*F zIapAFz)6FdD`Q<|&ZL7WSwmL~`m?xHuzZ zuj$d)#gc|Mr?r z1{Y^BQjp4GbLDz$=d|ZUZ-&XJaISW6*x(+kgjNT?_qwd$7W0i3T;uuQUeg$)!kxfi zO?FKiJ-q$U8l-I1{>A#P(8^}`-W3Wm5j$N&1zhQjc$N*9R)eu@dxp24$g;I+S+-Wj zvPCixEL$|gKC<6siDj4vL(t0Jk8IHTjR;g0L^fw>@sqvK>T}Yb&88M?(XJcOK9>zw ztIu3**NDNXY(IDCv8;?ca-XymlFlOt{??HFZnvc!S-{y^@>JflmLf86lx~y5&s&Pg zKF-#Xm(2StCFC2!)-QGy?wgB7>z)On_bk}AV;EaYV)>_21Uq%CPG{zy#>3@KgAD}3 zukPb?+t%F6$$3P^osSShILoQY$){&&^ zg^XQqCYaojq_4sLj>#QK1{ti#T8}vPgCp~t>tbwz!M@F7>>`7`!aXlF*k~WKrLHpQ zGd%EWgZ+WCMuYWqGuP`4HUKMqBw1y!dG;bS^iG}OaEgO1ouW798F-E&*Bb0a*$KAN zV617Q$?YkFp3QTDZZKE_DjY)|G}tw6C)ghic7>-N3+7paE%G?Qb{p(&*v66947LTf zapa)Eu7hnnIb^Wau#G2woq}VF4co2$dNP6h-7s$SJHfs)*m|^dBKgH&ThP#nM4qb0 zUFxhSlZf45S2>+vUW46et|ya8*kEhTPOwsgsTki2N!3(-Y+rBox}2cdC;SwHE$8R` z6w;jsYUW$>tE^MVe1rMzoxv6vtjOLItjS;_V_V78HaVw~JGpH+xr`harjnn)(0_e( zPix~kjY!j250{hsNv(An2^j1~GRS%nDKywtVTAQ!k}%lo!X)c-(%E323H8<)ZF0^e zy$xH1FyA_pJk3}NRs1MSw#*{?xlwOSeVf2~a>xj*Cd;sf@rsm03m8tplSm}b(2H&Kf90{Wqf9^aDjN&F*TTX^^5Df*AtsQ)Q4ue8M*t4{omoVRE?yBCW@@!#x^&a44~AJb9rv4AHERP-r$lDhoKD$jN(x86K*~^~NV;i{ zjp-r;wNJ+k7V@=y7Apo@EKt337;&f%$gJUhnp&UnZoXl|s(2MWc%yT}+#T-`vgE*&H&bv6y z5STn47{k|tgMs#B4X}#r5Dunbs1}*I2gh1q7c!K~vw^+HR`Pyq1eq_+DHu%_i(SJL z$TnWhHu7HdVzNS9k7-{lu8v$z)`%-2SCPBKorMitvk0iPYlKGEKU~-h`BVEX(AQz- zvZ&PED9#pM#mOdAGv2lpxBB6>22^~~`y|;zo(t^)76x~5pBKm;p@;JgRQ7y~HK?D; zV!Nr|L9;J*nrtG6vGLcSng-y#c2(FWUT#+)pUgdt>KX1p$DF{+-K8mEA0MK9q{db* zuoiU`wj#w)VUNfX*$I21>=R$hogy6Mo(IXt;pxI5af#f8w1~2Jsc-`6=E7PgFBDFT za#X=B?4sxjp@qDxt`Y=^Rd12r&l^oxT>@)F!5-AL!~KSkT8vh(o}F~Q3su+f8^Au{ zFEhAw{f~UJBU7Q{76Q^BTkn3+4sm#p@LUs*5fyX zr_hggpgH4tN8TjP3w#7TjRmzyT!Rb$R^cDflkzrH|2^=CIe3ZrV30J>1W=e8^Y}&KkrWfcLMjJ zfQQA64EQ18_pv#qZBkstsVwxuGHelU zvMe>7K!)X}Q^M8e6~L(ZR$zbgU8WY{BKNKooK8n}kyApGd828cbhYyl7>?#{Gp!Lj z*mjthIAksgm6H?@dkqL&JSm?-=b@^R-W z;Ng&{FdwthB5jU1loQgQoDszZO|fFZ=1eH5Q_|s3C*`!%xzG%3k<7{(k;PyQS5D2}ql!ra2|l+nm=S{a=}O9geYSn14B-!xM6^SmLJ$D;NFJ6+D5=5nWb!g{qmt^sq@ zDtxisXsse1vK3fBUIP}9gTM-o=aDZU_aY~O1Gr{55p7kt05bg)jAx9aolVgCm9pf<+@({;t;8e$Q@g`_iKyGks1^&UY4|u2J1n^OZ zMLLYpDw1}=r;N)3B$i$739ci@S2H;W(XR6UU7lcXK?(k+``( z#~O~)IW}?J$Z?MNO#rxFH5e4hfNb+@WR5a$F-%+8O59ZUByR zGrYvh@Ix!ZdLP4`E{5wkp3i;8%S^M}eiLvnm-VQ}xpk?BSs&$i8^bDczn`n}7*Z$0 z{bq&%o^);gYRKK}Yk^~9>$O)eIx*t{;Toa0I8dA*t`YAP?-idApBJm8DbfwndTE>V zg7lH(mMi58-@`t8}6t`NW3b-G|@t#r;-X-6vto#cR@IF`&J|HJztUN?=3PmCd zhe@I4nqHC`tNtuuEQrj&_?xOO<}Zn|bz{{qf9cg$_XNH>w2d9BF7%$Oo2vfIU#+!` zeUbU4+EhGNmC|q4FjIs)5hQqdB$Eg}QG8;AwM!=DL5nfkb#pkdPRR1Q7XPxa`x9E~t3m06{cjm?0JI}Crw~xH~ zxUhZ9BDZb(CCd-`@}^B8W65wb6kpaykvcLE|6A~w)^}Z{R&|eAdwGjjYq!#)t-iY* zxZ)-M*1O~SZ0|1GfengQxH1n>v$g0=EYhd_eceQjuB$OJF>Zw%*FtxPwA6Y_-i&|O zjBU%kTJ+{nrYW26srmPg8#YvDIr+8S%M~8TTJqO>uFLSMd(O9)O+h!u;6EeD812{w zFO0HOhE5l5c)E>o`>_pgR&0Of^(RE<9gd5Z%+!VqeRjsK!Hsi*TeL;{YlUiU=l+mT zt9`J4W3H;o4u^Z1s1U*3^qE6zersZQwh8|Tu6e#WCJ7Fd4tq@Wx~~-6^T8HtKfE=> zvO+qC}gbn-pTk~V$C z9iBI{+P-o(@6c|1r!uj}5)%b?^JG`^D>+1VH}7{hop2!gLHr{LM>=K)kNk`F={xb% z$9ioo)|d%-PMPt?oBSD?#hUz?i3qOo4wZJ#-t~-RF=3P0 zp`zg?3?ch(a}I0lHG&-@hzLloYgjlV4wZW~yWGuQq&BKIt1Ju5TJSyUFXQ1Zy*Jme z5_8mCxqb6{*9c0PtT4Ia{c8pM4~n1oAnYjPov*|cqJm28@CW16GLsn-QMtYAho$%l zglwOBB+r~k|Nl2Yfol|wJ_%pNKf`Ozu>;56^X$em1D1U=Q2Sf^Yo9DkEZx5TxLI(1 xBeM;z|5;4}xno;Q%oqPDYj1s4b4RSdcx1Wb^Jg8O3ujh{p)Sw5`inmb{{xOJo=w9*_WEho!nerlS{v`p6j``q_^5%`Da^WOVC=UL7<_ndRjIrqM| zxXh=x%x6QJfMT0rg_6o>zxT`owjkR9+jaARpN~)Th^rmPm?%H(dUyUeN4xZSBPBEb zX31ACR7N&z;~&!UwT}uPkGd%jjsLp){HT3#(eG`%RgtsHJP>k^U&xdHXAb>zblRXd zKALl^|E0pwSErmjQjznL8L&CiUEJnMN96aue&I8myW-AEU`&zE_QU&T$%dF>pSwGZ z`@S+Hwsqr%CmXwsKQpCoY6rhEl0=JAL;bp;Fau;;Y6st*L(WCjNS7WqtG?4SDvEf_LL}EL)q1z`EjSuMnIh8cb{92~B4@)lf=#1Vo zXDzyK(xSfk(NF9D zuQ|?<_S&hu4A~`5W&w7qRzVJRGK|;Uu+E756691zXMC$5h7gBp6S!1wtwtl;#W4f< z5OJiZ1ghjLht{*oF8$NToA1lZ>0!Mtc--9e96WJ+je3Sq(iRTAXs?~t^L}Kd9Gcl( zfl`$GfIO1nAAYncX~N@L4NnNBu5q$FqhnjJKr+%fp$WwtY=xLFNPIE;*GM=HHXc(M+WLOt3V_>Sk_`oUAZ&gpA3G zweP0jvCSAfSd5UhS#Jh@jU*1q0$dI{WMrp=z=I)%(F##6qSqbrSf>tlwHl2`%Wfmb zJ54ZmOWV$2W?$*kIniE4hGAG zE_vZQn-eFL>6Lo|BF4i*+0OWv)PbaG6Sz1v(hj?Xygl+`q*G;kc1oa1rg$Vjhsc@i zJX`BX>E1Qe_Es|)*)=L~7s~IPhEeWk7>HJMZD;=+Q4qIY6Xvx}@n)`2gw0!+DYf`vGy%U3?y4YrsQ|e z3e>XhMiR&+%#Dz}-P>8B!-%RdTJDs8ckf`ohvaJmmj|t(& zv>ym3v@tw^;`}Da`5rCpZD`vKSg3Y_1oRvhI0eaQBx{hYiIk~52L`^2XgHz-+>b@d zUwS6n)#?dEzzC9CJtvsoNp4Po^Z@~`m4D1Nc z!p`s#%!W5%H`suZyTeAXCu|9GY=)~inn)i6ZDBvy72XT)hyCGjct4y12f(TD+Bd%x z7r^19i{MC`v2q=J0P0A882JvEOZsIv2_A-1kspBt@F<*a3p0vO6PbbF9Gne5g>z)> zpcW6^Z8S4RbNqoLivcv3Bf)SO^~w8U_W;Ms9{c$o``?sH@4?+%?$yq;Rx}#QV2m4W zC||<@>Z09T2-D$0I1ny|Bj6G^3NC}=pbJiiY%+>x!WB@5*eX~8OQ81Z8n_;=wHYIe zw-b2`!SA59>Q1Nw^7q2nBuwH^a;DDR>2Lh1cLV88M{5UEe^ z#n{cEVF#8>_{L7C_e(isP+K+7(fl0g4EQ{}7w)G2#>1CLPlOEh;z@8HTm<*SWv~*u z;6a-qJ%)BO{bbotXXn?DYH5dIB76ht{BQ&&!=tb}tb)DZF?ct8TTTs4G4GdKL!GYU z$TW`+U>rOFJHeAsZBM~L@I$D#)JHH6o`&<`8Mq3bh1%xlpw5y1fUm<(;T!NgJOV$1 z+SZ>#ZmovK7ew^lx+LYph6T8ncGGxGHtq~|eT_~l_iv~p<_c^LuR^^*?O46zza^am zZ$ceG|FIcswE2D^y&M`1jN;9}ggWsyfX_f5SO)!|4vf3tLD&$!Y8W=7k!&13$acXl zMMEPxH$kek35MF-AyAvU8PwW@L2d5lP@CHcwFx67V?>G>C%Ge>u2#r2k2t8!9S^m+ z6X8JE8tSr`1XsXhxD%#8YGky5e}iq|m#`iD1-6Gi++S%h2zG!eup`WZ>2RoNbTBfB zOhb?bXTVNyChP)N!fdGPL3g+o_K>zCCkO0n;+|$V%8k2&$Mi<07tsf5xAcSBE%!ie zi2GnB900We2Ex8@2pkKC!KrXK)QcU}z*wm_?pO&Jm2K-8B-`>M9Jwg%Z1=uJv4x6^ zklCZ0t~{iTkxaI`)+z1yzfdV(fZSZNh8*YW~z-Qob zs6$vM>N3)w!0qtw@OSVU+zGG4a%eZ?vC*s9pgG1kUC%WzO1e?=J#Y&=fPqeSm9QMX z3ZI2qz;p05()-|HsB7FC@K5jvJOq!z6R-+$V_N>_Jp3HKgZvAq`7!cb#&HDNlpnxb z@C5u7{tcSc_!JCJ`8-5Ch!Ox+N)QeC@>ObKucnOx6 z_Z!BSL>@z+8Ex?JX{Z_Mh`EaVjHcmP_zl#iy$=5YzlG=F%?7gIfdgiOBt7VKeUC7J ztN0P>0Qw2)jQu~@8{TSQtkGul^Kmz$KYWjL0Q>;jp*CL=_#F&_HVWZ+(7G{$eT+3r zNk@`i1*4!=wk2eo*o;_t`@uo3x0<2Z~@GKOJOEl2eaS~*a=p`Y^YOnSNK=h&4=BKPA=UMu(vXLKqu@8^`__r`@!C@ zKhy>v1pAO40q^lKR_%mCNPhvgu_VNZR~EayAM7LS;UPzw0I-CYskc=6S1;v;NSsRR5kU89#4P)RO$OLB0g%7~_a2i|ynE|W{&w~p|FM&&_ z0K?f>MVbyY7PyGeQJD=4Bth*0v?AeJuHD# zrg#l}53YqDz#Z@e+zJ0yIs4&M)0`mXyUa}{*vyCU0uW}=78wU^GG%?P9Ri@tKEEUtw#>$|H4s%sy?!;`R$ejV7Xjims!)A^TJ^*s(oISDO5e+oOqA`@AEdfNW;h)zJPhg9)^0+3Ip31TBaPw7 zNB0;ion=WAkVY9X)zU&snpj6#WJy~ijW*(|ZA&a^64H1c)xG&XVM&wgNX3$}6xOoc zW=VNgDy~`Ia!bmiWG&m>mXt-bmhC=Enu;`$=V7gbj*C|%V3yOBhLqdi=m?v_bQlFQ zpf)bkyR~&=&tvUjJ3(DjIz!#Mb%FVih1%NwcZJJIb30pW3tOY&QqsMk?yP&mXW`v& zkM>_*BD(!x{jjzl_dwl#u#ObJ4ex{Rz@e2N%o=5yeI$NPti4Zr_u$cNH@UGg{DTp_^ee zd>T@)H3MngwpOm4_l)WPzV|M#y3kziUNG8rF++ANm|^=mLjntD*uKq>BJg8|Tq_*O zo6W7<#g8luQmdjF5%L64EO#tSwk(lcLV`8Q%AiFdruk=yUz`~5XU`=kNX60+$zSZ0 zg-aUC{TyYBN84_-m;Ww~NxJ1RvR>lQFhQ)f+5%p=)gPo|C1**Z!zb-_FKH{r%8jMY z7Nd#!L$|vJ)MT)R211b2jtGXDLTCev|8zU1+`_UoMMHdch;tEB^u8vpzh= zVDFWC$4{K36~#LCdUNrN#5kE$l;Sv4okuH&mL4bNMPWP#{;6mJozTMNjNZuz(9u&4 zBjN9%-i6OXeJb1~V_Yq@Gop$klKz13E`%>aU9GvDikrcgU@Y7V)p?)f7AKnfWi2}n zy#5E=xQd(pH_@B7gdI@Y-E}l62B^z z^S!I~+2$t7PphIL<|ewwk9V}`3$p$c@O7eeC@C_tq@pC2^XZadW`9Xqy~MUZMV?EKs?uh@LVB>K;V^g|PK7t%GI$f}JE-sAa`<2PlwmYAz9*tjpg+QG@PG2J&7)|E zoTr>F9tf?<(Tvu5V?rG~yftMdW5Wma)qx*Oh7Dm`sFk7tjK-w(Dc26~fq}3P2ARex zZU`fkG&g|dcfi9#FakLPx1kY9!?wPy zI@y0-8bUCWQW7{v$~HoTS@Nr zRQtz_q=k$dZLHQ3{kx84dpWgzLcq7Ak8<>os^7h%)qHH{zqQ~6kcfj9gCBOmF;XF#7URR?zRi@vbeH? zd9S=&nQGgaAQviE*nUi4`xPn+4~A&;$%Ct@aI;rjTA`<2c`U$P6O(0@HAT9<`ly*x z`Sz<5OmlK&(rcGZd@7qBYHG8+*iI4-CxFUnh|`sUu-*;pJehfOb*eeiBZDb{?=C{JCW}hQbv79?OPJyAT=2 zF^Xdt$9)_TMmT?++l@dB#*of70$84OI@Wo<8CS2D*qpANH4Y$A`Hj)<7pyudX zU5P#$7XAZ|)uz#I!i0G^F!s3v!66cXw9hi{SCES?(W6RNfkK z>^%L7hAo~a)Yj?0XRmoxMs1yb)zY^foqnY9xXuAqsO`7U#ZcJj)z!&VKTM&iJ)ySa z2y{2<$+RyW{CPb({hFh!hEDgKR?w^0_u}P#UPIT*Pj4 z{PF4uHS>JZcSJP*bXcz@QJcn-x)Zm8#&wO#sgd?XEjwKnhSsCYiDf(Qsc79iC&yX; zM^~3&7&87^gfzbrN)4s`6}|v4WP=;mujILf5i;j0pA4uzLjHMmus1sJTDA1ZwXt4l z<~MwH;I`fI%_BrNU7zY2J5P(OZxE=Q`O*_=HqqF5U!ptbxf*80T7&gX7KL$Mw>mm~ zug=`-4z4=NMJNw@m34F(bOQT+PpDnseTo*clKkBd_-=jeObgL< zug6h8K>4hm0`((^8}<0jG;vo1gsW#t=^*HEv>z)n$yZDoSR2+sWH^Z+&}@ zH=1zMiq5)ejgFHyBVA9>ei|Odq`G#bb-4YQz<)iKw(gMTJs>Nk_fm1K4w{EPpC8jz zo={hp#gp|Sw@P)lCmMC!k23DI(i3x6OdrV$>e2O(itn8IjB?_;2w#0#nl7il3-i?n zpS>D$>E>X8$JukiR!R%iomE~vI^8&|uScgVF+VHR%~Rh9_VVZ$gw->X)x`R0(c7%m z(*(w*(R!u6ZR_H3^fQuHdZLz2U#77~u|i#)zWAz7_d?nz{$l%0$9aan&nWZg?lOM; z)f1(_d4|57IPsf0!x+z6e3w-13J%aV%&(Q2RV!6kD^*l0RZ=VUM6Hz6N^Pr^(mx4q zm%h7JO8+Iet&F1f zW_8nl@8Eaq4t_4*jQab&F}nKtKI9I5&O7+U*6?F~zkU7M_xe{Fvy&B2RtMGTziO;b zR;a6csCK3vD|d@dMAz&${e}%+l)+z^!#v7HweE9$6uSD8)AyogEjNA&^ZlAwLo4=e zwd_YvEVRo&eJM8Gla<}L{I0hW)P3M|bowBop^na2B^CcmF*jG9|6gMLU;DojX~y~3 zj&*;!myem5)`^?-rACH+FKG-8)z7osi{T2WO|+OpcWVndbX8gK^vgbGm|6VCDj##D mX`hT}DhE$w#+0Xr_?ao@{ED2Frn#~r?QiBETlt&qZT}Cm6h*TD delta 12484 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 From 93a959502db4902d5239537a5bdf76f9e97b42eb Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Jun 2017 23:44:44 +1000 Subject: [PATCH 192/839] Fix reserved memory filter --- ProcessHacker/prpgmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index 0dec99fbd5df..8a8dbc604144 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -245,7 +245,7 @@ BOOLEAN PhpMemoryTreeFilterCallback( return FALSE; if (memoryContext->ListContext.HideReservedRegions && - (memoryItem->Type & MEM_PRIVATE || memoryItem->Type & MEM_MAPPED) && + (memoryItem->Type & MEM_PRIVATE || memoryItem->Type & MEM_MAPPED || memoryItem->Type & MEM_IMAGE) && memoryItem->State & MEM_RESERVE && memoryItem->AllocationBaseItem // Ignore root nodes ) From d6b17c91884aad3af44ba8d11c7ea35d8bd1ac53 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Jun 2017 05:32:53 +1000 Subject: [PATCH 193/839] update macro --- ProcessHacker/about.c | 3 ++- ProcessHacker/affinity.c | 4 ++-- ProcessHacker/chcol.c | 2 +- ProcessHacker/chdlg.c | 2 +- ProcessHacker/chproc.c | 4 ++-- ProcessHacker/findobj.c | 4 ++-- ProcessHacker/gdihndl.c | 5 ++--- ProcessHacker/hidnproc.c | 2 +- ProcessHacker/hndlstat.c | 5 ++--- ProcessHacker/infodlg.c | 2 +- ProcessHacker/jobprp.c | 5 ++--- ProcessHacker/logwnd.c | 2 +- ProcessHacker/mainwnd.c | 2 +- ProcessHacker/mdump.c | 4 ++-- ProcessHacker/memedit.c | 2 +- ProcessHacker/memlists.c | 5 ++--- ProcessHacker/memprot.c | 2 +- ProcessHacker/memrslt.c | 2 +- ProcessHacker/memsrch.c | 4 ++-- ProcessHacker/miniinfo.c | 4 ++-- ProcessHacker/mtgndlg.c | 4 ++-- ProcessHacker/netstk.c | 7 ++----- ProcessHacker/notifico.c | 2 +- ProcessHacker/ntobjprp.c | 21 +++++++++++---------- ProcessHacker/procprp.c | 3 ++- ProcessHacker/prpgenv.c | 10 ++++------ ProcessHacker/prpgmem.c | 2 +- plugins/DotNetTools/asmpage.c | 2 +- plugins/ExtendedNotifications/main.c | 22 +++------------------- plugins/ExtendedServices/options.c | 2 +- plugins/ExtendedServices/other.c | 6 +++--- plugins/ExtendedServices/recovery.c | 10 +++++----- plugins/ExtendedServices/srvprgrs.c | 2 +- plugins/ExtendedServices/trigger.c | 8 ++++---- plugins/ExtendedServices/triggpg.c | 2 +- plugins/ExtendedTools/disktab.c | 2 +- plugins/ExtendedTools/gpunodes.c | 2 +- plugins/ExtendedTools/gpusys.c | 2 +- plugins/ExtendedTools/modsrv.c | 2 +- plugins/ExtendedTools/options.c | 2 +- plugins/ExtendedTools/unldll.c | 2 +- plugins/ExtendedTools/wswatch.c | 2 +- plugins/HardwareDevices/prpsh.c | 2 +- plugins/NetworkTools/options.c | 2 +- plugins/NetworkTools/ping.c | 2 +- plugins/OnlineChecks/options.c | 2 +- plugins/UserNotes/main.c | 10 +++++----- plugins/WindowExplorer/wnddlg.c | 4 ++-- plugins/WindowExplorer/wndprp.c | 10 +++++----- 49 files changed, 96 insertions(+), 118 deletions(-) diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index 4a8b262e16f0..91c61818a748 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -23,6 +23,7 @@ #include +#include #include #include @@ -87,7 +88,7 @@ static INT_PTR CALLBACK PhpAboutDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/affinity.c b/ProcessHacker/affinity.c index d019c3e2ac65..48c3a19f96c6 100644 --- a/ProcessHacker/affinity.c +++ b/ProcessHacker/affinity.c @@ -232,7 +232,7 @@ static INT_PTR CALLBACK PhpProcessAffinityDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); @@ -304,7 +304,7 @@ static INT_PTR CALLBACK PhpProcessAffinityDlgProc( HWND checkBox = GetDlgItem(hwndDlg, IDC_CPU0 + i); if (IsWindowEnabled(checkBox)) - Button_SetCheck(checkBox, LOWORD(wParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(checkBox, GET_WM_COMMAND_ID(wParam, lParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED); } } break; diff --git a/ProcessHacker/chcol.c b/ProcessHacker/chcol.c index 5774a3a54550..68c16754a6c7 100644 --- a/ProcessHacker/chcol.c +++ b/ProcessHacker/chcol.c @@ -207,7 +207,7 @@ INT_PTR CALLBACK PhpColumnsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/ProcessHacker/chdlg.c b/ProcessHacker/chdlg.c index bb4683bc5d0e..b7acd39a9ed4 100644 --- a/ProcessHacker/chdlg.c +++ b/ProcessHacker/chdlg.c @@ -272,7 +272,7 @@ INT_PTR CALLBACK PhpChoiceDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index fd0578842349..4b83a8f176eb 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -21,8 +21,8 @@ */ #include - #include +#include typedef struct _CHOOSE_PROCESS_DIALOG_CONTEXT { @@ -257,7 +257,7 @@ INT_PTR CALLBACK PhpChooseProcessDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 926cc9549c2a..1865b4b54f99 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -293,7 +293,7 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: { @@ -477,7 +477,7 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( info.TypeName = result->TypeName; info.BestObjectName = result->Name; - if (LOWORD(wParam) == ID_HANDLE_OBJECTPROPERTIES1) + if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_OBJECTPROPERTIES1) PhShowHandleObjectProperties1(hwndDlg, &info); else PhShowHandleObjectProperties2(hwndDlg, &info); diff --git a/ProcessHacker/gdihndl.c b/ProcessHacker/gdihndl.c index c8d192d049a0..e5d62f7018ae 100644 --- a/ProcessHacker/gdihndl.c +++ b/ProcessHacker/gdihndl.c @@ -21,10 +21,9 @@ */ #include - #include - #include +#include typedef struct _GDI_HANDLES_CONTEXT { @@ -357,7 +356,7 @@ INT_PTR CALLBACK PhpGdiHandlesDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index 926b16cb00eb..c3b16c7158b2 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -189,7 +189,7 @@ static INT_PTR CALLBACK PhpHiddenProcessesDlgProc( return TRUE; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/hndlstat.c b/ProcessHacker/hndlstat.c index 3835d47f750d..15c9e45bd720 100644 --- a/ProcessHacker/hndlstat.c +++ b/ProcessHacker/hndlstat.c @@ -21,10 +21,9 @@ */ #include - #include - #include +#include typedef struct _HANDLE_STATISTICS_ENTRY { @@ -220,7 +219,7 @@ INT_PTR CALLBACK PhpHandleStatisticsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 176140cc2662..8c8cf487cc57 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -96,7 +96,7 @@ static INT_PTR CALLBACK PhpInformationDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/jobprp.c b/ProcessHacker/jobprp.c index c3ecb3db161a..a9668838d10d 100644 --- a/ProcessHacker/jobprp.c +++ b/ProcessHacker/jobprp.c @@ -21,12 +21,11 @@ */ #include - #include #include - #include #include +#include typedef struct _JOB_PAGE_CONTEXT { @@ -390,7 +389,7 @@ INT_PTR CALLBACK PhpJobPageProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_TERMINATE: { diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 0fde6d46e648..1bcfe90a3f01 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -209,7 +209,7 @@ INT_PTR CALLBACK PhpLogDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 4ae19d682a3e..76c9b25b83d7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -262,7 +262,7 @@ LRESULT CALLBACK PhMwpWndProc( break; case WM_COMMAND: { - PhMwpOnCommand(LOWORD(wParam)); + PhMwpOnCommand(GET_WM_COMMAND_ID(wParam, lParam)); } break; case WM_SHOWWINDOW: diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index 92e88db305eb..e40ab800a630 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -22,8 +22,8 @@ #include +#include #include - #include #include @@ -401,7 +401,7 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index 8d30e574cfcc..b76344d1c68a 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -349,7 +349,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 537e318f8c55..c61d9a321db0 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -21,13 +21,12 @@ */ #include - #include #include - #include #include #include +#include #define MSG_UPDATE (WM_APP + 1) @@ -191,7 +190,7 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/memprot.c b/ProcessHacker/memprot.c index 5feec758b8b9..17b6d09f09e3 100644 --- a/ProcessHacker/memprot.c +++ b/ProcessHacker/memprot.c @@ -102,7 +102,7 @@ static INT_PTR CALLBACK PhpMemoryProtectDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index 3ea48045760c..a066c5770f2b 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -369,7 +369,7 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index 41c2b0c8876e..0440ff6f4b30 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -554,7 +554,7 @@ INT_PTR CALLBACK PhpMemoryStringDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); @@ -679,7 +679,7 @@ INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 64bf00ebfe15..3a7b394662f0 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -219,7 +219,7 @@ LRESULT CALLBACK PhMipContainerWndProc( break; case WM_ACTIVATE: { - PhMipContainerOnActivate(LOWORD(wParam), !!HIWORD(wParam)); + PhMipContainerOnActivate(GET_WM_COMMAND_ID(wParam, lParam), !!HIWORD(wParam)); } break; case WM_SIZE: @@ -282,7 +282,7 @@ INT_PTR CALLBACK PhMipMiniInfoDialogProc( break; case WM_COMMAND: { - PhMipOnCommand(LOWORD(wParam), HIWORD(wParam)); + PhMipOnCommand(GET_WM_COMMAND_ID(wParam, lParam), HIWORD(wParam)); } break; case WM_NOTIFY: diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index 1c9e6249de3e..cd864ba0eba6 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -21,8 +21,8 @@ */ #include - #include +#include typedef struct _MITIGATION_POLICY_ENTRY { @@ -127,7 +127,7 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/ProcessHacker/netstk.c b/ProcessHacker/netstk.c index 2e89b091a71a..8ae55638bfed 100644 --- a/ProcessHacker/netstk.c +++ b/ProcessHacker/netstk.c @@ -21,10 +21,9 @@ */ #include - #include - #include +#include typedef struct NETWORK_STACK_CONTEXT { @@ -210,9 +209,7 @@ static INT_PTR CALLBACK PhpNetworkStackDlgProc( break; case WM_COMMAND: { - INT id = LOWORD(wParam); - - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: // Esc and X button to close case IDOK: diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 729f81412e78..66aaa2891ae0 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -208,7 +208,7 @@ VOID PhNfForwardMessage( } } - switch (LOWORD(LParam)) + switch (GET_WM_COMMAND_ID(WParam, LParam)) { case WM_LBUTTONDOWN: { diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index 3864cfc5fd41..a0a9355a3603 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -23,9 +23,10 @@ #include #include - #include +#include + typedef struct _COMMON_PAGE_CONTEXT { PPH_OPEN_OBJECT OpenObject; @@ -227,7 +228,7 @@ INT_PTR CALLBACK PhpEventPageProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_SET: case IDC_RESET: @@ -242,7 +243,7 @@ INT_PTR CALLBACK PhpEventPageProc( pageContext->Context ))) { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_SET: NtSetEvent(eventHandle, NULL); @@ -308,7 +309,7 @@ INT_PTR CALLBACK PhpEventPairPageProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_SETLOW: case IDC_SETHIGH: @@ -322,7 +323,7 @@ INT_PTR CALLBACK PhpEventPairPageProc( pageContext->Context ))) { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_SETLOW: NtSetLowEventPair(eventPairHandle); @@ -609,7 +610,7 @@ INT_PTR CALLBACK PhpSemaphorePageProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_ACQUIRE: case IDC_RELEASE: @@ -619,11 +620,11 @@ INT_PTR CALLBACK PhpSemaphorePageProc( if (NT_SUCCESS(status = pageContext->OpenObject( &semaphoreHandle, - LOWORD(wParam) == IDC_ACQUIRE ? SYNCHRONIZE : SEMAPHORE_MODIFY_STATE, + GET_WM_COMMAND_ID(wParam, lParam) == IDC_ACQUIRE ? SYNCHRONIZE : SEMAPHORE_MODIFY_STATE, pageContext->Context ))) { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_ACQUIRE: { @@ -719,7 +720,7 @@ INT_PTR CALLBACK PhpTimerPageProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_CANCEL: { @@ -732,7 +733,7 @@ INT_PTR CALLBACK PhpTimerPageProc( pageContext->Context ))) { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_CANCEL: NtCancelTimer(timerHandle, NULL); diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 03236a716ee7..bf6e302925ef 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -242,7 +243,7 @@ LRESULT CALLBACK PhpPropSheetWndProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: // Prevent the OK button from working (even though diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index e819a260440c..3806999f8fd1 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -23,14 +23,12 @@ #include #include #include - -#include - #include #include #include - #include +#include +#include typedef struct _EDIT_ENV_DIALOG_CONTEXT { @@ -243,7 +241,7 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_NEW: { @@ -489,7 +487,7 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index 8a8dbc604144..adb9146f7418 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -421,7 +421,7 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( break; } - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SHOWCONTEXTMENU: { diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index 6ead4b55596d..fc9419e258b2 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -1270,7 +1270,7 @@ INT_PTR CALLBACK DotNetAsmPageDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_COPY: { diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index 33abdab44813..b272d100f136 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -695,11 +695,11 @@ INT_PTR HandleCommonMessages( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_LIST: { - if (HIWORD(wParam) == LBN_SELCHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE) { ULONG i; @@ -900,14 +900,6 @@ INT_PTR CALLBACK ProcessesDlgProc( EditingProcessFilterList = NULL; } break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - NOTHING; - } - } - break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; @@ -964,14 +956,6 @@ INT_PTR CALLBACK ServicesDlgProc( EditingServiceFilterList = NULL; } break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - NOTHING; - } - } - break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; @@ -1016,7 +1000,7 @@ INT_PTR CALLBACK LoggingDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_BROWSE: { diff --git a/plugins/ExtendedServices/options.c b/plugins/ExtendedServices/options.c index 51cb5d9ce2a1..019bfbd32dff 100644 --- a/plugins/ExtendedServices/options.c +++ b/plugins/ExtendedServices/options.c @@ -40,7 +40,7 @@ INT_PTR CALLBACK OptionsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/plugins/ExtendedServices/other.c b/plugins/ExtendedServices/other.c index 5b7957afa653..28d98146d6a9 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -395,7 +395,7 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_ADD: { @@ -535,7 +535,7 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( break; } - switch (HIWORD(wParam)) + switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case EN_CHANGE: case CBN_SELCHANGE: @@ -544,7 +544,7 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( { context->Dirty = TRUE; - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_PRESHUTDOWNTIMEOUT: context->PreshutdownTimeoutValid = TRUE; diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index b917b0334498..91f42768cefe 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -343,13 +343,13 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_FIRSTFAILURE: case IDC_SECONDFAILURE: case IDC_SUBSEQUENTFAILURES: { - if (HIWORD(wParam) == CBN_SELCHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) { EspFixControls(hwndDlg, context); } @@ -398,7 +398,7 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( break; } - switch (HIWORD(wParam)) + switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case EN_CHANGE: case CBN_SELCHANGE: @@ -627,7 +627,7 @@ INT_PTR CALLBACK RestartComputerDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); @@ -692,7 +692,7 @@ INT_PTR CALLBACK RestartComputerDlgProc( break; case IDC_RESTARTMESSAGE: { - if (HIWORD(wParam) == EN_CHANGE) + 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), diff --git a/plugins/ExtendedServices/srvprgrs.c b/plugins/ExtendedServices/srvprgrs.c index 0cde80360efb..3b19031596ef 100644 --- a/plugins/ExtendedServices/srvprgrs.c +++ b/plugins/ExtendedServices/srvprgrs.c @@ -79,7 +79,7 @@ INT_PTR CALLBACK EspRestartServiceDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/plugins/ExtendedServices/trigger.c b/plugins/ExtendedServices/trigger.c index e0aa2b667435..fb2c2803837e 100644 --- a/plugins/ExtendedServices/trigger.c +++ b/plugins/ExtendedServices/trigger.c @@ -1376,16 +1376,16 @@ INT_PTR CALLBACK EspServiceTriggerDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_TYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) { EspFixServiceTriggerControls(hwndDlg, context); } break; case IDC_SUBTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) { EspFixServiceTriggerControls(hwndDlg, context); } @@ -1702,7 +1702,7 @@ INT_PTR CALLBACK ValueDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/plugins/ExtendedServices/triggpg.c b/plugins/ExtendedServices/triggpg.c index 0188ddaead0f..514dd38a0d5b 100644 --- a/plugins/ExtendedServices/triggpg.c +++ b/plugins/ExtendedServices/triggpg.c @@ -107,7 +107,7 @@ INT_PTR CALLBACK EspServiceTriggersDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_NEW: if (context->TriggerContext) diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index b8b1ae72e953..2ab3ad8c3cd5 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -1133,7 +1133,7 @@ INT_PTR CALLBACK EtpDiskTabErrorDialogProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_RESTART: ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index cc56abcd55b6..1526bd358a45 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -310,7 +310,7 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/plugins/ExtendedTools/gpusys.c b/plugins/ExtendedTools/gpusys.c index 57c9dcb68aae..aae9aec7745a 100644 --- a/plugins/ExtendedTools/gpusys.c +++ b/plugins/ExtendedTools/gpusys.c @@ -236,7 +236,7 @@ INT_PTR CALLBACK EtpGpuPanelDialogProc( { case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_NODES: EtShowGpuNodesDialog(GpuDialog, GpuSection->Parameters); diff --git a/plugins/ExtendedTools/modsrv.c b/plugins/ExtendedTools/modsrv.c index 16b110ae4628..654578396009 100644 --- a/plugins/ExtendedTools/modsrv.c +++ b/plugins/ExtendedTools/modsrv.c @@ -158,7 +158,7 @@ INT_PTR CALLBACK EtpModuleServicesDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/plugins/ExtendedTools/options.c b/plugins/ExtendedTools/options.c index a33671d5f1e5..cd4c21e63b7d 100644 --- a/plugins/ExtendedTools/options.c +++ b/plugins/ExtendedTools/options.c @@ -61,7 +61,7 @@ INT_PTR CALLBACK OptionsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 93325b16fbf3..8a0f5a597649 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -356,7 +356,7 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/plugins/ExtendedTools/wswatch.c b/plugins/ExtendedTools/wswatch.c index 470092cb3b0d..9e7ca403c5b5 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -507,7 +507,7 @@ INT_PTR CALLBACK EtpWsWatchDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index 2aa401d4d246..546ec8f469db 100644 --- a/plugins/HardwareDevices/prpsh.c +++ b/plugins/HardwareDevices/prpsh.c @@ -198,7 +198,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: // Prevent the OK button from working (even though diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index 142402753dd7..caa320ccf068 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -42,7 +42,7 @@ INT_PTR CALLBACK OptionsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index cb6165e4e30d..fc415881497e 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -353,7 +353,7 @@ INT_PTR CALLBACK NetworkPingWndProc( return TRUE; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: diff --git a/plugins/OnlineChecks/options.c b/plugins/OnlineChecks/options.c index 328c96da5b34..c7f27a4acc97 100644 --- a/plugins/OnlineChecks/options.c +++ b/plugins/OnlineChecks/options.c @@ -43,7 +43,7 @@ INT_PTR CALLBACK OptionsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 88a55cfa438b..d5bed8c6e40e 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -1492,7 +1492,7 @@ INT_PTR CALLBACK OptionsDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); @@ -1703,11 +1703,11 @@ INT_PTR CALLBACK ProcessCommentPageDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_COMMENT: { - if (HIWORD(wParam) == EN_CHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) EnableWindow(context->RevertHandle, TRUE); } break; @@ -1813,11 +1813,11 @@ INT_PTR CALLBACK ServiceCommentPageDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_COMMENT: { - if (HIWORD(wParam) == EN_CHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE); } break; diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index baa2117ac900..ab97179b6f24 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -422,7 +422,7 @@ INT_PTR CALLBACK WepWindowsDlgProc( break; } - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: DestroyWindow(hwndDlg); @@ -847,7 +847,7 @@ INT_PTR CALLBACK WepWindowsPageProc( break; } - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: WepRefreshWindows(context); diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 515b17ee9c40..c853353a0b11 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -413,7 +413,7 @@ LRESULT CALLBACK WepPropSheetWndProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: { @@ -845,7 +845,7 @@ INT_PTR CALLBACK WepWindowGeneralDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: context->HookDataValid = FALSE; @@ -977,7 +977,7 @@ INT_PTR CALLBACK WepWindowStylesDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: WepRefreshWindowStyles(hwndDlg, context); @@ -1092,7 +1092,7 @@ INT_PTR CALLBACK WepWindowClassDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: context->HookDataValid = FALSE; @@ -1196,7 +1196,7 @@ INT_PTR CALLBACK WepWindowPropertiesDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_REFRESH: WepRefreshWindowProps(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), context); From 5d6efceb6d88518fd78494a2d0cc46a3f08ccce1 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Jun 2017 21:08:28 +1000 Subject: [PATCH 194/839] partial revert 60abdf1ae8807b95a3434ba5e4bc8cb6003628d5 --- ProcessHacker/memlist.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index c41417218ba2..87b45fcbdd9a 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -327,6 +327,9 @@ VOID PhReplaceMemoryList( 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)) @@ -355,6 +358,9 @@ VOID PhReplaceMemoryList( 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 { From a390a880138b7e1eb418fe5f6970c2b569545795 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Jun 2017 00:38:23 +1000 Subject: [PATCH 195/839] Updater: disable installer mode --- plugins/Updater/page5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 0b766debcfeb..0a94def0107c 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -66,7 +66,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( break; info.lpFile = PhGetStringOrEmpty(context->SetupFilePath); - info.lpParameters = L"-update"; + //info.lpParameters = L"-update"; info.lpVerb = PhGetOwnTokenAttributes().Elevated ? NULL : L"runas"; info.nShow = SW_SHOW; info.hwnd = hwndDlg; From a31181accdff1a1f29cce7ecde45f5e2b4b49478 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Jun 2017 00:50:23 +1000 Subject: [PATCH 196/839] Fix regression --- ProcessHacker/notifico.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 66aaa2891ae0..0dfe8e91674c 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -208,7 +207,7 @@ VOID PhNfForwardMessage( } } - switch (GET_WM_COMMAND_ID(WParam, LParam)) + switch (LOWORD(LParam)) { case WM_LBUTTONDOWN: { From b8b063a9eba2a2ab533c848c152c6cee7e7e2423 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Jun 2017 02:03:54 +1000 Subject: [PATCH 197/839] Add buildsdk command fix --- ProcessHacker/ProcessHacker.vcxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 2220ef6efe98..2e5b41dd74cf 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -109,7 +109,7 @@ $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - $(SolutionDir)build\build_sdk.cmd + "$(SolutionDir)build\build_sdk.cmd" @@ -141,7 +141,7 @@ - $(SolutionDir)build\build_sdk.cmd + "$(SolutionDir)build\build_sdk.cmd" @@ -179,7 +179,7 @@ $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - $(SolutionDir)build\build_sdk.cmd + "$(SolutionDir)build\build_sdk.cmd" @@ -216,7 +216,7 @@ $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - $(SolutionDir)build\build_sdk.cmd + "$(SolutionDir)build\build_sdk.cmd" From 6e866dae983ddb0fa91480b43fdb0a22d1bd9e70 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Jun 2017 03:08:42 +1000 Subject: [PATCH 198/839] Fix PsAttributes #147, Fix spacing --- phnt/include/ntpsapi.h | 45 ++++++++++++++++++++------------- phnt/include/ntrtl.h | 56 +++++++++++++++++++++--------------------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 23ae8b712e56..002a83c1d48d 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1222,15 +1222,15 @@ 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 PsAttributeMax } PS_ATTRIBUTE_NUM; @@ -1243,11 +1243,11 @@ typedef enum _PS_ATTRIBUTE_NUM ((Additive) ? PS_ATTRIBUTE_ADDITIVE : 0)) #define PS_ATTRIBUTE_PARENT_PROCESS \ - PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, FALSE) + PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, TRUE) #define PS_ATTRIBUTE_DEBUG_PORT \ - PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, FALSE) + PsAttributeValue(PsAttributeDebugPort, FALSE, TRUE, TRUE) #define PS_ATTRIBUTE_TOKEN \ - PsAttributeValue(PsAttributeToken, FALSE, TRUE, FALSE) + PsAttributeValue(PsAttributeToken, FALSE, TRUE, TRUE) #define PS_ATTRIBUTE_CLIENT_ID \ PsAttributeValue(PsAttributeClientId, TRUE, FALSE, FALSE) #define PS_ATTRIBUTE_TEB_ADDRESS \ @@ -1274,6 +1274,8 @@ typedef enum _PS_ATTRIBUTE_NUM PsAttributeValue(PsAttributeIdealProcessor, TRUE, TRUE, FALSE) #define PS_ATTRIBUTE_MITIGATION_OPTIONS \ PsAttributeValue(PsAttributeMitigationOptions, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_PROTECTION_LEVEL \ + PsAttributeValue(PsAttributeProtectionLevel, FALSE, TRUE, FALSE) // end_rev @@ -1331,18 +1333,14 @@ typedef struct _PS_STD_HANDLE_INFO ULONG StdHandleSubsystemType; } PS_STD_HANDLE_INFO, *PPS_STD_HANDLE_INFO; -// windows-internals-book:"Chapter 5" -typedef enum _PS_CREATE_STATE +// private +typedef struct _PS_BNO_ISOLATION_PARAMETERS { - PsCreateInitialState, - PsCreateFailOnFileOpen, - PsCreateFailOnSectionCreate, - PsCreateFailExeFormat, - PsCreateFailMachineMismatch, - PsCreateFailExeName, // Debugger specified - PsCreateSuccess, - PsCreateMaximumStates -} PS_CREATE_STATE; + UNICODE_STRING IsolationPrefix; + ULONG HandleCount; + PVOID *Handles; + BOOLEAN IsolationEnabled; +} PS_BNO_ISOLATION_PARAMETERS, *PPS_BNO_ISOLATION_PARAMETERS; // private typedef enum _PS_MITIGATION_OPTION @@ -1369,6 +1367,19 @@ typedef enum _PS_MITIGATION_OPTION PS_MITIGATION_OPTION_RESTRICT_SET_THREAD_CONTEXT } PS_MITIGATION_OPTION; +// windows-internals-book:"Chapter 5" +typedef enum _PS_CREATE_STATE +{ + PsCreateInitialState, + PsCreateFailOnFileOpen, + PsCreateFailOnSectionCreate, + PsCreateFailExeFormat, + PsCreateFailMachineMismatch, + PsCreateFailExeName, // Debugger specified + PsCreateSuccess, + PsCreateMaximumStates +} PS_CREATE_STATE; + typedef struct _PS_CREATE_INFO { SIZE_T Size; diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 1d904d2ead41..c7522fb38c09 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -323,13 +323,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 +339,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 @@ -954,11 +954,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_* @@ -2407,8 +2407,8 @@ 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; From a94fef7c568d1897cb1d6b7c6d86277f755648bc Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Jun 2017 00:33:17 +1000 Subject: [PATCH 199/839] Update ProcessEnergyValues & SystemFullProcessInformation --- ProcessHacker/settings.c | 1 + phnt/include/ntexapi.h | 67 +++++++++++++++++++++++++++++++++++----- phnt/include/ntpsapi.h | 2 +- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 547591b0f0fd..3501aebaf6f4 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -46,6 +46,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); PhpAddIntegerSetting(L"EnableStage2", L"1"); PhpAddIntegerSetting(L"EnableWarnings", L"1"); + PhpAddIntegerSetting(L"EnableWindowText", L"1"); PhpAddStringSetting(L"EnvironmentListViewColumns", L""); PhpAddIntegerSetting(L"FindObjRegex", L"0"); PhpAddStringSetting(L"FindObjListViewColumns", L""); diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 545f81e9ad52..5a42dbf6bffa 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -2662,6 +2662,18 @@ typedef struct _PROCESS_DISK_COUNTERS } PROCESS_DISK_COUNTERS, *PPROCESS_DISK_COUNTERS; // private +typedef struct _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]; @@ -2672,19 +2684,55 @@ 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[2][4]; + ULONGLONG WorkOnBehalfCycles[2][4]; } 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[9]; + 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; + }; + }; +} 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 struct _SYSTEM_PROCESS_INFORMATION_EXTENSION { @@ -2696,7 +2744,9 @@ typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION struct { ULONG HasStrongId : 1; - ULONG Spare : 31; + ULONG Classification : 4; + ULONG BackgroundActivityModerated : 1; + ULONG Spare : 26; }; }; ULONG UserSidOffset; @@ -2706,6 +2756,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 diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 002a83c1d48d..0e65c8dfd8f2 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -173,7 +173,7 @@ typedef enum _PROCESSINFOCLASS ProcessChildProcessInformation, // PROCESS_CHILD_PROCESS_INFORMATION ProcessHighGraphicsPriorityInformation, ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 - ProcessEnergyValues, + ProcessEnergyValues, // PROCESS_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES ProcessActivityThrottleState, // PROCESS_ACTIVITY_THROTTLE_STATE ProcessActivityThrottlePolicy, // PROCESS_ACTIVITY_THROTTLE_POLICY ProcessWin32kSyscallFilterInformation, From 53e8a37a66befc1e3726854d642dcc8637fb118a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Jun 2017 05:08:00 +1000 Subject: [PATCH 200/839] Update HEAP_INFORMATION_CLASS, Fix RTL_USER_PROCESS_PARAMETERS32 types --- ProcessHacker/dbgcon.c | 2 +- phnt/include/ntrtl.h | 40 ++++++++++++++++++++++++++++++++++++++-- phnt/include/ntwow64.h | 4 ++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index 7708ddbae347..2107da471b6b 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -1558,7 +1558,7 @@ NTSTATUS PhpDebugConsoleThreadStart( debuggingInfo.StackTraceDepth = 32; debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine; - if (!NT_SUCCESS(RtlSetHeapInformation(NULL, HeapDebuggingInformation, &debuggingInfo, sizeof(HEAP_DEBUGGING_INFORMATION)))) + 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."); } diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index c7522fb38c09..0f01ff0e065b 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -3595,8 +3595,44 @@ 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 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; + PROCESS_HEAP_INFORMATION ProcessHeapInformation; + HEAP_INFORMATION HeapInformation; +} HEAP_EXTENDED_INFORMATION, *PHEAP_EXTENDED_INFORMATION; // rev typedef NTSTATUS (NTAPI *PRTL_HEAP_LEAK_ENUMERATION_ROUTINE)( diff --git a/phnt/include/ntwow64.h b/phnt/include/ntwow64.h index 985c5f0a911e..16948f927aae 100644 --- a/phnt/include/ntwow64.h +++ b/phnt/include/ntwow64.h @@ -219,8 +219,8 @@ 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; From ca494f6a683a397ac014421ad61acbe233d17e7a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Jun 2017 05:15:59 +1000 Subject: [PATCH 201/839] Update more default window icons --- ProcessHacker/findobj.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 1865b4b54f99..2c222fa4f177 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -215,6 +215,9 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( { HWND lvHandle; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); PhFindObjectsListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); From 2aa3678afa717b841793f8c1ee92e4263203fb09 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Jun 2017 20:09:04 +1000 Subject: [PATCH 202/839] DotNetTools: Remove XP support --- plugins/DotNetTools/asmpage.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index fc9419e258b2..f21322d1026f 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -113,18 +113,6 @@ typedef struct _FLAG_DEFINITION 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 ); @@ -926,17 +914,11 @@ 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) @@ -947,7 +929,7 @@ NTSTATUS UpdateDotNetTraceInfoThreadStart( else guidToEnable = &ClrRuntimeProviderGuid; - EnableTraceEx_I( + EnableTraceEx( guidToEnable, NULL, sessionHandle, From 2cb7b9a3020e21cda5642378b17cbd3147c1605a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 9 Jun 2017 09:46:41 +1000 Subject: [PATCH 203/839] Add PEB comment --- phnt/include/ntpebteb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 5b6801e5dd9c..60f3b02adc67 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -108,7 +108,7 @@ typedef struct _PEB ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; PVOID pShimData; - PVOID AppCompatInfo; + PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA UNICODE_STRING CSDVersion; From c14c8ca0616b8d4c69ecd28008b245399b3925bd Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 00:59:12 +1000 Subject: [PATCH 204/839] Remove unused resources (patch by tetyys) (1/2) --- ProcessHacker/include/mainwnd.h | 2 -- ProcessHacker/include/mainwndp.h | 2 +- ProcessHacker/mainwnd.c | 52 +++++++++++++++++--------------- ProcessHacker/resource.h | 4 --- ProcessHacker/settings.c | 1 + 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index bd6259c0c169..12cfb2551d49 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; diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 239f6f720bbd..ef1b7c8ea106 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -33,7 +33,7 @@ LRESULT CALLBACK PhMwpWndProc( // Initialization -BOOLEAN PhMwpInitializeWindowClass( +RTL_ATOM PhMwpInitializeWindowClass( VOID ); diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 76c9b25b83d7..1941ca980cab 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -3,6 +3,7 @@ * Main window * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -99,6 +100,7 @@ BOOLEAN PhMainWndInitialization( _In_ INT ShowCommand ) { + RTL_ATOM windowAtom; PH_STRING_BUILDER stringBuilder; PH_RECTANGLE windowRectangle; @@ -118,7 +120,7 @@ BOOLEAN PhMainWndInitialization( PhMwpInitializeProviders(); - if (!PhMwpInitializeWindowClass()) + if ((windowAtom = PhMwpInitializeWindowClass()) == INVALID_ATOM) return FALSE; windowRectangle.Position = PhGetIntegerPairSetting(L"MainWindowPosition"); @@ -127,23 +129,27 @@ BOOLEAN PhMainWndInitialization( // Create the window title. PhInitializeStringBuilder(&stringBuilder, 100); - PhAppendStringBuilder2(&stringBuilder, L"Process Hacker"); - if (PhCurrentUserName) + if (PhGetIntegerSetting(L"EnableWindowText")) { - PhAppendStringBuilder2(&stringBuilder, L" ["); - PhAppendStringBuilder(&stringBuilder, &PhCurrentUserName->sr); - PhAppendCharStringBuilder(&stringBuilder, ']'); - if (KphIsConnected()) PhAppendCharStringBuilder(&stringBuilder, '+'); - } + 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)"); + if (WINDOWS_HAS_UAC && PhGetOwnTokenAttributes().ElevationType == TokenElevationTypeFull) + PhAppendStringBuilder2(&stringBuilder, L" (Administrator)"); + } // Create the window. PhMainWndHandle = CreateWindow( - PH_MAINWND_CLASSNAME, + MAKEINTATOM(windowAtom), stringBuilder.String->Buffer, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, windowRectangle.Left, @@ -329,12 +335,13 @@ LRESULT CALLBACK PhMwpWndProc( return DefWindowProc(hWnd, uMsg, wParam, lParam); } -BOOLEAN PhMwpInitializeWindowClass( +RTL_ATOM PhMwpInitializeWindowClass( VOID ) { WNDCLASSEX wcex; - + PPH_STRING className; + memset(&wcex, 0, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = 0; @@ -342,17 +349,14 @@ BOOLEAN PhMwpInitializeWindowClass( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - //wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + className = PhaGetStringSetting(L"MainWindowClassName"); + wcex.lpszClassName = PhGetStringOrDefault(className, L"MainWindowClassName"); wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAINWND); - wcex.lpszClassName = PH_MAINWND_CLASSNAME; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - if (!RegisterClassEx(&wcex)) - return FALSE; - - return TRUE; + return RegisterClassEx(&wcex); } VOID PhMwpInitializeProviders( @@ -425,7 +429,7 @@ VOID PhMwpInitializeControls( 3, 3, PhMainWndHandle, - (HMENU)ID_MAINWND_PROCESSTL, + NULL, PhLibImageBase, NULL ); @@ -440,7 +444,7 @@ VOID PhMwpInitializeControls( 3, 3, PhMainWndHandle, - (HMENU)ID_MAINWND_SERVICETL, + NULL, PhLibImageBase, NULL ); @@ -455,7 +459,7 @@ VOID PhMwpInitializeControls( 3, 3, PhMainWndHandle, - (HMENU)ID_MAINWND_NETWORKTL, + NULL, PhLibImageBase, NULL ); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 0b906b03259b..b8c58833da9e 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -534,10 +534,6 @@ #define IDC_HANDLESEARCH 1385 #define IDC_SEARCH 1387 #define IDC_FILTEROPTIONS 1389 -#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 diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 3501aebaf6f4..a2f16f8c98c2 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -81,6 +81,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerPairSetting(L"LogWindowPosition", L"300,300"); 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"); From 15a09ae75d7b8c8e64b6850bd2dfad0cdcedad8a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 01:02:30 +1000 Subject: [PATCH 205/839] Improve startup performance, Improve installer mutant handling, Improve single instance (2/2) --- ProcessHacker/main.c | 59 ++++++++++++++++++++++++++++++++---------- phlib/include/phutil.h | 35 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 3d8b82255a96..6b9a95c163f5 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -252,17 +252,26 @@ INT WINAPI wWinMain( HANDLE mutantHandle; OBJECT_ATTRIBUTES oa; UNICODE_STRING mutantName; + PPH_STRING objectName; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PhMutant_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 2, 16); + PhStringRefToUnicodeString(&objectName->sr, &mutantName); - RtlInitUnicodeString(&mutantName, L"\\BaseNamedObjects\\ProcessHackerMutant"); InitializeObjectAttributes( &oa, &mutantName, - 0, - NULL, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), NULL ); NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + + PhDereferenceObject(objectName); } // Set priority. @@ -418,26 +427,48 @@ VOID PhUnregisterMessageLoopFilter( PhFree(FilterEntry); } -VOID PhActivatePreviousInstance( - VOID +static BOOLEAN NTAPI PhpPreviousInstancesCallback( + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF TypeName, + _In_opt_ PVOID Context ) { - HWND hwnd; - - hwnd = FindWindow(PH_MAINWND_CLASSNAME, NULL); + ULONG64 processId64; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; - if (hwnd) + if ( + PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && + PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && + PhStringToInteger64(&secondPart, 10, &processId64) + ) { - ULONG_PTR result; + HWND hwnd; - SendMessageTimeout(hwnd, WM_PH_ACTIVATE, PhStartupParameters.SelectPid, 0, SMTO_BLOCK, 5000, &result); + hwnd = PhGetProcessMainWindow((HANDLE)processId64, NULL); - if (result == PH_ACTIVATE_REPLY) + if (hwnd) { - SetForegroundWindow(hwnd); - RtlExitUserProcess(STATUS_SUCCESS); + 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); + } } } + + return TRUE; +} + +VOID PhActivatePreviousInstance( + VOID + ) +{ + PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); } VOID PhInitializeCommonControls( diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 9b6a440e9bbc..34a020c2ba4e 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1059,6 +1059,41 @@ PhParseCommandLineFuzzy( _Out_opt_ PPH_STRING *FullFileName ); +FORCEINLINE +HANDLE +NTAPI +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)) + { + OBJECT_ATTRIBUTES objectAttributes; + + InitializeObjectAttributes( + &objectAttributes, + &namespacePathUs, + OBJ_OPENIF, + NULL, + NULL + ); + + NtCreateDirectoryObject( + &directory, + MAXIMUM_ALLOWED, + &objectAttributes + ); + + PhEndInitOnce(&initOnce); + } + + return directory; +} + #ifdef __cplusplus } #endif From 6b14d397d1ea662462d4e7efee1f4180ffa99ac4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 01:12:02 +1000 Subject: [PATCH 206/839] DotNetTools: Fix appdomain enumeration failing on weird machines --- plugins/DotNetTools/counters.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/DotNetTools/counters.c b/plugins/DotNetTools/counters.c index 2092908c9e35..669831c546db 100644 --- a/plugins/DotNetTools/counters.c +++ b/plugins/DotNetTools/counters.c @@ -476,7 +476,7 @@ BOOLEAN OpenDotNetPublicControlBlock_V2( InitializeObjectAttributes( &objectAttributes, §ionNameUs, - 0, + OBJ_CASE_INSENSITIVE, NULL, NULL ); @@ -714,7 +714,7 @@ PPH_LIST QueryDotNetAppDomainsForPid_V2( InitializeObjectAttributes( &objectAttributes, §ionNameUs, - 0, + OBJ_CASE_INSENSITIVE, NULL, NULL ); @@ -832,7 +832,7 @@ PPH_LIST QueryDotNetAppDomainsForPid_V4( InitializeObjectAttributes( &objectAttributes, §ionNameUs, - 0, + OBJ_CASE_INSENSITIVE, NULL, NULL ); From a80b39c10c064a88b76a6d59d0b746150156dbe2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 01:13:05 +1000 Subject: [PATCH 207/839] peview: Use native timer queue --- tools/peview/include/peview.h | 4 +++- tools/peview/pdbprp.c | 29 ++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 10a4cc2c73bc..c2586e7db2b6 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -210,7 +210,9 @@ typedef struct _PDB_SYMBOL_CONTEXT HWND TreeNewHandle; HWND ParentWindowHandle; HANDLE SearchThreadHandle; - HANDLE UpdateTimer; + + HANDLE TimerQueueHandle; + HANDLE UpdateTimerHandle; ULONG64 BaseAddress; PPH_STRING FileName; diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 05e833a0d16c..c72a7a9e8722 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -908,7 +908,7 @@ VOID CALLBACK PvSymbolTreeUpdateCallback( TreeNew_NodesStructured(Context->TreeNewHandle); TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); - ChangeTimerQueueTimer(NULL, Context->UpdateTimer, 1000, INFINITE); + RtlUpdateTimer(Context->TimerQueueHandle, Context->UpdateTimerHandle, 1000, INFINITE); } INT_PTR CALLBACK PvpSymbolsDlgProc( @@ -958,17 +958,17 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( context->SearchThreadHandle = PhCreateThread(0, PeDumpFileSymbols, context); - if (CreateTimerQueueTimer( - &treeNewTimer, - NULL, - PvSymbolTreeUpdateCallback, - context, - 1000, - 1000, - 0 - )) + if (NT_SUCCESS(RtlCreateTimerQueue(&context->TimerQueueHandle))) { - context->UpdateTimer = treeNewTimer; + RtlCreateTimer( + context->TimerQueueHandle, + &context->UpdateTimerHandle, + PvSymbolTreeUpdateCallback, + context, + 0, + 1000, + 0 + ); } EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); @@ -976,8 +976,11 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( break; case WM_DESTROY: { - if (context->UpdateTimer) - DeleteTimerQueueTimer(NULL, context->UpdateTimer, NULL); + if (context->UpdateTimerHandle) + RtlDeleteTimer(context->TimerQueueHandle, context->UpdateTimerHandle, NULL); + + if (context->TimerQueueHandle) + RtlDeleteTimerQueue(context->TimerQueueHandle); NtClose(context->SearchThreadHandle); From aacc84ea8a15883605a37b84e9f1c715e26e0a77 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 01:37:17 +1000 Subject: [PATCH 208/839] OnlineChecks: Update detection ratio string for easier readability, Reduce resource usage, Remove unused code --- plugins/OnlineChecks/db.c | 7 ++-- plugins/OnlineChecks/db.h | 4 +- plugins/OnlineChecks/main.c | 3 -- plugins/OnlineChecks/onlnchk.h | 4 +- plugins/OnlineChecks/virustotal.c | 70 +++++++++++++------------------ 5 files changed, 35 insertions(+), 53 deletions(-) diff --git a/plugins/OnlineChecks/db.c b/plugins/OnlineChecks/db.c index 683b5264bc9e..e1082a5bb5ff 100644 --- a/plugins/OnlineChecks/db.c +++ b/plugins/OnlineChecks/db.c @@ -108,8 +108,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; @@ -122,10 +122,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 4a6568c3db54..8753c31115f4 100644 --- a/plugins/OnlineChecks/db.h +++ b/plugins/OnlineChecks/db.h @@ -57,8 +57,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 8bbfe03f7b1a..652594742bfa 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -608,7 +608,6 @@ VOID NTAPI ProcessItemCreateCallback( memset(extension, 0, sizeof(PROCESS_EXTENSION)); extension->ProcessItem = processItem; - extension->VirusTotalResult = PhReferenceEmptyString(); PhAcquireQueuedLockExclusive(&ProcessesListLock); InsertTailList(&ProcessListHead, &extension->ListEntry); @@ -643,7 +642,6 @@ VOID NTAPI ModuleItemCreateCallback( memset(extension, 0, sizeof(PROCESS_EXTENSION)); extension->ModuleItem = moduleItem; - extension->VirusTotalResult = PhReferenceEmptyString(); PhAcquireQueuedLockExclusive(&ProcessesListLock); InsertTailList(&ProcessListHead, &extension->ListEntry); @@ -678,7 +676,6 @@ VOID NTAPI ServiceItemCreateCallback( memset(extension, 0, sizeof(PROCESS_EXTENSION)); extension->ServiceItem = serviceItem; - extension->VirusTotalResult = PhReferenceEmptyString(); PhAcquireQueuedLockExclusive(&ProcessesListLock); InsertTailList(&ProcessListHead, &extension->ListEntry); diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 6e57a920a0f6..79b1ff043651 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -103,12 +103,12 @@ typedef struct _VIRUSTOTAL_FILE_HASH_ENTRY PPROCESS_EXTENSION Extension; INT64 Positives; + INT64 Total; 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 @@ -227,9 +227,7 @@ 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; diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 642ce0ddd9f3..4e7ed4ee645a 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -118,7 +118,7 @@ VOID VirusTotalRemoveCacheResult( PhClearReference(&extension->FileNameAnsi); PhClearReference(&extension->FileHashAnsi); PhClearReference(&extension->CreationTime); - PhClearReference(&extension->FileResult); + PhFree(extension); } @@ -220,9 +220,7 @@ PPH_LIST VirusTotalJsonToResultList( { PVIRUSTOTAL_API_RESULT result; PVOID jsonArrayObject; - PSTR fileLink; PSTR fileHash; - PSTR fileRatio; if (!(jsonArrayObject = JsonGetObjectArrayIndex(JsonObject, i))) continue; @@ -230,16 +228,11 @@ PPH_LIST VirusTotalJsonToResultList( 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->FileHash = fileHash ? PhZeroExtendToUtf16(fileHash) : NULL; PhAddItemList(results, result); } @@ -581,7 +574,7 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( PVOID jsonRootObject; - PVOID jsonScanObject; + //PVOID jsonScanObject; PVIRUSTOTAL_FILE_REPORT_RESULT result; if (!(jsonRootObject = CreateJsonParser(subRequestBuffer))) @@ -604,28 +597,28 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( result->Permalink = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "permalink")); result->StatusMessage = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "verbose_msg")); - if (jsonScanObject = JsonGetObject(jsonRootObject, "scans")) - { - PPH_LIST jsonArrayList; - - if (jsonArrayList = JsonGetObjectArrayList(jsonScanObject)) - { - result->ScanResults = PhCreateList(jsonArrayList->Count); - - 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"); - - PhFree(object); - } - - PhDereferenceObject(jsonArrayList); - } - } + //if (jsonScanObject = JsonGetObject(jsonRootObject, "scans")) + //{ + // PPH_LIST jsonArrayList; + // + // if (jsonArrayList = JsonGetObjectArrayList(jsonScanObject)) + // { + // result->ScanResults = PhCreateList(jsonArrayList->Count); + // + // 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"); + // + // PhFree(object); + // } + // + // PhDereferenceObject(jsonArrayList); + // } + //} return result; } @@ -721,24 +714,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 - { - - } } } From d3ee123245de4b19644402a9f78b93319f66f949 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 02:35:35 +1000 Subject: [PATCH 209/839] Fix single instance regression when application minimized --- ProcessHacker/appsup.c | 14 +++++++++++++- ProcessHacker/include/appsup.h | 6 ++++++ ProcessHacker/main.c | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 4e6a5d25f525..8d1c8eeaa023 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -3,6 +3,7 @@ * application support functions * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -723,6 +724,7 @@ typedef struct _GET_PROCESS_MAIN_WINDOW_CONTEXT HWND ImmersiveWindow; HANDLE ProcessId; BOOLEAN IsImmersive; + BOOLEAN SkipInvisible; } GET_PROCESS_MAIN_WINDOW_CONTEXT, *PGET_PROCESS_MAIN_WINDOW_CONTEXT; BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( @@ -735,7 +737,7 @@ BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( HWND parentWindow; WINDOWINFO windowInfo; - if (!IsWindowVisible(hwnd)) + if (context->SkipInvisible && !IsWindowVisible(hwnd)) return TRUE; GetWindowThreadProcessId(hwnd, &processId); @@ -769,12 +771,22 @@ 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; diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 275273f6d4e8..0973ce98f32a 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -109,6 +109,12 @@ HWND PhGetProcessMainWindow( _In_opt_ HANDLE ProcessHandle ); +HWND PhGetProcessMainWindowEx( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ BOOLEAN SkipInvisible + ); + PPH_STRING PhGetServiceRelevantFileName( _In_ PPH_STRINGREF ServiceName, _In_ SC_HANDLE ServiceHandle diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 6b9a95c163f5..d13513da6754 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -445,7 +445,7 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( { HWND hwnd; - hwnd = PhGetProcessMainWindow((HANDLE)processId64, NULL); + hwnd = PhGetProcessMainWindowEx((HANDLE)processId64, NULL, FALSE); if (hwnd) { From 82e171a493807121864f1496547fa87fa015ee88 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 04:36:35 +1000 Subject: [PATCH 210/839] Fix bug in previous commit --- ProcessHacker/appsup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 8d1c8eeaa023..e926582c6946 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -742,9 +742,9 @@ BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( GetWindowThreadProcessId(hwnd, &processId); - if (UlongToHandle(processId) == context->ProcessId && + if (UlongToHandle(processId) == context->ProcessId && (context->SkipInvisible ? !((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 + PhGetWindowTextEx(hwnd, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0 : TRUE)) // skip windows with no title { if (!context->ImmersiveWindow && context->IsImmersive && GetProp(hwnd, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow")) From c765bded0cc8b71d16ce849ecd6225153611b226 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 06:05:43 +1000 Subject: [PATCH 211/839] Remove legacy XP code --- ProcessHacker/ProcessHacker.rc | 58 -------------- ProcessHacker/actions.c | 4 +- ProcessHacker/anawait.c | 2 +- ProcessHacker/findobj.c | 2 +- ProcessHacker/hidnproc.c | 2 +- ProcessHacker/hndlprv.c | 2 +- ProcessHacker/mainwnd.c | 18 ++--- ProcessHacker/memprv.c | 40 ++-------- ProcessHacker/modlist.c | 11 +-- ProcessHacker/modprv.c | 15 ++-- ProcessHacker/mwpgproc.c | 30 +++---- ProcessHacker/netlist.c | 7 +- ProcessHacker/notifico.c | 9 +-- ProcessHacker/ntobjprp.c | 11 +-- ProcessHacker/procprv.c | 58 ++++++-------- ProcessHacker/proctree.c | 47 ++++------- ProcessHacker/prpggen.c | 2 +- ProcessHacker/prpgstat.c | 37 +++------ ProcessHacker/prpgthrd.c | 50 +++--------- ProcessHacker/resource.h | 1 - ProcessHacker/runas.c | 12 +-- ProcessHacker/srvprp.c | 50 +++++------- ProcessHacker/srvprv.c | 6 +- ProcessHacker/sysinfo.c | 2 +- ProcessHacker/sysscmem.c | 103 ++++++++++++------------ ProcessHacker/thrdprv.c | 11 ++- ProcessHacker/tokprp.c | 70 +++++++---------- phlib/global.c | 21 ++--- phlib/guisup.c | 15 +--- phlib/include/phconfig.h | 5 -- phlib/native.c | 140 ++++++++++----------------------- phlib/secdata.c | 6 +- phlib/treenew.c | 4 +- phlib/util.c | 87 ++++++-------------- phlib/verify.c | 6 +- phlib/workqueue.c | 35 ++++----- 36 files changed, 307 insertions(+), 672 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index b372627fd3a0..3566b045ab4e 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1827,60 +1827,6 @@ 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 @@ -2428,10 +2374,6 @@ BEGIN BOTTOMMARGIN, 175 END - IDD_SYSINFO_MEMPANELXP, DIALOG - BEGIN - END - IDD_MINIINFO, DIALOG BEGIN LEFTMARGIN, 4 diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index c9113390bc08..41b523b581cc 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -164,7 +164,7 @@ BOOLEAN PhpShowErrorAndElevateAction( )) return FALSE; - if (!WINDOWS_HAS_UAC || PhGetOwnTokenAttributes().Elevated) + if (PhGetOwnTokenAttributes().Elevated) return FALSE; elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); @@ -257,7 +257,7 @@ BOOLEAN PhpShowErrorAndConnectToPhSvc( )) return FALSE; - if (!WINDOWS_HAS_UAC || PhGetOwnTokenAttributes().Elevated) + if (PhGetOwnTokenAttributes().Elevated) return FALSE; elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index bc7c2af90f70..cbf30b17eb8a 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -439,7 +439,7 @@ static BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback( 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" + L"Thread is waiting for an ALPC port:\r\n" ); PhAppendStringBuilder( &context->StringBuilder, diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 2c222fa4f177..78eb2690f0c0 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -901,7 +901,7 @@ static NTSTATUS PhpFindObjectsThreadStart( PH_WORK_QUEUE workQueue; processHandleHashtable = PhCreateSimpleHashtable(8); - if (!KphIsConnected() && WindowsVersion >= WINDOWS_VISTA) + if (!KphIsConnected()) { useWorkQueue = TRUE; PhInitializeWorkQueue(&workQueue, 1, 20, 1000); diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index c3b16c7158b2..f30b4f664328 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -808,7 +808,7 @@ NTSTATUS PhpEnumHiddenProcessesBruteForce( } // Use an alternative method if we don't have sufficient access. - if (status2 == STATUS_ACCESS_DENIED && WindowsVersion >= WINDOWS_VISTA) + if (status2 == STATUS_ACCESS_DENIED) { if (NT_SUCCESS(status2 = PhGetProcessImageFileNameByProcessId(UlongToHandle(pid), &fileName))) { diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 5a21fe2d26f7..6a85795f1c24 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -487,7 +487,7 @@ VOID PhHandleProviderUpdate( ))) goto UpdateExit; - if (!KphIsConnected() && WindowsVersion >= WINDOWS_VISTA) + if (!KphIsConnected()) { useWorkQueue = TRUE; PhInitializeWorkQueue(&workQueue, 1, 20, 1000); diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 1941ca980cab..bd7355eb1b69 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -142,7 +142,7 @@ BOOLEAN PhMainWndInitialization( if (KphIsConnected()) PhAppendCharStringBuilder(&stringBuilder, '+'); } - if (WINDOWS_HAS_UAC && PhGetOwnTokenAttributes().ElevationType == TokenElevationTypeFull) + if (PhGetOwnTokenAttributes().ElevationType == TokenElevationTypeFull) PhAppendStringBuilder2(&stringBuilder, L" (Administrator)"); } @@ -175,8 +175,7 @@ BOOLEAN PhMainWndInitialization( windowRectangle.Width, windowRectangle.Height, FALSE); // Allow WM_PH_ACTIVATE to pass through UIPI. - if (WINDOWS_HAS_UAC) - ChangeWindowMessageFilter_I(WM_PH_ACTIVATE, MSGFLT_ADD); + ChangeWindowMessageFilter_I(WM_PH_ACTIVATE, MSGFLT_ADD); PhMwpOnSettingChange(); @@ -3103,15 +3102,12 @@ VOID PhAddMiniProcessMenuItems( // I/O priority - if (WindowsVersion >= WINDOWS_VISTA) - { - ioPriorityMenu = PhCreateEMenuItem(0, 0, L"I/O priority", NULL, ProcessId); + 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); - } + 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 diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 998ed6eeca73..14b155de1f6d 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -347,20 +347,6 @@ NTSTATUS PhpUpdateMemoryRegionTypes( { 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; @@ -437,27 +423,13 @@ NTSTATUS PhpUpdateMemoryRegionTypes( PVOID candidateHeap = NULL; ULONG candidateHeap32 = 0; PPH_MEMORY_ITEM heapMemoryItem; + PHEAP_SEGMENT heapSegment = (PHEAP_SEGMENT)buffer; + PHEAP_SEGMENT32 heapSegment32 = (PHEAP_SEGMENT32)buffer; - 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 (heapSegment->SegmentSignature == HEAP_SEGMENT_SIGNATURE) + candidateHeap = heapSegment->Heap; + if (heapSegment32->SegmentSignature == HEAP_SEGMENT_SIGNATURE) + candidateHeap32 = heapSegment32->Heap; if (candidateHeap) { diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 15df0c622cac..af8adadb31c9 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -747,15 +747,8 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( 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"); - } + if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) + PhInitializeStringRef(&getCellText->Text, L"ASLR"); break; case PHMOTLC_TIMESTAMP: { diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 30c04ac6af7b..de4854d285c3 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -111,15 +111,12 @@ PPH_MODULE_PROVIDER PhCreateModuleProvider( 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 - ); - } + // 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; } diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 11307c937294..cce1265a13b7 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -363,10 +363,16 @@ VOID PhMwpSetProcessMenuPriorityChecks( { if (SetPriority) { - NtQueryInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS), NULL); + NtQueryInformationProcess( + processHandle, + ProcessPriorityClass, + &priorityClass, + sizeof(PROCESS_PRIORITY_CLASS), + NULL + ); } - if (SetIoPriority && WindowsVersion >= WINDOWS_VISTA) + if (SetIoPriority) { if (!NT_SUCCESS(PhGetProcessIoPriority( processHandle, @@ -377,7 +383,7 @@ VOID PhMwpSetProcessMenuPriorityChecks( } } - if (SetPagePriority && WindowsVersion >= WINDOWS_VISTA) + if (SetPagePriority) { if (!NT_SUCCESS(PhGetProcessPagePriority( processHandle, @@ -545,17 +551,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) { @@ -643,13 +638,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( diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index bfc42aca3fb6..6c416a97f978 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -132,7 +132,7 @@ VOID PhInitializeNetworkTreeList( 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); + 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); @@ -538,10 +538,7 @@ BOOLEAN NTAPI PhpNetworkTreeNewCallback( 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 + getCellText->Text = PhGetStringRef(networkItem->OwnerName); break; case PHNETLC_TIMESTAMP: { diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 0dfe8e91674c..726d858b7797 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -103,7 +103,7 @@ VOID PhNfLoadStage2( { ULONG i; - PhNfMiniInfoEnabled = WindowsVersion >= WINDOWS_VISTA && !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); + PhNfMiniInfoEnabled = !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); for (i = PH_ICON_MINIMUM; i != PhNfMaximumIconId; i <<= 1) { @@ -558,11 +558,8 @@ BOOLEAN PhNfpAddNotifyIcon( Shell_NotifyIcon(NIM_ADD, ¬ifyIcon); - if (WindowsVersion >= WINDOWS_VISTA) - { - notifyIcon.uVersion = NOTIFYICON_VERSION_4; - Shell_NotifyIcon(NIM_SETVERSION, ¬ifyIcon); - } + notifyIcon.uVersion = NOTIFYICON_VERSION_4; + Shell_NotifyIcon(NIM_SETVERSION, ¬ifyIcon); return TRUE; } diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index a0a9355a3603..5235537c7e18 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -388,10 +388,7 @@ static VOID PhpRefreshMutantPageInfo( SetDlgItemText(hwndDlg, IDC_ABANDONED, L"Unknown"); } - if ( - WindowsVersion >= WINDOWS_VISTA && - NT_SUCCESS(PhGetMutantOwnerInformation(mutantHandle, &ownerInfo)) - ) + if (NT_SUCCESS(PhGetMutantOwnerInformation(mutantHandle, &ownerInfo))) { PPH_STRING name; @@ -433,12 +430,6 @@ INT_PTR CALLBACK PhpMutantPageProc( { case WM_INITDIALOG: { - if (WindowsVersion < WINDOWS_VISTA) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_OWNERLABEL), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_OWNER), FALSE); - } - PhpRefreshMutantPageInfo(hwndDlg, pageContext); } break; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 510b16268626..9f21779c5f5b 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1072,32 +1072,29 @@ VOID PhpProcessQueryStage1( // Token information if (processHandleLimited) { - if (WINDOWS_HAS_UAC) - { - HANDLE tokenHandle; + HANDLE tokenHandle; - status = PhOpenProcessToken(processHandleLimited, TOKEN_QUERY, &tokenHandle); + status = PhOpenProcessToken(processHandleLimited, TOKEN_QUERY, &tokenHandle); - if (NT_SUCCESS(status)) + if (NT_SUCCESS(status)) + { + // Elevation + if (NT_SUCCESS(PhGetTokenElevationType( + tokenHandle, + &Data->ElevationType + ))) { - // Elevation - if (NT_SUCCESS(PhGetTokenElevationType( - tokenHandle, - &Data->ElevationType - ))) - { - Data->IsElevated = Data->ElevationType == TokenElevationTypeFull; - } + Data->IsElevated = Data->ElevationType == TokenElevationTypeFull; + } - // Integrity - PhGetTokenIntegrityLevel( - tokenHandle, - &Data->IntegrityLevel, - &Data->IntegrityString - ); + // Integrity + PhGetTokenIntegrityLevel( + tokenHandle, + &Data->IntegrityLevel, + &Data->IntegrityString + ); - NtClose(tokenHandle); - } + NtClose(tokenHandle); } } @@ -1360,24 +1357,13 @@ VOID PhpFillProcessItem( { PPH_STRING fileName; - if (WindowsVersion >= WINDOWS_VISTA) + if (processHandle) { - if (processHandle) - { - PhGetProcessImageFileNameWin32(processHandle, &ProcessItem->FileName); - } - else - { - if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessItem->ProcessId, &fileName))) - { - ProcessItem->FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - } - } + PhGetProcessImageFileNameWin32(processHandle, &ProcessItem->FileName); } else { - if (processHandle && NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName))) + if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessItem->ProcessId, &fileName))) { ProcessItem->FileName = PhGetFileName(fileName); PhDereferenceObject(fileName); @@ -2241,7 +2227,7 @@ VOID PhProcessProviderUpdate( { PhOpenProcess(&processItem->QueryHandle, PROCESS_QUERY_INFORMATION, processItem->ProcessId); - if (WINDOWS_HAS_LIMITED_ACCESS && !processItem->QueryHandle) + if (!processItem->QueryHandle) PhOpenProcess(&processItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, processItem->ProcessId); } diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index b27671f789fb..10d78dc82b86 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -949,7 +949,7 @@ static VOID PhpUpdateProcessNodeToken( ProcessNode->VirtualizationAllowed = FALSE; ProcessNode->VirtualizationEnabled = FALSE; - if (WINDOWS_HAS_UAC && ProcessNode->ProcessItem->QueryHandle) + if (ProcessNode->ProcessItem->QueryHandle) { if (NT_SUCCESS(PhOpenProcessToken( ProcessNode->ProcessItem->QueryHandle, @@ -2359,16 +2359,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( 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"); - } + if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) + PhInitializeStringRef(&getCellText->Text, L"ASLR"); break; case PHPRTLC_RELATIVESTARTTIME: { @@ -2397,27 +2389,20 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( { PWSTR type; - if (WINDOWS_HAS_UAC) + switch (processItem->ElevationType) { - 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""; + 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); diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 76318e4492e0..0fc4508d5593 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -344,7 +344,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( SetDlgItemText(hwndDlg, IDC_PROTECTION, L"N/A"); - if (WINDOWS_HAS_LIMITED_ACCESS && processHandle) + if (processHandle) { if (WindowsVersion >= WINDOWS_8_1) { diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 97b0127b4ea9..cb2ef363d4b5 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -123,18 +123,14 @@ VOID PhpUpdateProcessStatistics( 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); - } + PhGetProcessPagePriority(ProcessItem->QueryHandle, &pagePriority); + PhGetProcessIoPriority(ProcessItem->QueryHandle, &ioPriority); } if (Context->ProcessHandle) @@ -165,26 +161,17 @@ VOID PhpUpdateProcessStatistics( 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")); + SetDlgItemText(hwndDlg, IDC_ZCYCLES_V, PhGetStringOrDefault(cycles, L"Unknown")); - if (WindowsVersion >= WINDOWS_VISTA) - { - 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"); - } + if (pagePriority != -1 && pagePriority <= MEMORY_PRIORITY_NORMAL) + SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, PhPagePriorityNames[pagePriority]); else - { - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, L"N/A"); - } + 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"); SetDlgItemText(hwndDlg, IDC_ZPRIVATEWS_V, PhGetStringOrDefault(privateWs, L"Unknown")); SetDlgItemText(hwndDlg, IDC_ZSHAREABLEWS_V, PhGetStringOrDefault(shareableWs, L"Unknown")); diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index cf23dd2cccc2..35601d2d7fdf 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -102,8 +102,6 @@ VOID PhpInitializeThreadMenu( _In_ ULONG NumberOfThreads ) { - PPH_EMENU_ITEM item; - if (NumberOfThreads == 0) { PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); @@ -133,18 +131,6 @@ 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 @@ -163,32 +149,25 @@ VOID PhpInitializeThreadMenu( ))) { THREAD_BASIC_INFORMATION basicInfo; + HANDLE tokenHandle; if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo))) { threadPriority = basicInfo.BasePriority; } - if (WindowsVersion >= WINDOWS_VISTA) - { - PhGetThreadIoPriority(threadHandle, &ioPriority); - PhGetThreadPagePriority(threadHandle, &pagePriority); - } + PhGetThreadIoPriority(threadHandle, &ioPriority); + PhGetThreadPagePriority(threadHandle, &pagePriority); - // Token + 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); @@ -363,9 +342,7 @@ VOID PhpUpdateThreadDetails( PhPrintTimeSpan(userTime, threadItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); contextSwitches = PhaFormatUInt64(threadItem->ContextSwitchesDelta.Value, TRUE); - - if (WINDOWS_HAS_CYCLE_TIME) - cycles = PhaFormatUInt64(threadItem->CyclesDelta.Value, TRUE); + cycles = PhaFormatUInt64(threadItem->CyclesDelta.Value, TRUE); if (threadItem->State != Waiting) { @@ -588,7 +565,6 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( // 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) { HANDLE processHandle; @@ -619,7 +595,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); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index b8c58833da9e..82fb961b55c9 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -109,7 +109,6 @@ #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 diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 6abbef469ff3..c765e6293a38 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -349,9 +349,6 @@ INT_PTR CALLBACK PhpRunAsDlgProc( //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: @@ -407,11 +404,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( 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; + useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED; if (PhFindIntegerSiKeyValuePairs( PhpLogonTypePairs, @@ -444,8 +437,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( 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))) + if (desktopName->Length != 0 && !PhEqualString2(desktopName, L"WinSta0\\Default", TRUE)) createInfo.DesktopName = desktopName->Buffer; PhSetDesktopWinStaAccess(); diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index 658efd1f439c..17508959dcc5 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -174,10 +174,7 @@ static VOID PhpRefreshControls( _In_ HWND hwndDlg ) { - if ( - WindowsVersion >= WINDOWS_VISTA && - PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_STARTTYPE), L"Auto start", FALSE) - ) + if (PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_STARTTYPE), L"Auto start", FALSE)) { EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), TRUE); } @@ -254,10 +251,7 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( PhDereferenceObject(description); } - if ( - WindowsVersion >= WINDOWS_VISTA && - PhGetServiceDelayedAutoStart(serviceHandle, &delayedStart) - ) + if (PhGetServiceDelayedAutoStart(serviceHandle, &delayedStart)) { context->OldDelayedStart = delayedStart; @@ -460,16 +454,13 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( NULL )) { - if (WindowsVersion >= WINDOWS_VISTA) - { - BOOLEAN newDelayedStart; + BOOLEAN newDelayedStart; - newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; + newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; - if (newDelayedStart != context->OldDelayedStart) - { - PhSetServiceDelayedAutoStart(serviceHandle, newDelayedStart); - } + if (newDelayedStart != context->OldDelayedStart) + { + PhSetServiceDelayedAutoStart(serviceHandle, newDelayedStart); } PhMarkNeedsConfigUpdateServiceItem(serviceItem); @@ -503,23 +494,20 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( NULL ))) { - if (WindowsVersion >= WINDOWS_VISTA) - { - BOOLEAN newDelayedStart; - - newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; + BOOLEAN newDelayedStart; - if (newDelayedStart != context->OldDelayedStart) - { - SERVICE_DELAYED_AUTO_START_INFO info; + newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; - info.fDelayedAutostart = newDelayedStart; - PhSvcCallChangeServiceConfig2( - serviceItem->Name->Buffer, - SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - &info - ); - } + 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); diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 18f258c7aa3d..22ae892d9122 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -463,11 +463,7 @@ VOID PhServiceProviderUpdate( { if (!PhpNonPollInitialized) { - if (WindowsVersion >= WINDOWS_VISTA) - { - PhpInitializeServiceNonPoll(); - } - + PhpInitializeServiceNonPoll(); PhpNonPollInitialized = TRUE; } diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 889066c520c7..1f6184e83044 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -428,7 +428,7 @@ VOID PhSipOnShowWindow( MinimumSize.left = 0; MinimumSize.top = 0; - MinimumSize.right = WindowsVersion >= WINDOWS_VISTA ? 430 : 370; // XP doesn't have the Memory Lists group + MinimumSize.right = 430; MinimumSize.bottom = 290; MapDialogRect(PhSipWindow, &MinimumSize); diff --git a/ProcessHacker/sysscmem.c b/ProcessHacker/sysscmem.c index f375ad8fb9d7..f17d5ad36e05 100644 --- a/ProcessHacker/sysscmem.c +++ b/ProcessHacker/sysscmem.c @@ -320,7 +320,7 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( MemoryPanel = CreateDialog( PhInstanceHandle, - WindowsVersion >= WINDOWS_VISTA ? MAKEINTRESOURCE(IDD_SYSINFO_MEMPANEL) : MAKEINTRESOURCE(IDD_SYSINFO_MEMPANELXP), + MAKEINTRESOURCE(IDD_SYSINFO_MEMPANEL), hwndDlg, PhSipMemoryPanelDialogProc ); @@ -777,64 +777,61 @@ VOID PhSipUpdateMemoryPanel( // 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"); + 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_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"); - } + } + else + { + 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"); } } diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 8f7a60b85b6a..a0cf4bee575b 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -538,7 +538,7 @@ NTSTATUS PhpThreadQueryWorker( // 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) + if (!data->ThreadProvider->HasServicesKnown) { PPH_PROCESS_ITEM processItem; @@ -552,9 +552,10 @@ NTSTATUS PhpThreadQueryWorker( } // Get the service tag, and the service name. - if (WINDOWS_HAS_SERVICE_TAGS && + if ( data->ThreadProvider->SymbolProvider->IsRealHandle && - data->ThreadItem->ThreadHandle) + data->ThreadItem->ThreadHandle + ) { PVOID serviceTag; @@ -869,7 +870,6 @@ VOID PhpThreadProviderUpdate( } // Get the cycle count. - if (WINDOWS_HAS_CYCLE_TIME) { ULONG64 cycles; @@ -1027,7 +1027,6 @@ VOID PhpThreadProviderUpdate( } // Update the cycle count. - if (WINDOWS_HAS_CYCLE_TIME) { ULONG64 cycles; ULONG64 oldDelta; @@ -1055,7 +1054,7 @@ VOID PhpThreadProviderUpdate( // 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)) + if (PhEnableCycleCpuUsage && (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID || threadItem->ThreadHandle)) { threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta; } diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 6a0f419656db..d5b6caa775db 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -450,9 +450,6 @@ INT_PTR CALLBACK PhpTokenPageProc( 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, @@ -490,34 +487,26 @@ INT_PTR CALLBACK PhpTokenPageProc( 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(PhGetTokenElevationType(tokenHandle, &elevationType))) + SetDlgItemText(hwndDlg, IDC_ELEVATED, PhGetElevationTypeString(elevationType)); - if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + { + if (isVirtualizationAllowed) { - if (isVirtualizationAllowed) + if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) { - if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) - { - SetDlgItemText( - hwndDlg, - IDC_VIRTUALIZED, - isVirtualizationEnabled ? L"Yes" : L"No" - ); - } - } - else - { - SetDlgItemText(hwndDlg, IDC_VIRTUALIZED, L"Not allowed"); + SetDlgItemText( + hwndDlg, + IDC_VIRTUALIZED, + isVirtualizationEnabled ? L"Yes" : L"No" + ); } } - } - else - { - SetDlgItemText(hwndDlg, IDC_ELEVATED, L"N/A"); - SetDlgItemText(hwndDlg, IDC_VIRTUALIZED, L"N/A"); + else + { + SetDlgItemText(hwndDlg, IDC_VIRTUALIZED, L"Not allowed"); + } } if (WINDOWS_HAS_IMMERSIVE) @@ -1119,28 +1108,25 @@ INT_PTR CALLBACK PhpTokenGeneralPageProc( PhGetTokenSessionId(tokenHandle, &tokenSessionId); - if (WINDOWS_HAS_UAC) + if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) { - if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) - { - tokenElevated = PhGetElevationTypeString(elevationType); - hasLinkedToken = elevationType != TokenElevationTypeDefault; - } + tokenElevated = PhGetElevationTypeString(elevationType); + hasLinkedToken = elevationType != TokenElevationTypeDefault; + } - if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + { + if (isVirtualizationAllowed) { - if (isVirtualizationAllowed) + if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) { - if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) - { - tokenVirtualization = isVirtualizationEnabled ? L"Enabled" : L"Disabled"; - } - } - else - { - tokenVirtualization = L"Not Allowed"; + tokenVirtualization = isVirtualizationEnabled ? L"Enabled" : L"Disabled"; } } + else + { + tokenVirtualization = L"Not Allowed"; + } } NtClose(tokenHandle); diff --git a/phlib/global.c b/phlib/global.c index fb8a44453ab0..004986fdc189 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -220,20 +220,9 @@ static VOID PhInitializeWindowsVersion( 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; - } + 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; } diff --git a/phlib/guisup.c b/phlib/guisup.c index c8f8f6e3298e..c7d3d1143d75 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -50,8 +50,7 @@ VOID PhGuiSupportInitialization( shell32Handle = LoadLibrary(L"shell32.dll"); shlwapiHandle = LoadLibrary(L"shlwapi.dll"); - if (WINDOWS_HAS_UAC) - ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter"); + ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter"); if (WINDOWS_HAS_IMMERSIVE) IsImmersiveProcess_I = PhGetModuleProcAddress(L"user32.dll", "IsImmersiveProcess"); RunFileDlg = PhGetProcedureAddress(shell32Handle, NULL, 61); @@ -63,10 +62,7 @@ VOID PhSetControlTheme( _In_ PWSTR Theme ) { - if (WindowsVersion >= WINDOWS_VISTA) - { - SetWindowTheme(Handle, Theme, NULL); - } + SetWindowTheme(Handle, Theme, NULL); } INT PhAddListViewColumn( @@ -694,16 +690,11 @@ VOID PhGetStockApplicationIcon( PhInitializeStringRef(&dllBaseName, L"\\imageres.dll"); index = 11; } - else if (WindowsVersion >= WINDOWS_VISTA) + else { PhInitializeStringRef(&dllBaseName, L"\\user32.dll"); index = 0; } - else - { - PhInitializeStringRef(&dllBaseName, L"\\shell32.dll"); - index = 2; - } dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName); PhDereferenceObject(systemDirectory); diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 75892b856fe8..6da596042c2f 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -33,13 +33,8 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_NEW MAXLONG #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 diff --git a/phlib/native.c b/phlib/native.c index d00fdb099868..2ce8abb501dd 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -87,11 +87,8 @@ PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( BOOLEAN elevated = TRUE; TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; - if (WINDOWS_HAS_UAC) - { - PhGetTokenIsElevated(attributes.TokenHandle, &elevated); - PhGetTokenElevationType(attributes.TokenHandle, &elevationType); - } + PhGetTokenIsElevated(attributes.TokenHandle, &elevated); + PhGetTokenElevationType(attributes.TokenHandle, &elevationType); attributes.Elevated = elevated; attributes.ElevationType = elevationType; @@ -1319,39 +1316,19 @@ NTSTATUS PhInjectDllProcess( ))) 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; - } - } + if (!NT_SUCCESS(status = RtlCreateUserThread( + ProcessHandle, + NULL, + FALSE, + 0, + 0, + 0, + (PUSER_THREAD_START_ROUTINE)threadStart, + baseAddress, + &threadHandle, + NULL + ))) + goto FreeExit; // Wait for the thread to finish. status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); @@ -1465,36 +1442,18 @@ NTSTATUS PhUnloadDllProcess( } #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(); - } - } + status = RtlCreateUserThread( + ProcessHandle, + NULL, + FALSE, + 0, + 0, + 0, + (PUSER_THREAD_START_ROUTINE)threadStart, + BaseAddress, + &threadHandle, + NULL + ); if (!NT_SUCCESS(status)) return status; @@ -1639,39 +1598,20 @@ NTSTATUS PhSetEnvironmentVariableRemote( } } - 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 (!NT_SUCCESS(status = RtlCreateUserThread( + ProcessHandle, + NULL, + TRUE, + 0, + 0, + 0, + (PUSER_THREAD_START_ROUTINE)rtlExitUserThread, + NULL, + &threadHandle, + NULL + ))) { - if (!(threadHandle = CreateRemoteThread( - ProcessHandle, - NULL, - 0, - (PTHREAD_START_ROUTINE)rtlExitUserThread, - NULL, - CREATE_SUSPENDED, - NULL - ))) - { - status = PhGetLastWin32ErrorAsNtStatus(); - goto CleanupExit; - } + goto CleanupExit; } #ifdef _WIN64 diff --git a/phlib/secdata.c b/phlib/secdata.c index d87f74df6a5e..c4f4706c4f92 100644 --- a/phlib/secdata.c +++ b/phlib/secdata.c @@ -645,13 +645,11 @@ BOOLEAN PhGetAccessEntries( } else if (PhEqualStringZ(Type, L"Process", TRUE)) { - if (WindowsVersion >= WINDOWS_VISTA) - Type = L"Process60"; + Type = L"Process60"; } else if (PhEqualStringZ(Type, L"Thread", TRUE)) { - if (WindowsVersion >= WINDOWS_VISTA) - Type = L"Thread60"; + Type = L"Thread60"; } // Find the specific type. diff --git a/phlib/treenew.c b/phlib/treenew.c index c3e63846612b..6466a00bae9f 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -2049,13 +2049,13 @@ VOID PhTnpUpdateTextMetrics( } else { - if (WindowsVersion >= WINDOWS_VISTA && !(Context->Style & TN_STYLE_THIN_ROWS)) + if (!(Context->Style & TN_STYLE_THIN_ROWS)) Context->RowHeight += 1; // HACK } Context->RowHeight += 1; // HACK - if (WindowsVersion >= WINDOWS_VISTA && !(Context->Style & TN_STYLE_THIN_ROWS)) + if (!(Context->Style & TN_STYLE_THIN_ROWS)) Context->RowHeight += 2; // HACK } diff --git a/phlib/util.c b/phlib/util.c index 7e84cbb10dad..27ce988d2135 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -41,9 +41,6 @@ #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, @@ -2849,15 +2846,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; @@ -3154,7 +3144,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); @@ -3287,7 +3276,7 @@ BOOLEAN PhShellExecuteEx( 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 (ShellExecuteEx(&info)) @@ -3418,11 +3407,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; } @@ -3733,31 +3718,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; } /** @@ -3770,31 +3745,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; } /** diff --git a/phlib/verify.c b/phlib/verify.c index 518076a6971f..1118bc495bf8 100644 --- a/phlib/verify.c +++ b/phlib/verify.c @@ -217,11 +217,7 @@ VERIFY_RESULT PhpVerifyFile( 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; + trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; } status = WinVerifyTrust_I(NULL, ActionId, &trustData); diff --git a/phlib/workqueue.c b/phlib/workqueue.c index b0f39740670f..2ccc3495e626 100644 --- a/phlib/workqueue.c +++ b/phlib/workqueue.c @@ -267,32 +267,29 @@ VOID PhpUpdateWorkQueueEnvironment( } } - if (WindowsVersion >= WINDOWS_VISTA) + if (CurrentEnvironment->IoPriority != NewEnvironment->IoPriority || NewEnvironment->ForceUpdate) { - if (CurrentEnvironment->IoPriority != NewEnvironment->IoPriority || NewEnvironment->ForceUpdate) - { - IO_PRIORITY_HINT ioPriority; + IO_PRIORITY_HINT ioPriority; - ioPriority = NewEnvironment->IoPriority; + ioPriority = NewEnvironment->IoPriority; - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, - &ioPriority, sizeof(IO_PRIORITY_HINT)))) - { - CurrentEnvironment->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; + if (CurrentEnvironment->PagePriority != NewEnvironment->PagePriority || NewEnvironment->ForceUpdate) + { + ULONG pagePriority; - pagePriority = NewEnvironment->PagePriority; + pagePriority = NewEnvironment->PagePriority; - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadPagePriority, - &pagePriority, sizeof(ULONG)))) - { - CurrentEnvironment->PagePriority = NewEnvironment->PagePriority; - } + if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadPagePriority, + &pagePriority, sizeof(ULONG)))) + { + CurrentEnvironment->PagePriority = NewEnvironment->PagePriority; } } } From 43ef233feca961170e641bd87d27383cb069f548 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 11 Jun 2017 06:12:34 +1000 Subject: [PATCH 212/839] ToolStatus: Remove XP support --- plugins/ToolStatus/filter.c | 2 +- plugins/ToolStatus/toolbar.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ToolStatus/filter.c b/plugins/ToolStatus/filter.c index 3e89fa21b0d3..4a20fa9d329b 100644 --- a/plugins/ToolStatus/filter.c +++ b/plugins/ToolStatus/filter.c @@ -201,7 +201,7 @@ BOOLEAN ProcessTreeFilterCallback( } } - if (WINDOWS_HAS_UAC && processNode->ProcessItem->ElevationType != TokenElevationTypeDefault) + if (processNode->ProcessItem->ElevationType != TokenElevationTypeDefault) { switch (processNode->ProcessItem->ElevationType) { diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index c7b752460c7b..018cd2b6380f 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -318,7 +318,7 @@ VOID ToolbarLoadSettings( { case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: { - if (WINDOWS_HAS_UAC && PhGetOwnTokenAttributes().Elevated) + if (PhGetOwnTokenAttributes().Elevated) { buttonInfo.fsState |= TBSTATE_HIDDEN; } From af3d39c918b95bb6f7bcfec52df15d47481fb17a Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 13 Jun 2017 03:09:50 +1000 Subject: [PATCH 213/839] [Setup] Fix minimum OS version --- .../CustomSetupTool/CustomSetupTool.vcxproj | 3 +- .../CustomSetupTool/CustomSetupTool/appsup.c | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 888e8bbaa14f..02cea7f702b1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -68,6 +68,7 @@ $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows RequireAdministrator + 6.01 _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -97,7 +98,7 @@ uxtheme.lib;winhttp.lib;phlib.lib;ntdll.lib;%(AdditionalDependencies) $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows - 5.01 + 6.01 true RequireAdministrator diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 5e5d174eb8b7..aeaf18ed86e6 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -470,31 +470,31 @@ 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; - - while (windowHandle = FindWindow(L"ProcessHacker", NULL)) + ULONG64 processId64; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + + if ( + PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && + PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && + PhStringToInteger64(&secondPart, 10, &processId64) + ) { - GetWindowThreadProcessId(windowHandle, &processID); + HANDLE processHandle; if (NT_SUCCESS(PhOpenProcess( &processHandle, SYNCHRONIZE | PROCESS_TERMINATE, - ULongToHandle(processID) + ULongToHandle((ULONG)processId64) ))) { - PostMessage(windowHandle, WM_QUIT, 0, 0); - - // Wait for process exit. - if (WaitForSingleObject(processHandle, 10 * 1000) != WAIT_OBJECT_0) - { - // Timed out, kill the process. - NtTerminateProcess(processHandle, 1); - } - + NtTerminateProcess(processHandle, 1); NtClose(processHandle); } } @@ -502,6 +502,13 @@ BOOLEAN ShutdownProcessHacker(VOID) return TRUE; } +BOOLEAN ShutdownProcessHacker(VOID) +{ + PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); + + return TRUE; +} + BOOLEAN CreateDirectoryPath(_In_ PWSTR DirPath) { BOOLEAN success = FALSE; From 9927969e2dbbc7a07a07bc782c6388433678ecc7 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 13 Jun 2017 03:12:34 +1000 Subject: [PATCH 214/839] Plugins: Remove legacy Vista support --- plugins/ExtendedServices/main.c | 4 ++-- plugins/ExtendedServices/recovery.c | 14 +++++--------- plugins/ExtraPlugins/setup/updater.c | 1 - plugins/NetworkTools/update.c | 2 -- plugins/Updater/updater.c | 2 -- plugins/WindowExplorer/hook.c | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index c36e3b8f38f7..af0e8f012988 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -348,7 +348,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); @@ -362,7 +362,7 @@ NTAPI ServicePropertiesInitializingCallback( } // Other - if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index 91f42768cefe..47a55520c2f0 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -213,8 +213,7 @@ NTSTATUS EspLoadRecoveryInfo( // Enable actions for stops with errors - // This is Vista and above only. - if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2( + if (QueryServiceConfig2( serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (BYTE *)&failureActionsFlag, @@ -319,13 +318,10 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( else if (!NT_SUCCESS(status)) { SetDlgItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0"); - - if (WindowsVersion >= WINDOWS_VISTA) - { - context->EnableFlagCheckBox = TRUE; - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE); - } - + + 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); } diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 19bd41c7b45c..f8fdf0111cad 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -451,7 +451,6 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 579feb6015cc..7e9cd5e07cc2 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -198,7 +198,6 @@ PPH_STRING QueryFwLinkUrl( ); } - if (WindowsVersion >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_REDIRECTS; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); @@ -428,7 +427,6 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) { ULONG option = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 65ffb77af54e..29b737f7c63d 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -364,7 +364,6 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); @@ -805,7 +804,6 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) { ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); diff --git a/plugins/WindowExplorer/hook.c b/plugins/WindowExplorer/hook.c index abecae533993..3bd85bd6fa62 100644 --- a/plugins/WindowExplorer/hook.c +++ b/plugins/WindowExplorer/hook.c @@ -193,7 +193,6 @@ BOOLEAN WepCreateServerObjects( } // 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; From 4e89d8f121481fc9f51519b8f223aa0dbfe1e51a Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 13 Jun 2017 03:19:25 +1000 Subject: [PATCH 215/839] ExtendedTools: Fix unloaded modules window flicker --- plugins/ExtendedTools/unldll.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 8a0f5a597649..f7643e977197 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -78,7 +78,6 @@ BOOLEAN EtpRefreshUnloadedDlls( HWND lvHandle; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - ListView_DeleteAllItems(lvHandle); RtlGetUnloadEventTraceEx(&elementSize, &elementCount, &eventTrace); @@ -145,6 +144,8 @@ BOOLEAN EtpRefreshUnloadedDlls( ExtendedListView_SetRedraw(lvHandle, FALSE); + ListView_DeleteAllItems(lvHandle); + for (i = 0; i < capturedElementCount; i++) { PRTL_UNLOAD_EVENT_TRACE rtlEvent = currentEvent; From 8cdd6d81721e240458d15d2918200ab894735563 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Jun 2017 09:06:46 +1000 Subject: [PATCH 216/839] Remove legacy Vista support --- ProcessHacker/ProcessHacker.rc | 1 - ProcessHacker/actions.c | 20 +----- ProcessHacker/chproc.c | 8 +-- ProcessHacker/hndlprv.c | 105 +++++++------------------------ ProcessHacker/main.c | 3 - ProcessHacker/mainwnd.c | 12 ---- ProcessHacker/memprv.c | 7 +-- ProcessHacker/mwpgnet.c | 6 -- ProcessHacker/mwpgproc.c | 2 +- ProcessHacker/netprv.c | 13 ---- ProcessHacker/options.c | 13 +--- ProcessHacker/procprv.c | 49 +++------------ ProcessHacker/proctree.c | 112 ++++++++++++--------------------- ProcessHacker/prpgstat.c | 41 +++++------- ProcessHacker/resource.h | 1 - ProcessHacker/runas.c | 14 +---- ProcessHacker/syssccpu.c | 18 +++--- phlib/global.c | 17 +---- phlib/hndlinfo.c | 38 +++-------- phlib/include/phconfig.h | 3 - phlib/native.c | 41 +++++------- 21 files changed, 124 insertions(+), 400 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 3566b045ab4e..d08e9ed52974 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -383,7 +383,6 @@ BEGIN 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 diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 41b523b581cc..b822fd9f75a8 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -726,7 +726,6 @@ static BOOLEAN PhpIsDangerousProcess( ) { NTSTATUS status; - HANDLE processHandle; PPH_STRING fileName; PPH_STRING systemDirectory; ULONG i; @@ -734,24 +733,7 @@ static BOOLEAN PhpIsDangerousProcess( 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)) + if (!NT_SUCCESS(status = PhGetProcessImageFileNameByProcessId(ProcessId, &fileName))) return FALSE; PhMoveReference(&fileName, PhGetFileName(fileName)); diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index 4b83a8f176eb..a39911ba828e 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -120,9 +120,6 @@ static VOID PhpRefreshProcessList( 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))) @@ -140,11 +137,10 @@ static VOID PhpRefreshProcessList( 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(); + else + PhGetProcessImageFileNameByProcessId(process->UniqueProcessId, &fileName); if (fileName) PhMoveReference(&fileName, PhGetFileName(fileName)); diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 6a85795f1c24..8b7707c0f154 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -315,105 +315,44 @@ NTSTATUS PhEnumHandlesGeneric( // 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 - ); + if (!NT_SUCCESS(status = KphEnumerateProcessHandles2(ProcessHandle, &handles))) + return status; - convertedHandles->NumberOfHandles = handles->HandleCount; + convertedHandles = PhAllocate( + FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + + sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * 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; - } + convertedHandles->NumberOfHandles = handles->HandleCount; - PhFree(handles); + 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; + } - *Handles = convertedHandles; - *FilterNeeded = FALSE; + PhFree(handles); - return status; - } + *Handles = convertedHandles; + *FilterNeeded = FALSE; } - - if (WindowsVersion >= WINDOWS_XP) + else { 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; + return status; } NTSTATUS PhpCreateHandleItemFunction( diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index d13513da6754..d972aac56428 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -592,9 +592,6 @@ VOID PhInitializeKph( PUCHAR signature; ULONG signatureSize; - if (WindowsVersion < WINDOWS_7) - return; - kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index bd7355eb1b69..cb6592fbca56 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1520,18 +1520,6 @@ VOID PhMwpOnCommand( } } 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; diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 14b155de1f6d..0cd374fce86d 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -780,12 +780,7 @@ NTSTATUS PhQueryMemoryItemList( PhpUpdateMemoryRegionTypes(List, processHandle); if (Flags & PH_QUERY_MEMORY_WS_COUNTERS) - { - if (WindowsVersion >= WINDOWS_SERVER_2003) - PhpUpdateMemoryWsCounters(List, processHandle); - else - PhpUpdateMemoryWsCountersOld(List, processHandle); - } + PhpUpdateMemoryWsCounters(List, processHandle); NtClose(processHandle); diff --git a/ProcessHacker/mwpgnet.c b/ProcessHacker/mwpgnet.c index c8fa1d5cd18f..68832d0a5438 100644 --- a/ProcessHacker/mwpgnet.c +++ b/ProcessHacker/mwpgnet.c @@ -221,12 +221,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) { diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index cce1265a13b7..619dbe6c12ee 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -123,7 +123,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; diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index d3cc1187db6a..48f5b77c72f9 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -814,19 +814,6 @@ BOOLEAN PhGetNetworkConnections( 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) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index c93fcbef6a06..988c32d5a3cb 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -680,9 +680,7 @@ VOID PhpAdvancedPageLoad( 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"); + SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); SetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, PhGetIntegerSetting(L"SampleCount"), FALSE); SetDlgItemCheckForSetting(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); @@ -762,9 +760,7 @@ VOID PhpAdvancedPageSave( 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"); + SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); sampleCount = GetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, NULL, FALSE); SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); @@ -874,11 +870,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( 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: diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 9f21779c5f5b..ebbff15f1ab8 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1151,7 +1151,7 @@ VOID PhpProcessQueryStage1( } // Console host process - if (processHandleLimited && WINDOWS_HAS_CONSOLE_HOST) + if (processHandleLimited) { PhGetProcessConsoleHostProcessId(processHandleLimited, &Data->ConsoleHostProcessId); } @@ -1423,35 +1423,6 @@ VOID PhpFillProcessItem( } } - 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); } @@ -1951,8 +1922,6 @@ VOID PhProcessProviderUpdate( 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 @@ -1974,8 +1943,6 @@ VOID PhProcessProviderUpdate( PhPurgeProcessRecords(); } - isCycleCpuUsageEnabled = WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage; - if (!PhProcessStatisticsInitialized) { PhpInitializeProcessStatistics(); @@ -1984,7 +1951,7 @@ VOID PhProcessProviderUpdate( PhpUpdatePerfInformation(); - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) { PhpUpdateCpuInformation(FALSE, &sysTotalTime); PhpUpdateCpuCycleInformation(&sysIdleCycleTime); @@ -2053,7 +2020,7 @@ VOID PhProcessProviderUpdate( process->UniqueProcessKey = (ULONG_PTR)pidBuckets[bucketIndex]; pidBuckets[bucketIndex] = process; - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) { PPH_PROCESS_ITEM processItem; @@ -2069,7 +2036,7 @@ VOID PhProcessProviderUpdate( // 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) + if (PhEnableCycleCpuUsage) { PhInterruptsProcessInformation.KernelTime.QuadPart = PhCpuTotals.DpcTime.QuadPart + PhCpuTotals.InterruptTime.QuadPart; PhInterruptsProcessInformation.CycleTime = PhCpuSystemCycleDelta.Value; @@ -2131,7 +2098,7 @@ VOID PhProcessProviderUpdate( exitTime = times.ExitTime; } - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) { if (NT_SUCCESS(PhGetProcessCycleTime(processItem->QueryHandle, &finalCycleTime))) { @@ -2324,7 +2291,7 @@ VOID PhProcessProviderUpdate( if (InterlockedExchange(&processItem->JustProcessed, 0) != 0) modified = TRUE; - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) { FLOAT totalDelta; @@ -2464,7 +2431,7 @@ VOID PhProcessProviderUpdate( if (process == NULL) { - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) process = &PhInterruptsProcessInformation; else process = &PhDpcsProcessInformation; @@ -2489,7 +2456,7 @@ VOID PhProcessProviderUpdate( // I/O "deltas" will be huge because they are currently the raw accumulated values. if (runCount != 0) { - if (isCycleCpuUsageEnabled) + if (PhEnableCycleCpuUsage) PhpUpdateCpuCycleUsageInformation(sysTotalCycleTime, sysIdleCycleTime); PhpUpdateSystemHistory(); diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 10d78dc82b86..7a9a18156528 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -427,7 +427,7 @@ PPH_PROCESS_NODE PhAddProcessNode( } } - if (WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage && ProcessItem->ProcessId == INTERRUPTS_PROCESS_ID) + if (PhEnableCycleCpuUsage && ProcessItem->ProcessId == INTERRUPTS_PROCESS_ID) PhInitializeStringRef(&processNode->DescriptionText, L"Interrupts and DPCs"); if (FilterSupport.FilterList) @@ -982,28 +982,25 @@ static VOID PhpUpdateProcessOsContext( { HANDLE processHandle; - if (WindowsVersion >= WINDOWS_7) + if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) { - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) + if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid))) { - 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); + 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; @@ -1104,15 +1101,8 @@ static VOID PhpUpdateProcessNodeAppId( 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 - { + if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessNode->ProcessId))) goto Done; - } } if (NT_SUCCESS(PhGetProcessWindowTitle( @@ -2026,12 +2016,10 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( 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); - } + // TODO: Remove this. + sortFunctions[PHPRTLC_PRIVATEWS] = SORT_FUNCTION(PrivateWsWin7); + sortFunctions[PHPRTLC_CYCLES] = SORT_FUNCTION(CyclesWin7); + sortFunctions[PHPRTLC_CYCLESDELTA] = SORT_FUNCTION(CyclesDeltaWin7); PhEndInitOnce(&initOnce); } @@ -2421,7 +2409,6 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_CYCLES: - if (WindowsVersion >= WINDOWS_7) { ULONG64 value = 0; PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Value), &value); @@ -2432,13 +2419,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( 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); @@ -2449,10 +2431,6 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getCellText->Text = node->CyclesDeltaText->sr; } } - else - { - getCellText->Text = PhGetStringRef(node->CyclesDeltaText); - } break; case PHPRTLC_DEP: PhpUpdateProcessNodeDepStatus(node); @@ -2559,34 +2537,26 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_OSCONTEXT: PhpUpdateProcessOsContext(node); - - if (WindowsVersion >= WINDOWS_7) + switch (node->OsContextVersion) { - 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"); + 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: diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index cb2ef363d4b5..3f0f2f862649 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -103,21 +103,17 @@ VOID PhpUpdateProcessStatistics( if (ProcessItem->QueryHandle) { ULONG64 cycleTime; - - if (WindowsVersion >= WINDOWS_7) + PROCESS_HANDLE_INFORMATION handleInfo; + + if (NT_SUCCESS(NtQueryInformationProcess( + ProcessItem->QueryHandle, + ProcessHandleCount, + &handleInfo, + sizeof(PROCESS_HANDLE_INFORMATION), + NULL + ))) { - 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 @@ -146,19 +142,12 @@ VOID PhpUpdateProcessStatistics( } } - if (WindowsVersion >= WINDOWS_7) - { - if (!gotCycles) - cycles = PhaFormatUInt64(ProcessItem->CycleTimeDelta.Value, TRUE); - if (!gotWsCounters) - privateWs = PhaFormatSize(ProcessItem->WorkingSetPrivateSize, -1); - } - - if (WindowsVersion >= WINDOWS_7) - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, PhGetStringOrDefault(peakHandles, L"Unknown")); - else - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, L"N/A"); + if (!gotCycles) + cycles = PhaFormatUInt64(ProcessItem->CycleTimeDelta.Value, TRUE); + if (!gotWsCounters) + privateWs = PhaFormatSize(ProcessItem->WorkingSetPrivateSize, -1); + SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, PhGetStringOrDefault(peakHandles, L"Unknown")); 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, L"Unknown")); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 82fb961b55c9..ad488eaad530 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -645,7 +645,6 @@ #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 diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index c765e6293a38..ed8eec32b283 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -538,19 +538,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( 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); - } + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Service", FALSE); } else { diff --git a/ProcessHacker/syssccpu.c b/ProcessHacker/syssccpu.c index b7b82704742f..c2a6cc7d9a69 100644 --- a/ProcessHacker/syssccpu.c +++ b/ProcessHacker/syssccpu.c @@ -177,8 +177,7 @@ VOID PhSipInitializeCpuDialog( CurrentPerformanceDistribution = NULL; PreviousPerformanceDistribution = NULL; - if (WindowsVersion >= WINDOWS_7) - PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); + PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); } VOID PhSipUninitializeCpuDialog( @@ -241,15 +240,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++; @@ -693,7 +689,7 @@ VOID PhSipUpdateCpuPanel( cpuGhz = 0; distributionSucceeded = FALSE; - if (WindowsVersion >= WINDOWS_7 && CurrentPerformanceDistribution && PreviousPerformanceDistribution) + if (CurrentPerformanceDistribution && PreviousPerformanceDistribution) { if (PhSipGetCpuFrequencyFromDistribution(&cpuFraction)) { diff --git a/phlib/global.c b/phlib/global.c index 004986fdc189..01388363c212 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -176,25 +176,10 @@ static VOID PhInitializeWindowsVersion( majorVersion = versionInfo.dwMajorVersion; minorVersion = versionInfo.dwMinorVersion; - if (majorVersion == 5 && minorVersion < 1 || majorVersion < 5) + if (majorVersion == 6 && minorVersion < 1 || majorVersion < 6) { 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) { diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 77df87c8d7c1..58c22e8b8bbf 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -1294,31 +1294,12 @@ NTSTATUS PhGetHandleInformationEx( // 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; - } + status = PhpGetObjectName( + ProcessHandle, + KphIsConnected() ? Handle : dupHandle, + TRUE, + &objectName + ); } else { @@ -1453,14 +1434,9 @@ ULONG PhGetObjectTypeNumber( objectIndex = objectType->TypeIndex; break; } - else if (WindowsVersion >= WINDOWS_7) - { - objectIndex = i + 2; - break; - } else { - objectIndex = i + 1; + objectIndex = i + 2; break; } } diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 6da596042c2f..95a4c95b35c1 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -24,7 +24,6 @@ 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 @@ -32,8 +31,6 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_10 100 #define WINDOWS_NEW MAXLONG -#define WINDOWS_HAS_CONSOLE_HOST (WindowsVersion >= WINDOWS_7) -#define WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID (WindowsVersion >= WINDOWS_VISTA) #define WINDOWS_HAS_IMMERSIVE (WindowsVersion >= WINDOWS_8) // Debugging diff --git a/phlib/native.c b/phlib/native.c index 2ce8abb501dd..55a0bfa54840 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -736,25 +736,21 @@ NTSTATUS PhGetProcessWindowTitle( BOOLEAN isWow64 = FALSE; #endif ULONG windowFlags; + PPROCESS_WINDOW_INFORMATION windowInfo; - if (WindowsVersion >= WINDOWS_7) - { - PPROCESS_WINDOW_INFORMATION windowInfo; - - status = PhpQueryProcessVariableSize( - ProcessHandle, - ProcessWindowInformation, - &windowInfo - ); + status = PhpQueryProcessVariableSize( + ProcessHandle, + ProcessWindowInformation, + &windowInfo + ); - if (NT_SUCCESS(status)) - { - *WindowFlags = windowInfo->WindowFlags; - *WindowTitle = PhCreateStringEx(windowInfo->WindowTitle, windowInfo->WindowTitleLength); - PhFree(windowInfo); + if (NT_SUCCESS(status)) + { + *WindowFlags = windowInfo->WindowFlags; + *WindowTitle = PhCreateStringEx(windowInfo->WindowTitle, windowInfo->WindowTitleLength); + PhFree(windowInfo); - return status; - } + return status; } #ifdef _WIN64 @@ -2918,10 +2914,8 @@ NTSTATUS PhpEnumProcessModules( 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; + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7; // Traverse the linked list (in load order). @@ -3281,10 +3275,8 @@ NTSTATUS PhpEnumProcessModules32( 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; + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7_32; // Traverse the linked list (in load order). @@ -4804,10 +4796,7 @@ VOID PhUpdateMupDevicePrefixes( 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");*/ + // PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\DfsClient"); remainingPart = providerOrder->sr; From e86dbba2ce5c938644d097aa30d64f3d2afd6d9f Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Jun 2017 10:24:16 +1000 Subject: [PATCH 217/839] Remove legacy cycle time information --- ProcessHacker/include/proctree.h | 2 - ProcessHacker/proctree.c | 161 ++----------------------------- 2 files changed, 9 insertions(+), 154 deletions(-) diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index bce21d481390..23c30a4f3b66 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -161,8 +161,6 @@ 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 diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 7a9a18156528..e59e448ffab8 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -61,14 +61,6 @@ 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, @@ -92,13 +84,11 @@ 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 PH_TN_FILTER_SUPPORT FilterSupport; -static BOOLEAN NeedCyclesInformation = FALSE; - static HDC GraphContext = NULL; static ULONG GraphContextWidth = 0; static ULONG GraphContextHeight = 0; @@ -254,8 +244,6 @@ VOID PhLoadSettingsProcessTreeList( { SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0); } - - PhpUpdateNeedCyclesInformation(); } VOID PhSaveSettingsProcessTreeList( @@ -624,10 +612,6 @@ VOID PhTickProcessNodes( node->CpuGraphBuffers.Valid = FALSE; node->PrivateGraphBuffers.Valid = FALSE; node->IoGraphBuffers.Valid = FALSE; - - // Updates cycles if necessary. - if (NeedCyclesInformation) - PhpUpdateProcessNodeCycles(node); } fullyInvalidated = FALSE; @@ -986,17 +970,17 @@ static VOID PhpUpdateProcessOsContext( { if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid))) { - if (memcmp(&ProcessNode->OsContextGuid, &WINTHRESHOLD_CONTEXT_GUID, sizeof(GUID)) == 0) + if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINTHRESHOLD_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_10; - else if (memcmp(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID, sizeof(GUID)) == 0) + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_8_1; - else if (memcmp(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID, sizeof(GUID)) == 0) + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_8; - else if (memcmp(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID, sizeof(GUID)) == 0) + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_7; - else if (memcmp(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID, sizeof(GUID)) == 0) + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_VISTA; - else if (memcmp(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID, sizeof(GUID)) == 0) + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_XP; } @@ -1182,91 +1166,6 @@ static VOID PhpUpdateProcessNodeFileAttributes( } } -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( \ @@ -1411,14 +1310,6 @@ BEGIN_SORT_FUNCTION(PeakWorkingSet) 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); } @@ -1661,24 +1552,12 @@ BEGIN_SORT_FUNCTION(WindowStatus) 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); } @@ -2011,19 +1890,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( SORT_FUNCTION(FileModifiedTime), SORT_FUNCTION(FileSize) }; - static PH_INITONCE initOnce = PH_INITONCE_INIT; int (__cdecl *sortFunction)(const void *, const void *); - if (PhBeginInitOnce(&initOnce)) - { - // TODO: Remove this. - 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, @@ -2183,15 +2051,7 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( 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)); - } + PhMoveReference(&node->PrivateWsText, PhFormatSize(processItem->WorkingSetPrivateSize, -1)); getCellText->Text = node->PrivateWsText->sr; break; case PHPRTLC_SHAREDWS: @@ -3101,11 +2961,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( 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(); + PhHandleTreeNewColumnMenu(&data); PhDeleteTreeNewColumnMenu(&data); } return TRUE; From c44ca0ae68ce7a6f0d3dacfdec2e3246623c36e2 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Jun 2017 16:20:48 +1000 Subject: [PATCH 218/839] Update versioning for Windows 10, Fix process OS Context on Win10-RS2, Add PEB comments --- ProcessHacker/appsup.c | 13 ++++++++++++- phlib/global.c | 21 ++++++++++++++++++++- phlib/include/phconfig.h | 5 ++++- phlib/svcsup.c | 25 +++++++++++-------------- phnt/include/ntpebteb.h | 16 ++++++++-------- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index e926582c6946..163045b58ea1 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -178,7 +178,18 @@ NTSTATUS PhGetProcessSwitchContext( if (!data) return STATUS_UNSUCCESSFUL; // no compatibility context data - if (WindowsVersion >= WINDOWS_10) + 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, diff --git a/phlib/global.c b/phlib/global.c index 01388363c212..0afc6504abc1 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -163,6 +163,7 @@ static VOID PhInitializeWindowsVersion( RTL_OSVERSIONINFOEXW versionInfo; ULONG majorVersion; ULONG minorVersion; + ULONG buildVersion; versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); @@ -175,6 +176,7 @@ static VOID PhInitializeWindowsVersion( memcpy(&PhOsVersion, &versionInfo, sizeof(RTL_OSVERSIONINFOEXW)); majorVersion = versionInfo.dwMajorVersion; minorVersion = versionInfo.dwMinorVersion; + buildVersion = versionInfo.dwBuildNumber; if (majorVersion == 6 && minorVersion < 1 || majorVersion < 6) { @@ -198,7 +200,24 @@ static VOID PhInitializeWindowsVersion( /* Windows 10 */ else if (majorVersion == 10 && minorVersion == 0) { - WindowsVersion = WINDOWS_10; + switch (buildVersion) + { + case 10240: + WindowsVersion = WINDOWS_10_TH1; + break; + case 10586: + WindowsVersion = WINDOWS_10_TH2; + break; + case 14393: + WindowsVersion = WINDOWS_10_RS1; + break; + case 15063: + WindowsVersion = WINDOWS_10_RS2; + break; + default: + WindowsVersion = WINDOWS_10; + break; + } } else if (majorVersion == 10 && minorVersion > 0 || majorVersion > 10) { diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 95a4c95b35c1..420d3ed97dfc 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -8,7 +8,6 @@ extern "C" { #define _User_set_ PHLIBAPI extern _User_set_ PVOID PhLibImageBase; - PHLIBAPI extern _User_set_ PWSTR PhApplicationName; PHLIBAPI extern _User_set_ ULONG PhGlobalDpi; PHLIBAPI extern PVOID PhHeapHandle; @@ -29,6 +28,10 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_8 62 #define WINDOWS_8_1 63 #define WINDOWS_10 100 +#define WINDOWS_10_TH1 101 +#define WINDOWS_10_TH2 102 +#define WINDOWS_10_RS1 103 +#define WINDOWS_10_RS2 104 #define WINDOWS_NEW MAXLONG #define WINDOWS_HAS_IMMERSIVE (WindowsVersion >= WINDOWS_8) diff --git a/phlib/svcsup.c b/phlib/svcsup.c index cd796658aca7..07e33948a58e 100644 --- a/phlib/svcsup.c +++ b/phlib/svcsup.c @@ -93,21 +93,18 @@ PVOID PhEnumServices( if (!Type) { - if (WindowsVersion >= WINDOWS_10) + if (WindowsVersion >= WINDOWS_10_RS1) { - 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; - } + 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 { diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 60f3b02adc67..6169a18d4f44 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -65,9 +65,9 @@ typedef struct _PEB PVOID ReadOnlySharedMemoryBase; PVOID HotpatchInformation; PVOID *ReadOnlyStaticServerData; - PVOID AnsiCodePageData; - PVOID OemCodePageData; - PVOID UnicodeCaseTableData; + PVOID AnsiCodePageData; // PCPTABLEINFO + PVOID OemCodePageData; // PCPTABLEINFO + PVOID UnicodeCaseTableData; // PNLSTABLEINFO ULONG NumberOfProcessors; ULONG NtGlobalFlag; @@ -80,7 +80,7 @@ typedef struct _PEB ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; - PVOID *ProcessHeaps; + PVOID *ProcessHeaps; // PHEAP PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; @@ -112,10 +112,10 @@ typedef struct _PEB 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; From 054af2fa4cf0d9708fd56f3594b4cb8f14192c97 Mon Sep 17 00:00:00 2001 From: Efreak Date: Wed, 14 Jun 2017 16:41:59 -0700 Subject: [PATCH 219/839] Add shortcut keys (#151) - View\Tray Icons contents - CPU &history - CPU &usage - &I/O history - &Commit charge history - &Physical memory history - Tools\Start &Task Manager - Process Context Menu\Miscellaneous contents - Reduce working &set - &Run as... - Run &as this user... - Services tab - View\&Hide driver services - Content menu items 'Affinity', 'Priority', 'I/O Priority' sub-items: - &Save for %s - Save &for this command line --- ProcessHacker/ProcessHacker.rc | 18 +++++++++--------- ProcessHacker/mwpgsrv.c | 2 +- plugins/ExtraPlugins/main.c | 4 ++-- plugins/UserNotes/main.c | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index d08e9ed52974..675362f8993c 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -111,11 +111,11 @@ 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 + 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 @@ -152,7 +152,7 @@ BEGIN 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 + MENUITEM "Start &Task Manager", ID_TOOLS_STARTTASKMANAGER END POPUP "&Users" BEGIN @@ -301,9 +301,9 @@ BEGIN 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 + 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 diff --git a/ProcessHacker/mwpgsrv.c b/ProcessHacker/mwpgsrv.c index 866ca49dc2e3..8862e02ded39 100644 --- a/ProcessHacker/mwpgsrv.c +++ b/ProcessHacker/mwpgsrv.c @@ -108,7 +108,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; diff --git a/plugins/ExtraPlugins/main.c b/plugins/ExtraPlugins/main.c index 24daa6d24447..ed2be884c1f1 100644 --- a/plugins/ExtraPlugins/main.c +++ b/plugins/ExtraPlugins/main.c @@ -253,7 +253,7 @@ VOID NTAPI MainMenuInitializingCallback( 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)); + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, pluginMenu->Id, L"&Plugins... (Beta)", NULL), PhIndexOfEMenuItem(menuInfo->Menu, pluginMenu)); PhRemoveEMenuItem(menuInfo->Menu, pluginMenu, 0); } } @@ -348,4 +348,4 @@ LOGICAL DllMain( } return TRUE; -} \ No newline at end of file +} diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index d5bed8c6e40e..6f2dde717b1c 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -870,8 +870,8 @@ VOID AddSavePriorityMenuItemsAndHook( // 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); + 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; @@ -890,8 +890,8 @@ VOID AddSavePriorityMenuItemsAndHook( 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); + 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; @@ -910,8 +910,8 @@ VOID AddSavePriorityMenuItemsAndHook( 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); + 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; From 9edf49688764c1d9df5a3c0857d7cc49386fc43d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 15 Jun 2017 10:33:31 +1000 Subject: [PATCH 220/839] Fix setup error checking --- .../CustomSetupTool/CustomSetupTool/appsup.c | 1 - tools/CustomSetupTool/CustomSetupTool/setup.c | 67 ++++++++++--------- .../CustomSetupTool/CustomSetupTool/update.c | 7 +- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index aeaf18ed86e6..92bdf7e63846 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -505,7 +505,6 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( BOOLEAN ShutdownProcessHacker(VOID) { PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); - return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index be2a2500c977..59e5ab6918c3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -178,41 +178,51 @@ BOOLEAN SetupCreateUninstallFile( // Check if the user has started the setup from the installation folder. if (PhStartsWithStringRef2(¤tFilePath, PhGetString(Context->SetupInstallPath), TRUE)) - { - // Do nothing, latest version already in the installation folder. return TRUE; - } backupFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.bak"); uninstallFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.exe"); if (RtlDoesFileExists_U(backupFilePath->Buffer)) { - // TODO: Move to temp directory - } + GUID randomGuid; + PPH_STRING tempPath = NULL; + PPH_STRING guidString = NULL; - if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) - { - if (!DeleteFile(backupFilePath->Buffer)) + tempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); + GetTempPath((ULONG)tempPath->Length / sizeof(WCHAR), tempPath->Buffer); + + PhGenerateGuid(&randomGuid); + + if (guidString = PhFormatGuid(&randomGuid)) + { + PhMoveReference(&guidString, PhSubstring(guidString, 1, guidString->Length / sizeof(WCHAR) - 2)); + } + + PhMoveReference(&guidString, PhConcatStrings( + 3, + PhGetString(tempPath), + L"\\", + PhGetString(guidString) + )); + + if (!MoveFile(backupFilePath->Buffer, guidString->Buffer)) { Context->ErrorCode = GetLastError(); - PhDereferenceObject(uninstallFilePath); - return FALSE; } + } + if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) + { if (!MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer)) { Context->ErrorCode = GetLastError(); - PhDereferenceObject(uninstallFilePath); - return FALSE; } } - if (!CopyFile(currentFilePath.Buffer, uninstallFilePath->Buffer, FALSE)) + if (!CopyFile(currentFilePath.Buffer, uninstallFilePath->Buffer, TRUE)) { Context->ErrorCode = GetLastError(); - PhDereferenceObject(uninstallFilePath); - return FALSE; } PhDereferenceObject(uninstallFilePath); @@ -336,27 +346,24 @@ BOOLEAN SetupUninstallKph( _In_ PPH_SETUP_CONTEXT Context ) { - BOOLEAN deleted = FALSE; - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (serviceHandle = PhOpenService( - L"KProcessHacker3", - SERVICE_STOP | DELETE - )) + while (TRUE) { - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + SC_HANDLE serviceHandle; + SERVICE_STATUS serviceStatus; - deleted = DeleteService(serviceHandle); + if (!(serviceHandle = PhOpenService( + L"KProcessHacker3", + SERVICE_STOP | DELETE + ))) + break; + + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + DeleteService(serviceHandle); CloseServiceHandle(serviceHandle); } - else - { - deleted = TRUE; - } - return deleted; + return TRUE; } VOID SetupSetWindowsOptions( diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index ae71b4ecb5a7..79a22c0e495e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -141,12 +141,7 @@ VOID SetupShowUpdatingErrorDialog( config.pfCallback = SetupErrorTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = PhApplicationName; - config.pszMainInstruction = PhaFormatString( - L"Error updating to the latest version...", - PHAPP_VERSION_MAJOR, - PHAPP_VERSION_MINOR, - PHAPP_VERSION_REVISION - )->Buffer; + config.pszMainInstruction = L"Error updating to the latest version."; if (Context->ErrorCode) { From 0b79b2409e0bc6311dee5f3b5287a55f618f99d4 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 15 Jun 2017 10:37:48 +1000 Subject: [PATCH 221/839] Enable CFG builds --- ProcessHacker/ProcessHacker.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 2e5b41dd74cf..0be944a9791b 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -160,6 +160,7 @@ true true StreamingSIMDExtensions + Guard aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) @@ -197,6 +198,7 @@ StdCall true true + Guard aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) From efbe81fbbf0574f1624e4013dd4cefdf626b5b00 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Jun 2017 18:39:15 +1000 Subject: [PATCH 222/839] Update ntpsapi.h #152 --- phnt/include/ntpsapi.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 0e65c8dfd8f2..7390ad399e2e 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1272,10 +1272,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, FALSE) + 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 From ac88ba9a2f6d744d17d257c63276c80039b20c1f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 18 Jun 2017 17:13:31 +1000 Subject: [PATCH 223/839] Tidy up variables --- plugins/ExtraPlugins/setup/updater.c | 10 +++++--- plugins/NetworkTools/update.c | 38 ++++++++++++++++++---------- plugins/OnlineChecks/upload.c | 8 ++++-- plugins/OnlineChecks/virustotal.c | 16 +++++++++--- plugins/Updater/updater.c | 30 ++++++++++++++-------- 5 files changed, 68 insertions(+), 34 deletions(-) diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index f8fdf0111cad..7dfb62bb0acd 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -451,10 +451,12 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 7e9cd5e07cc2..565830343209 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -198,13 +198,19 @@ PPH_STRING QueryFwLinkUrl( ); } - { - 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)); - } + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_REDIRECTS }, + sizeof(ULONG) + ); + + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); if (!WinHttpSendRequest( httpRequestHandle, @@ -400,8 +406,12 @@ NTSTATUS GeoIPUpdateThread( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); } if (!(httpConnectionHandle = WinHttpConnect( @@ -427,10 +437,12 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - { - ULONG option = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); - } + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 0fb00a62503b..5537dadbf63f 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1001,8 +1001,12 @@ NTSTATUS UploadCheckThreadStart( if (WindowsVersion >= WINDOWS_8_1) { - ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; - WinHttpSetOption(context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); + WinHttpSetOption( + context->HttpHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_ALL }, + sizeof(ULONG) + ); } switch (context->Service) diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 4e7ed4ee645a..a32907038f96 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -321,8 +321,12 @@ PSTR VirusTotalSendHttpRequest( if (WindowsVersion >= WINDOWS_8_1) { - ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); } if (!(connectHandle = WinHttpConnect( @@ -465,8 +469,12 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); } if (!(connectHandle = WinHttpConnect( diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 29b737f7c63d..c09751490822 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -364,11 +364,13 @@ BOOLEAN QueryUpdateData( goto CleanupExit; } - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } - + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); + if (versionHeader) { WinHttpAddRequestHeaders( @@ -775,8 +777,12 @@ NTSTATUS UpdateDownloadThread( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); } if (!(httpConnectionHandle = WinHttpConnect( @@ -804,10 +810,12 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); From e5df262b7b04e64506bf439c53354e1b766ac53b Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 18 Jun 2017 17:14:10 +1000 Subject: [PATCH 224/839] Updater: Re-enable express setup for updates --- plugins/Updater/page5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 0a94def0107c..0b766debcfeb 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -66,7 +66,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( break; info.lpFile = PhGetStringOrEmpty(context->SetupFilePath); - //info.lpParameters = L"-update"; + info.lpParameters = L"-update"; info.lpVerb = PhGetOwnTokenAttributes().Elevated ? NULL : L"runas"; info.nShow = SW_SHOW; info.hwnd = hwndDlg; From d8bd0829415215520d2cd2d6b283bf752eed0dfd Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 20 Jun 2017 21:22:46 +1000 Subject: [PATCH 225/839] Tidy up code --- ProcessHacker/appsup.c | 36 +++++++++++++++++----------------- ProcessHacker/include/appsup.h | 11 ++++++++--- ProcessHacker/procgrp.c | 20 +++++++++---------- plugins/ToolStatus/main.c | 26 ++++++++---------------- plugins/ToolStatus/toolbar.c | 24 ++++++----------------- 5 files changed, 50 insertions(+), 67 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 163045b58ea1..44c042089b49 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -711,18 +711,18 @@ BOOLEAN PhaGetProcessKnownCommandLine( } VOID PhEnumChildWindows( - _In_opt_ HWND hWnd, + _In_opt_ HWND WindowHandle, _In_ ULONG Limit, - _In_ WNDENUMPROC Callback, - _In_ LPARAM lParam + _In_ PH_CHILD_ENUM_CALLBACK Callback, + _In_ PVOID Context ) { HWND childWindow = NULL; ULONG i = 0; - while (i < Limit && (childWindow = FindWindowEx(hWnd, childWindow, NULL, NULL))) + while (i < Limit && (childWindow = FindWindowEx(WindowHandle, childWindow, NULL, NULL))) { - if (!Callback(childWindow, lParam)) + if (!Callback(childWindow, Context)) return; i++; @@ -738,36 +738,36 @@ typedef struct _GET_PROCESS_MAIN_WINDOW_CONTEXT BOOLEAN SkipInvisible; } GET_PROCESS_MAIN_WINDOW_CONTEXT, *PGET_PROCESS_MAIN_WINDOW_CONTEXT; -BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( - _In_ HWND hwnd, - _In_ LPARAM lParam +BOOLEAN CALLBACK PhpGetProcessMainWindowEnumWindowsProc( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context ) { - PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)lParam; + PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)Context; ULONG processId; HWND parentWindow; WINDOWINFO windowInfo; - if (context->SkipInvisible && !IsWindowVisible(hwnd)) + if (context->SkipInvisible && !IsWindowVisible(WindowHandle)) return TRUE; - GetWindowThreadProcessId(hwnd, &processId); + GetWindowThreadProcessId(WindowHandle, &processId); if (UlongToHandle(processId) == context->ProcessId && (context->SkipInvisible ? - !((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 : TRUE)) // skip windows with no title + !((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 (!context->ImmersiveWindow && context->IsImmersive && - GetProp(hwnd, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow")) + GetProp(WindowHandle, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow")) { - context->ImmersiveWindow = hwnd; + context->ImmersiveWindow = WindowHandle; } windowInfo.cbSize = sizeof(WINDOWINFO); - if (!context->Window && GetWindowInfo(hwnd, &windowInfo) && (windowInfo.dwStyle & WS_DLGFRAME)) + if (!context->Window && GetWindowInfo(WindowHandle, &windowInfo) && (windowInfo.dwStyle & WS_DLGFRAME)) { - context->Window = hwnd; + context->Window = WindowHandle; // If we're not looking at an immersive process, there's no need to search any more windows. if (!context->IsImmersive) @@ -807,7 +807,7 @@ HWND PhGetProcessMainWindowEx( if (processHandle && IsImmersiveProcess_I) context.IsImmersive = IsImmersiveProcess_I(processHandle); - PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, (LPARAM)&context); + PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, &context); if (!ProcessHandle && processHandle) NtClose(processHandle); diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 0973ce98f32a..db93af4c9627 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -97,11 +97,16 @@ PhaGetProcessKnownCommandLine( ); // end_phapppub +typedef BOOLEAN (CALLBACK *PH_CHILD_ENUM_CALLBACK)( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ); + VOID PhEnumChildWindows( - _In_opt_ HWND hWnd, + _In_opt_ HWND WindowHandle, _In_ ULONG Limit, - _In_ WNDENUMPROC Callback, - _In_ LPARAM lParam + _In_ PH_CHILD_ENUM_CALLBACK Callback, + _In_ PVOID Context ); HWND PhGetProcessMainWindow( diff --git a/ProcessHacker/procgrp.c b/ProcessHacker/procgrp.c index e0e38b8ca587..be3325709507 100644 --- a/ProcessHacker/procgrp.c +++ b/ProcessHacker/procgrp.c @@ -114,29 +114,29 @@ typedef struct _QUERY_WINDOWS_CONTEXT PPH_HASHTABLE ProcessDataHashtable; } QUERY_WINDOWS_CONTEXT, *PQUERY_WINDOWS_CONTEXT; -BOOL CALLBACK PhpQueryWindowsEnumWindowsProc( - _In_ HWND hwnd, - _In_ LPARAM lParam +BOOLEAN CALLBACK PhpQueryWindowsEnumWindowsProc( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context ) { - PQUERY_WINDOWS_CONTEXT context = (PQUERY_WINDOWS_CONTEXT)lParam; + PQUERY_WINDOWS_CONTEXT context = (PQUERY_WINDOWS_CONTEXT)Context; ULONG processId; PPHP_PROCESS_DATA processData; HWND parentWindow; - if (!IsWindowVisible(hwnd)) + if (!IsWindowVisible(WindowHandle)) return TRUE; - GetWindowThreadProcessId(hwnd, &processId); + GetWindowThreadProcessId(WindowHandle, &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 + 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 = hwnd; + processData->WindowHandle = WindowHandle; } return TRUE; @@ -285,7 +285,7 @@ PPH_LIST PhCreateProcessGroupList( PhpProcessDataListToHashtable(processDataList, &processDataHashtable); queryWindowsContext.ProcessDataHashtable = processDataHashtable; - PhEnumChildWindows(NULL, 0x800, PhpQueryWindowsEnumWindowsProc, (LPARAM)&queryWindowsContext); + PhEnumChildWindows(NULL, 0x800, PhpQueryWindowsEnumWindowsProc, &queryWindowsContext); processGroupList = PhCreateList(10); diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 2c81fa57babb..afd88f8ced2c 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -666,10 +666,8 @@ LRESULT CALLBACK MainWndSubclassProc( PhInvokeCallback(&SearchChangedEvent, SearchboxText); } - - goto DefaultWndProc; } - break; + goto DefaultWndProc; case EN_KILLFOCUS: { if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle) @@ -683,10 +681,8 @@ LRESULT CALLBACK MainWndSubclassProc( if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); } - - goto DefaultWndProc; } - break; + goto DefaultWndProc; } switch (GET_WM_COMMAND_ID(wParam, lParam)) @@ -720,11 +716,9 @@ LRESULT CALLBACK MainWndSubclassProc( SetFocus(SearchboxHandle); Edit_SetSel(SearchboxHandle, 0, -1); - } - - goto DefaultWndProc; + } } - break; + goto DefaultWndProc; case PHAPP_ID_VIEW_ALWAYSONTOP: { // Let Process Hacker perform the default processing. @@ -734,11 +728,9 @@ LRESULT CALLBACK MainWndSubclassProc( 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; + SendMessage(ToolBarHandle, TB_PRESSBUTTON, (WPARAM)PHAPP_ID_VIEW_ALWAYSONTOP, (LPARAM)(MAKELONG(isAlwaysOnTopEnabled, 0))); } - break; + goto DefaultWndProc; case PHAPP_ID_UPDATEINTERVAL_FAST: case PHAPP_ID_UPDATEINTERVAL_NORMAL: case PHAPP_ID_UPDATEINTERVAL_BELOWNORMAL: @@ -749,10 +741,8 @@ LRESULT CALLBACK MainWndSubclassProc( DefSubclassProc(hWnd, uMsg, wParam, lParam); StatusBarUpdate(TRUE); - - goto DefaultWndProc; } - break; + goto DefaultWndProc; case PHAPP_ID_VIEW_UPDATEAUTOMATICALLY: { UpdateAutomatically = !UpdateAutomatically; @@ -1142,7 +1132,7 @@ LRESULT CALLBACK MainWndSubclassProc( // 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 + _In_ HWND WindowHandle ); if (!HungWindowFromGhostWindow_I) diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index 018cd2b6380f..d5521bcdce06 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -109,23 +109,18 @@ VOID RebarLoadSettings( { if (ToolStatusConfig.ToolBarEnabled && !ToolBarImageList) { - // Enable scaling the Toolbar and images on high-DPI machines. - ToolBarImageSize.cx = PH_SCALE_DPI(16); - ToolBarImageSize.cy = PH_SCALE_DPI(16); - + ToolBarImageSize.cx = PH_SCALE_DPI(GetSystemMetrics(SM_CXSMICON)); + ToolBarImageSize.cy = PH_SCALE_DPI(GetSystemMetrics(SM_CYSMICON)); 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 + 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, @@ -137,7 +132,7 @@ VOID RebarLoadSettings( 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 + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | TBSTYLE_TOOLTIPS | TBSTYLE_AUTOSIZE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, RebarHandle, NULL, @@ -146,7 +141,7 @@ VOID RebarLoadSettings( ); // Set the rebar info with no imagelist. - SendMessage(RebarHandle, RB_SETBARINFO, 0, (LPARAM)&rebarInfo); + 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. @@ -157,15 +152,9 @@ VOID RebarLoadSettings( 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. + ULONG toolbarButtonSize = (ULONG)SendMessage(ToolBarHandle, TB_GETBUTTONSIZE, 0, 0); RebarBandInsert(REBAR_BAND_ID_TOOLBAR, ToolBarHandle, LOWORD(toolbarButtonSize), HIWORD(toolbarButtonSize)); } @@ -181,7 +170,6 @@ VOID RebarLoadSettings( if (ToolStatusConfig.StatusBarEnabled && !StatusBarHandle) { - // Create the StatusBar window. StatusBarHandle = CreateWindowEx( 0, STATUSCLASSNAME, From e1c7fefd1c47fa94b3229e0603b812efb2e295c1 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 20 Jun 2017 21:34:49 +1000 Subject: [PATCH 226/839] Move font functions to sdk --- ProcessHacker/appsup.c | 18 ---------- ProcessHacker/include/appsup.h | 5 --- ProcessHacker/include/phapp.h | 59 ++++++++++++++++++++++++++++++++ ProcessHacker/main.c | 28 ++------------- plugins/ToolStatus/customizesb.c | 2 +- plugins/ToolStatus/customizetb.c | 2 +- plugins/include/commonutil.h | 13 ------- 7 files changed, 63 insertions(+), 64 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 44c042089b49..4271dced71dc 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1148,24 +1148,6 @@ BOOLEAN PhGetListViewContextMenuPoint( 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 diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index db93af4c9627..1843bae473ad 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -212,11 +212,6 @@ PhGetListViewContextMenuPoint( ); // end_phapppub -HFONT PhDuplicateFontWithNewWeight( - _In_ HFONT Font, - _In_ LONG NewWeight - ); - VOID PhSetWindowOpacity( _In_ HWND WindowHandle, _In_ ULONG OpacityPercent diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index c213449420d2..0237a4cb5ff1 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -629,6 +629,32 @@ PhLoadPngImageFromResource( _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( @@ -668,6 +694,39 @@ PhCreateCommonFont( 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 diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index d972aac56428..4aed3dd8f786 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -488,30 +488,6 @@ VOID PhInitializeCommonControls( 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 ) @@ -533,8 +509,8 @@ VOID PhInitializeFont( success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0); if ( - !(PhApplicationFont = PhpCreateFont(L"Microsoft Sans Serif", 8, FW_NORMAL)) && - !(PhApplicationFont = PhpCreateFont(L"Tahoma", 8, FW_NORMAL)) + !(PhApplicationFont = PhCreateFont(L"Microsoft Sans Serif", 8, FW_NORMAL)) && + !(PhApplicationFont = PhCreateFont(L"Tahoma", 8, FW_NORMAL)) ) { if (success) diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index cd4dbd29034d..e8ce99115092 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -319,7 +319,7 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( 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)); + context->FontHandle = PhDuplicateFont((HFONT)SendMessage(StatusBarHandle, WM_GETFONT, 0, 0)); ListBox_SetItemHeight(context->AvailableListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight ListBox_SetItemHeight(context->CurrentListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index cef8fdffbb33..27330c0c74ba 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -523,7 +523,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( 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)); + context->FontHandle = PhDuplicateFont((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 diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index a6e265992413..c5511468865c 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -569,18 +569,5 @@ FormatAnsiString( return FormatAnsiString_V(Format, argptr); } -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 From bbe312f9826dea42826bb92c90474ed91a419ff0 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 21 Jun 2017 21:55:40 +1000 Subject: [PATCH 227/839] Add PhCreatePipe/PhCreateNamedPipe --- ProcessHacker/ProcessHacker.def | 2 + ProcessHacker/anawait.c | 4 +- phlib/include/phnative.h | 16 ++++ phlib/native.c | 156 ++++++++++++++++++++++++++++++++ phnt/include/ntioapi.h | 2 + 5 files changed, 179 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 0258a7b3b934..d8af2e8f5d83 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -229,6 +229,8 @@ EXPORTS PhCreateFileWin32 PhCreateFileWin32Ex PhCreateKey + PhCreateNamedPipe + PhCreatePipe PhDeleteFileWin32 PhDisconnectNamedPipe PhEnumDirectoryFile diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index cbf30b17eb8a..7f1f75b4bf1b 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -832,7 +832,9 @@ static VOID PhpInitializeServiceNumbers( // NtReadFile - if (CreatePipe(&pipeReadHandle, &pipeWriteHandle, NULL, 0)) + status = PhCreatePipe(&pipeReadHandle, &pipeWriteHandle); + + if (NT_SUCCESS(status)) { if (threadHandle = PhCreateThread(0, PhpRfThreadStart, pipeReadHandle)) { diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 6d1ff9b694fa..41390d6ec34d 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -973,6 +973,22 @@ PhDeleteFileWin32( _In_ PWSTR FileName ); +PHLIBAPI +NTSTATUS +NTAPI +PhCreatePipe( + _Out_ PHANDLE PipeReadHandle, + _Out_ PHANDLE PipeWriteHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhCreateNamedPipe( + _Out_ PHANDLE PipeHandle, + _In_ PWSTR PipeName + ); + PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/native.c b/phlib/native.c index 55a0bfa54840..6394261a6f90 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6172,6 +6172,162 @@ NTSTATUS PhDeleteFileWin32( 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 + ) +{ + NTSTATUS status; + HANDLE pipeDirectoryHandle; + HANDLE pipeReadHandle; + HANDLE pipeWriteHandle; + LARGE_INTEGER pipeTimeout; + UNICODE_STRING pipeNameUs; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + + RtlInitUnicodeString(&pipeNameUs, DEVICE_NAMED_PIPE); + InitializeObjectAttributes( + &oa, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenFile( + &pipeDirectoryHandle, + GENERIC_READ | SYNCHRONIZE, + &oa, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + InitializeObjectAttributes( + &oa, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + pipeDirectoryHandle, + NULL + ); + + status = NtCreateNamedPipeFile( + &pipeReadHandle, + FILE_WRITE_ATTRIBUTES | GENERIC_READ | SYNCHRONIZE, + &oa, + &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, + PIPE_UNLIMITED_INSTANCES, // min: 1, max: ULONG_MAX + PAGE_SIZE, + PAGE_SIZE, + PhTimeoutFromMilliseconds(&pipeTimeout, 500) + ); + + if (!NT_SUCCESS(status)) + { + NtClose(pipeDirectoryHandle); + return status; + } + + InitializeObjectAttributes( + &oa, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + pipeReadHandle, + NULL + ); + + status = NtOpenFile( + &pipeWriteHandle, + FILE_READ_ATTRIBUTES | GENERIC_WRITE | SYNCHRONIZE, + &oa, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + *PipeReadHandle = pipeReadHandle; + *PipeWriteHandle = pipeWriteHandle; + } + + 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; + 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 + ); + + status = NtCreateNamedPipeFile( + &pipeHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &oa, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, + FILE_PIPE_FULL_DUPLEX | FILE_SYNCHRONOUS_IO_NONALERT, + FILE_PIPE_MESSAGE_TYPE, + FILE_PIPE_MESSAGE_MODE, + FILE_PIPE_QUEUE_OPERATION, + PIPE_UNLIMITED_INSTANCES, // min: 1, max: ULONG_MAX + PAGE_SIZE, + PAGE_SIZE, + &pipeTimeout + ); + + if (NT_SUCCESS(status)) + { + *PipeHandle = pipeHandle; + } + + PhDereferenceObject(pipeName); + return status; +} + NTSTATUS PhListenNamedPipe( _In_ HANDLE FileHandle, _In_opt_ HANDLE Event, diff --git a/phnt/include/ntioapi.h b/phnt/include/ntioapi.h index 84e9c53724a2..c13aa19c3721 100644 --- a/phnt/include/ntioapi.h +++ b/phnt/include/ntioapi.h @@ -1667,6 +1667,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) From 4745882618f8fb8cf15bcea783a562b1ccac878c Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 21 Jun 2017 21:56:51 +1000 Subject: [PATCH 228/839] Fix typo --- phlib/native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phlib/native.c b/phlib/native.c index 6394261a6f90..0a5bc388b634 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6213,6 +6213,7 @@ NTSTATUS PhCreatePipe( if (!NT_SUCCESS(status)) return status; + RtlInitUnicodeString(&pipeNameUs, UNICODE_NULL); InitializeObjectAttributes( &oa, &pipeNameUs, @@ -6244,6 +6245,7 @@ NTSTATUS PhCreatePipe( return status; } + RtlInitUnicodeString(&pipeNameUs, UNICODE_NULL); InitializeObjectAttributes( &oa, &pipeNameUs, From 5e95d1b3b0aa6dbb0c2e6706de9ed0a226aa42ce Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 22 Jun 2017 14:27:31 +1000 Subject: [PATCH 229/839] Update imports --- phlib/apiimport.c | 6 +++--- phlib/symprv.c | 8 ++++---- phlib/treenew.c | 2 +- phlib/util.c | 4 ++-- plugins/OnlineChecks/upload.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/phlib/apiimport.c b/phlib/apiimport.c index bcf06a49574a..12e2bc7e52d5 100644 --- a/phlib/apiimport.c +++ b/phlib/apiimport.c @@ -30,18 +30,18 @@ PVOID PhpImportProcedure( _In_ PSTR ProcedureName ) { - HMODULE module; + PVOID module; PVOID procedure; if (*CacheValid) return *Cache; - module = GetModuleHandle(ModuleName); + module = PhGetDllHandle(ModuleName); if (!module) return NULL; - procedure = GetProcAddress(module, ProcedureName); + procedure = PhGetProcedureAddress(module, ProcedureName, 0); *Cache = procedure; MemoryBarrier(); *CacheValid = TRUE; diff --git a/phlib/symprv.c b/phlib/symprv.c index 641ad92da130..2065b1887c1a 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -112,8 +112,8 @@ VOID PhSymbolProviderCompleteInitialization( _In_opt_ PVOID DbgHelpBase ) { - HMODULE dbghelpHandle; - HMODULE symsrvHandle; + PVOID dbghelpHandle; + PVOID symsrvHandle; // The user should have loaded dbghelp.dll and symsrv.dll already. If not, it's not our problem. @@ -122,9 +122,9 @@ VOID PhSymbolProviderCompleteInitialization( if (DbgHelpBase) dbghelpHandle = DbgHelpBase; else - dbghelpHandle = GetModuleHandle(L"dbghelp.dll"); + dbghelpHandle = PhGetDllHandle(L"dbghelp.dll"); - symsrvHandle = GetModuleHandle(L"symsrv.dll"); + symsrvHandle = PhGetDllHandle(L"symsrv.dll"); SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); SymCleanup_I = PhGetProcedureAddress(dbghelpHandle, "SymCleanup", 0); diff --git a/phlib/treenew.c b/phlib/treenew.c index 6466a00bae9f..f5bef8b20da0 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -77,7 +77,7 @@ BOOLEAN PhTreeNewInitialization( if (!RegisterClassEx(&c)) return FALSE; - ComCtl32Handle = GetModuleHandle(L"comctl32.dll"); + ComCtl32Handle = PhGetDllHandle(L"comctl32.dll"); SmallIconWidth = GetSystemMetrics(SM_CXSMICON); SmallIconHeight = GetSystemMetrics(SM_CYSMICON); diff --git a/phlib/util.c b/phlib/util.c index 27ce988d2135..8084ead139cf 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -301,7 +301,7 @@ PPH_STRING PhGetNtMessage( PPH_STRING message; if (!NT_NTWIN32(Status)) - message = PhGetMessage(GetModuleHandle(L"ntdll.dll"), 0xb, GetUserDefaultLangID(), (ULONG)Status); + message = PhGetMessage(PhGetDllHandle(L"ntdll.dll"), 0xb, GetUserDefaultLangID(), (ULONG)Status); else message = PhGetWin32Message(WIN32_FROM_NTSTATUS(Status)); @@ -343,7 +343,7 @@ PPH_STRING PhGetWin32Message( { PPH_STRING message; - message = PhGetMessage(GetModuleHandle(L"kernel32.dll"), 0xb, GetUserDefaultLangID(), Result); + message = PhGetMessage(PhGetDllHandle(L"kernel32.dll"), 0xb, GetUserDefaultLangID(), Result); if (message) PhTrimToNullTerminatorString(message); diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 5537dadbf63f..f1040c0bfd7b 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -96,7 +96,7 @@ VOID RaiseUploadError( if (!Context->DialogHandle) return; - if (message = PhGetMessage(GetModuleHandle(L"winhttp.dll"), 0xb, GetUserDefaultLangID(), ErrorCode)) + if (message = PhGetMessage(PhGetDllHandle(L"winhttp.dll"), 0xb, GetUserDefaultLangID(), ErrorCode)) { PhTrimToNullTerminatorString(message); } From f3f0bda881c352b9c695017b1862cd430d6a31b8 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 22 Jun 2017 18:31:51 +1000 Subject: [PATCH 230/839] Update PhCreateThread, Remove legacy imports --- ProcessHacker/include/procprp.h | 2 +- ProcessHacker/mdump.c | 14 +--------- ProcessHacker/memsrch.c | 16 +---------- ProcessHacker/options.c | 7 ++--- ProcessHacker/phsvc/svcapiport.c | 6 +--- ProcessHacker/procprp.c | 17 ++---------- ProcessHacker/srvprv.c | 27 ++---------------- phlib/basesup.c | 47 ++++++++++++++++++++++++++++---- phlib/include/phbasesup.h | 8 ++++++ 9 files changed, 61 insertions(+), 83 deletions(-) diff --git a/ProcessHacker/include/procprp.h b/ProcessHacker/include/procprp.h index 3da58d000c31..065926df5f2a 100644 --- a/ProcessHacker/include/procprp.h +++ b/ProcessHacker/include/procprp.h @@ -156,7 +156,7 @@ PhEndPropPageLayout( } PHAPPAPI -BOOLEAN +VOID NTAPI PhShowProcessProperties( _In_ PPH_PROCESS_PROPCONTEXT Context diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index e40ab800a630..8214941f406a 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -48,7 +48,6 @@ typedef struct _PROCESS_MINIDUMP_CONTEXT HANDLE FileHandle; HWND WindowHandle; - HANDLE ThreadHandle; BOOLEAN Stop; BOOLEAN Succeeded; @@ -377,25 +376,14 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( 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); - } + PhCreateThread2(PhpProcessMiniDumpThreadStart, context); 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; diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index 0440ff6f4b30..fb22ff01d98a 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -44,7 +44,6 @@ typedef struct _MEMORY_STRING_CONTEXT BOOLEAN Mapped; HWND WindowHandle; - HANDLE ThreadHandle; PH_MEMORY_STRING_OPTIONS Options; PPH_LIST Results; } MEMORY_STRING_CONTEXT, *PMEMORY_STRING_CONTEXT; @@ -653,27 +652,14 @@ INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( 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; - } + PhCreateThread2(PhpMemoryStringThreadStart, context); 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; diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 988c32d5a3cb..4f27c293ce3e 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -883,7 +883,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( { case IDC_CHANGE: { - HANDLE threadHandle; RECT windowRect; // Save the options so they don't get "overwritten" when @@ -892,15 +891,13 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( GetWindowRect(GetParent(hwndDlg), &windowRect); WindowHandleForElevate = hwndDlg; - threadHandle = PhCreateThread(0, PhpElevateAdvancedThreadStart, PhFormatString( + + PhCreateThread2(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: diff --git a/ProcessHacker/phsvc/svcapiport.c b/ProcessHacker/phsvc/svcapiport.c index fb31465e3e3c..7bfb8ebf49b8 100644 --- a/ProcessHacker/phsvc/svcapiport.c +++ b/ProcessHacker/phsvc/svcapiport.c @@ -48,7 +48,6 @@ NTSTATUS PhSvcApiPortInitialization( PSID administratorsSid; PACL dacl; ULONG i; - HANDLE threadHandle; // Create the API port. @@ -99,10 +98,7 @@ NTSTATUS PhSvcApiPortInitialization( for (i = 0; i < 2; i++) { - threadHandle = PhCreateThread(0, PhSvcApiRequestThreadStart, NULL); - - if (threadHandle) - NtClose(threadHandle); + PhCreateThread2(PhSvcApiRequestThreadStart, NULL); } return status; diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index bf6e302925ef..ae884db625e5 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -704,23 +704,10 @@ NTSTATUS PhpProcessPropertiesThreadStart( return STATUS_SUCCESS; } -BOOLEAN PhShowProcessProperties( +VOID 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; - } + PhCreateThread2(PhpProcessPropertiesThreadStart, Context); } diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 22ae892d9122..fc2d8b6d1ba9 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -29,12 +29,6 @@ #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; @@ -89,7 +83,6 @@ VOID PhpInitializeServiceNonPoll( ); PPH_OBJECT_TYPE PhServiceItemType; - PPH_HASHTABLE PhServiceHashtable; PH_QUEUED_LOCK PhServiceHashtableLock = PH_QUEUED_LOCK_INIT; @@ -101,9 +94,7 @@ 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; @@ -939,7 +930,8 @@ NTSTATUS PhpServiceNonPollThreadStart( notifyContext->Buffer.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; notifyContext->Buffer.pfnNotifyCallback = PhpServiceNonPollScNotifyCallback; notifyContext->Buffer.pContext = notifyContext; - result = NotifyServiceStatusChangeW_I( + + result = NotifyServiceStatusChange( notifyContext->ServiceHandle, notifyContext->IsServiceManager ? (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED) @@ -1017,21 +1009,8 @@ 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; - } + PhCreateThread2(PhpServiceNonPollThreadStart, NULL); } diff --git a/phlib/basesup.c b/phlib/basesup.c index 61ee300a3574..358bcbb9fe07 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -243,16 +243,18 @@ HANDLE PhCreateThread( context->StartAddress = StartAddress; context->Parameter = Parameter; - threadHandle = CreateThread( + if (NT_SUCCESS(RtlCreateUserThread( + NtCurrentProcess(), NULL, + FALSE, + 0, + 0, StackSize, PhpBaseThreadStart, context, - 0, + &threadHandle, NULL - ); - - if (threadHandle) + ))) { PHLIB_INC_STATISTIC(BaseThreadsCreated); return threadHandle; @@ -265,6 +267,41 @@ HANDLE PhCreateThread( } } +VOID PhCreateThread2( + _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; + + if (NT_SUCCESS(RtlCreateUserThread( + NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + 0, + PhpBaseThreadStart, + context, + &threadHandle, + NULL + ))) + { + PHLIB_INC_STATISTIC(BaseThreadsCreated); + NtClose(threadHandle); + } + else + { + PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); + PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); + } +} + /** * Gets the current system time (UTC). * diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index f59e12f07c38..3c75c1eecefe 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -40,6 +40,14 @@ PhCreateThread( _In_opt_ PVOID Parameter ); +PHLIBAPI +VOID +NTAPI +PhCreateThread2( + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ); + // DLLs FORCEINLINE From f93d5ccc4ea61befbedef462abb7061bdf2c1bcc Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 22 Jun 2017 18:56:27 +1000 Subject: [PATCH 231/839] Update thread functions --- ProcessHacker/ProcessHacker.def | 1 + plugins/HardwareDevices/diskdetails.c | 6 +----- plugins/HardwareDevices/netdetails.c | 6 +----- plugins/NetworkTools/ping.c | 12 ++---------- plugins/NetworkTools/tracert.c | 17 +++-------------- plugins/NetworkTools/update.c | 5 +---- plugins/NetworkTools/whois.c | 17 +++-------------- plugins/OnlineChecks/upload.c | 17 +---------------- plugins/Updater/updater.c | 5 +---- tools/peview/include/peview.h | 1 - tools/peview/pdbprp.c | 4 +--- 11 files changed, 15 insertions(+), 76 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index d8af2e8f5d83..31e68e0bc05f 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -114,6 +114,7 @@ EXPORTS PhCreateString PhCreateStringEx PhCreateThread + PhCreateThread2 PhDecodeUnicodeDecoder PhDeleteArray PhDeleteBytesBuilder diff --git a/plugins/HardwareDevices/diskdetails.c b/plugins/HardwareDevices/diskdetails.c index 37f18f033e89..a6912b73bb20 100644 --- a/plugins/HardwareDevices/diskdetails.c +++ b/plugins/HardwareDevices/diskdetails.c @@ -755,7 +755,6 @@ VOID ShowDiskDriveDetailsDialog( _In_ PDV_DISK_SYSINFO_CONTEXT Context ) { - HANDLE dialogThread = NULL; PCOMMON_PAGE_CONTEXT pageContext; pageContext = PhAllocate(sizeof(COMMON_PAGE_CONTEXT)); @@ -768,8 +767,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); + PhCreateThread2(ShowDiskDriveDetailsDialogThread, pageContext); } \ No newline at end of file diff --git a/plugins/HardwareDevices/netdetails.c b/plugins/HardwareDevices/netdetails.c index 983a7ca071db..03cd4e92cf0a 100644 --- a/plugins/HardwareDevices/netdetails.c +++ b/plugins/HardwareDevices/netdetails.c @@ -653,7 +653,6 @@ VOID ShowNetAdapterDetailsDialog( _In_ PDV_NETADAPTER_SYSINFO_CONTEXT Context ) { - HANDLE dialogThread = NULL; PDV_NETADAPTER_DETAILS_CONTEXT context; context = PhAllocate(sizeof(DV_NETADAPTER_DETAILS_CONTEXT)); @@ -663,8 +662,5 @@ VOID ShowNetAdapterDetailsDialog( PhSetReference(&context->AdapterName, Context->AdapterEntry->AdapterName); CopyNetAdapterId(&context->AdapterId, &Context->AdapterEntry->Id); - if (dialogThread = PhCreateThread(0, ShowNetAdapterDetailsDialogThread, context)) - NtClose(dialogThread); - else - FreeNetAdapterDetailsContext(context); + PhCreateThread2(ShowNetAdapterDetailsDialogThread, context); } \ No newline at end of file diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index fc415881497e..4dce3d99221e 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -567,7 +567,6 @@ VOID ShowPingWindow( _In_ PPH_NETWORK_ITEM NetworkItem ) { - HANDLE dialogThread; PNETWORK_PING_CONTEXT context; context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); @@ -580,17 +579,13 @@ VOID ShowPingWindow( context->RemoteEndpoint = NetworkItem->RemoteEndpoint; - if (dialogThread = PhCreateThread(0, NetworkPingDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(NetworkPingDialogThreadStart, (PVOID)context); } VOID ShowPingWindowFromAddress( _In_ PH_IP_ENDPOINT RemoteEndpoint ) { - HANDLE dialogThread; PNETWORK_PING_CONTEXT context; context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); @@ -607,8 +602,5 @@ VOID ShowPingWindowFromAddress( context->RemoteEndpoint = RemoteEndpoint; - if (dialogThread = PhCreateThread(0, NetworkPingDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(NetworkPingDialogThreadStart, (PVOID)context); } \ No newline at end of file diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index f5fc427381e8..1a0f3abff127 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -566,8 +566,6 @@ INT_PTR CALLBACK TracertDlgProc( { case WM_INITDIALOG: { - HANDLE tracertThread; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); @@ -599,8 +597,7 @@ INT_PTR CALLBACK TracertDlgProc( PhReferenceObject(context); - if (tracertThread = PhCreateThread(0, NetworkTracertThreadStart, (PVOID)context)) - NtClose(tracertThread); + PhCreateThread2(NetworkTracertThreadStart, (PVOID)context); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } @@ -724,7 +721,6 @@ VOID ShowTracertWindow( _In_ PPH_NETWORK_ITEM NetworkItem ) { - HANDLE dialogThread; PNETWORK_TRACERT_CONTEXT context; context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); @@ -741,17 +737,13 @@ VOID ShowTracertWindow( RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); } - if (dialogThread = PhCreateThread(0, TracertDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(TracertDialogThreadStart, (PVOID)context); } VOID ShowTracertWindowFromAddress( _In_ PH_IP_ENDPOINT RemoteEndpoint ) { - HANDLE dialogThread; PNETWORK_TRACERT_CONTEXT context; context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); @@ -768,8 +760,5 @@ VOID ShowTracertWindowFromAddress( RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); } - if (dialogThread = PhCreateThread(0, TracertDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(TracertDialogThreadStart, (PVOID)context); } \ No newline at end of file diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 565830343209..57d728413f45 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -811,8 +811,5 @@ VOID ShowGeoIPUpdateDialog( _In_opt_ HWND Parent ) { - HANDLE threadHandle; - - if (threadHandle = PhCreateThread(0, GeoIPUpdateDialogThread, Parent)) - NtClose(threadHandle); + PhCreateThread2(GeoIPUpdateDialogThread, Parent); } \ No newline at end of file diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 5be18698f44d..3cca5ef466df 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -394,8 +394,6 @@ INT_PTR CALLBACK NetworkOutputDlgProc( { case WM_INITDIALOG: { - HANDLE dialogThread; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); @@ -421,8 +419,7 @@ INT_PTR CALLBACK NetworkOutputDlgProc( else PhCenterWindow(hwndDlg, PhMainWndHandle); - if (dialogThread = PhCreateThread(0, NetworkWhoisThreadStart, (PVOID)context)) - NtClose(dialogThread); + PhCreateThread2(NetworkWhoisThreadStart, (PVOID)context); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } @@ -556,7 +553,6 @@ VOID ShowWhoisWindow( _In_ PPH_NETWORK_ITEM NetworkItem ) { - HANDLE dialogThread; PNETWORK_WHOIS_CONTEXT context; context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); @@ -573,17 +569,13 @@ VOID ShowWhoisWindow( RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); } - if (dialogThread = PhCreateThread(0, NetworkWhoisDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(NetworkWhoisDialogThreadStart, (PVOID)context); } VOID ShowWhoisWindowFromAddress( _In_ PH_IP_ENDPOINT RemoteEndpoint ) { - HANDLE dialogThread; PNETWORK_WHOIS_CONTEXT context; context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); @@ -600,8 +592,5 @@ VOID ShowWhoisWindowFromAddress( RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); } - if (dialogThread = PhCreateThread(0, NetworkWhoisDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } + PhCreateThread2(NetworkWhoisDialogThreadStart, (PVOID)context); } \ No newline at end of file diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index f1040c0bfd7b..542063e168f3 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1175,16 +1175,6 @@ LRESULT CALLBACK TaskDialogSubclassProc( 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); @@ -1288,7 +1278,6 @@ VOID UploadToOnlineService( { static PH_INITONCE initOnce = PH_INITONCE_INIT; - HANDLE dialogThread; PUPLOAD_CONTEXT context; if (PhBeginInitOnce(&initOnce)) @@ -1305,9 +1294,5 @@ VOID UploadToOnlineService( context->FileName = FileName; context->BaseFileName = PhGetBaseName(context->FileName); - if (dialogThread = PhCreateThread(0, ShowUpdateDialogThread, (PVOID)context)) - { - PostMessage(dialogThread, UM_SHOWDIALOG, 0, 0); - NtClose(dialogThread); - } + PhCreateThread2(ShowUpdateDialogThread, (PVOID)context); } diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index c09751490822..7e65a8ee4807 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -1217,8 +1217,5 @@ VOID StartInitialCheck( VOID ) { - HANDLE silentCheckThread = NULL; - - if (silentCheckThread = PhCreateThread(0, UpdateCheckSilentThread, NULL)) - NtClose(silentCheckThread); + PhCreateThread2(UpdateCheckSilentThread, NULL); } \ No newline at end of file diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index c2586e7db2b6..d076b7649ac5 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -209,7 +209,6 @@ typedef struct _PDB_SYMBOL_CONTEXT HWND SearchHandle; HWND TreeNewHandle; HWND ParentWindowHandle; - HANDLE SearchThreadHandle; HANDLE TimerQueueHandle; HANDLE UpdateTimerHandle; diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index c72a7a9e8722..39d66924472e 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -956,7 +956,7 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( SearchResults = PhCreateList(0x1000); context->UdtList = PhCreateList(0x100); - context->SearchThreadHandle = PhCreateThread(0, PeDumpFileSymbols, context); + PhCreateThread2(PeDumpFileSymbols, context); if (NT_SUCCESS(RtlCreateTimerQueue(&context->TimerQueueHandle))) { @@ -982,8 +982,6 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( if (context->TimerQueueHandle) RtlDeleteTimerQueue(context->TimerQueueHandle); - NtClose(context->SearchThreadHandle); - PvDeleteSymbolTree(context); } break; From 0d40a08fbaccf39848db19b94d3e1f139c68027d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 22 Jun 2017 19:37:22 +1000 Subject: [PATCH 232/839] Add PhConnectPipe --- ProcessHacker/ProcessHacker.def | 1 + phlib/include/phnative.h | 32 +++--- phlib/native.c | 175 ++++++++++++++++++++------------ 3 files changed, 128 insertions(+), 80 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 31e68e0bc05f..6804648802d8 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -232,6 +232,7 @@ EXPORTS PhCreateKey PhCreateNamedPipe PhCreatePipe + PhConnectPipe PhDeleteFileWin32 PhDisconnectNamedPipe PhEnumDirectoryFile diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 41390d6ec34d..7b9d0baa713c 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -989,29 +989,33 @@ PhCreateNamedPipe( _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, @@ -1023,11 +1027,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, @@ -1038,17 +1038,15 @@ 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 ); #ifdef __cplusplus diff --git a/phlib/native.c b/phlib/native.c index 0a5bc388b634..c3ee08b66cd8 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1319,7 +1319,7 @@ NTSTATUS PhInjectDllProcess( 0, 0, 0, - (PUSER_THREAD_START_ROUTINE)threadStart, + threadStart, baseAddress, &threadHandle, NULL @@ -6310,7 +6310,7 @@ NTSTATUS PhCreateNamedPipe( &oa, &isb, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_CREATE, + FILE_OPEN_IF, FILE_PIPE_FULL_DUPLEX | FILE_SYNCHRONOUS_IO_NONALERT, FILE_PIPE_MESSAGE_TYPE, FILE_PIPE_MESSAGE_MODE, @@ -6330,37 +6330,92 @@ NTSTATUS PhCreateNamedPipe( 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 FileHandle, - _In_opt_ HANDLE Event, - _In_opt_ PIO_APC_ROUTINE ApcRoutine, - _In_opt_ PVOID ApcContext, - _Out_ PIO_STATUS_BLOCK IoStatusBlock + _In_ HANDLE PipeHandle ) { - return NtFsControlFile( - FileHandle, - Event, - ApcRoutine, - ApcContext, - IoStatusBlock, + 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 FileHandle + _In_ HANDLE PipeHandle ) { NTSTATUS status; IO_STATUS_BLOCK isb; status = NtFsControlFile( - FileHandle, + PipeHandle, NULL, NULL, NULL, @@ -6374,7 +6429,7 @@ NTSTATUS PhDisconnectNamedPipe( if (status == STATUS_PENDING) { - status = NtWaitForSingleObject(FileHandle, FALSE, NULL); + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); if (NT_SUCCESS(status)) status = isb.Status; @@ -6384,7 +6439,7 @@ NTSTATUS PhDisconnectNamedPipe( } NTSTATUS PhPeekNamedPipe( - _In_ HANDLE FileHandle, + _In_ HANDLE PipeHandle, _Out_writes_bytes_opt_(Length) PVOID Buffer, _In_ ULONG Length, _Out_opt_ PULONG NumberOfBytesRead, @@ -6401,7 +6456,7 @@ NTSTATUS PhPeekNamedPipe( peekBuffer = PhAllocate(peekBufferLength); status = NtFsControlFile( - FileHandle, + PipeHandle, NULL, NULL, NULL, @@ -6415,7 +6470,7 @@ NTSTATUS PhPeekNamedPipe( if (status == STATUS_PENDING) { - status = NtWaitForSingleObject(FileHandle, FALSE, NULL); + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); if (NT_SUCCESS(status)) status = isb.Status; @@ -6427,7 +6482,7 @@ NTSTATUS PhPeekNamedPipe( if (NT_SUCCESS(status)) { - ULONG numberOfBytesRead; + ULONG numberOfBytesRead = 0; if (Buffer || NumberOfBytesRead || NumberOfBytesLeftInMessage) numberOfBytesRead = (ULONG)(isb.Information - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data)); @@ -6451,55 +6506,58 @@ NTSTATUS PhPeekNamedPipe( } 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_ HANDLE PipeHandle, _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, + 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_opt_ PUNICODE_STRING FileSystemName, - _In_ PUNICODE_STRING Name, - _In_opt_ PLARGE_INTEGER Timeout, - _In_ BOOLEAN UseDefaultTimeout + _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; - if (!FileSystemName) - { - RtlInitUnicodeString(&localNpfsName, L"\\Device\\NamedPipe"); - FileSystemName = &localNpfsName; - } - + RtlInitUnicodeString(&localNpfsName, DEVICE_NAMED_PIPE); InitializeObjectAttributes( &oa, - FileSystemName, + &localNpfsName, OBJ_CASE_INSENSITIVE, NULL, NULL @@ -6517,30 +6575,24 @@ NTSTATUS PhWaitForNamedPipe( if (!NT_SUCCESS(status)) return status; - waitForBufferLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name) + Name->Length; + PhInitializeStringRefLongHint(&localNpfsNameSr, PipeName); + waitForBufferLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name) + (ULONG)localNpfsNameSr.Length; waitForBuffer = PhAllocate(waitForBufferLength); - if (UseDefaultTimeout) + if (Timeout) { - waitForBuffer->TimeoutSpecified = FALSE; + PhTimeoutFromMilliseconds(&waitForBuffer->Timeout, Timeout); + waitForBuffer->TimeoutSpecified = TRUE; } else { - if (Timeout) - { - waitForBuffer->Timeout = *Timeout; - } - else - { - waitForBuffer->Timeout.LowPart = 0; - waitForBuffer->Timeout.HighPart = MINLONG; // a very long time - } - + 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); + waitForBuffer->NameLength = (ULONG)localNpfsNameSr.Length; + memcpy(waitForBuffer->Name, localNpfsNameSr.Buffer, localNpfsNameSr.Length); status = NtFsControlFile( fileSystemHandle, @@ -6562,14 +6614,13 @@ NTSTATUS PhWaitForNamedPipe( } NTSTATUS PhImpersonateClientOfNamedPipe( - _In_ HANDLE FileHandle + _In_ HANDLE PipeHandle ) { - NTSTATUS status; IO_STATUS_BLOCK isb; - status = NtFsControlFile( - FileHandle, + return NtFsControlFile( + PipeHandle, NULL, NULL, NULL, @@ -6580,6 +6631,4 @@ NTSTATUS PhImpersonateClientOfNamedPipe( NULL, 0 ); - - return status; } From b2bc78c951df548a0621b27489839c38293a9cae Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 22 Jun 2017 19:44:34 +1000 Subject: [PATCH 233/839] Update find handles window layout --- ProcessHacker/ProcessHacker.rc | 28 ++++++++++++++-------------- ProcessHacker/findobj.c | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 675362f8993c..bcbe1bc418c3 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -111,11 +111,11 @@ 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 + 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 @@ -152,7 +152,7 @@ BEGIN 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 + MENUITEM "Start &Task Manager", ID_TOOLS_STARTTASKMANAGER END POPUP "&Users" BEGIN @@ -301,9 +301,9 @@ BEGIN 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 + 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 @@ -720,14 +720,14 @@ BEGIN END IDD_FINDOBJECTS DIALOGEX 0, 0, 357, 233 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +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 LTEXT "Filter:",IDC_STATIC,7,9,20,8 EDITTEXT IDC_FILTER,32,8,224,14,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 "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,25,353,206 CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,261,9,36,10 END @@ -1963,10 +1963,10 @@ BEGIN IDD_FINDOBJECTS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 350 + LEFTMARGIN, 2 + RIGHTMARGIN, 355 TOPMARGIN, 7 - BOTTOMMARGIN, 226 + BOTTOMMARGIN, 231 END IDD_OBJTOKEN, DIALOG diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 78eb2690f0c0..77e5ed193412 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -100,7 +100,7 @@ VOID PhShowFindObjectsDialog( PhFindObjectsWindowHandle = CreateDialog( PhInstanceHandle, MAKEINTRESOURCE(IDD_FINDOBJECTS), - PhMainWndHandle, + NULL, PhpFindObjectsDlgProc ); } From 8d3dd4b16fe1b4e9978ed5baa1c9b33b5f046d6e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 00:03:28 +1000 Subject: [PATCH 234/839] Add RtlInitEmptyUnicodeString --- phnt/include/ntrtl.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 0f01ff0e065b..66b9565e80e6 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -1371,6 +1371,20 @@ RtlUpperString( _In_ PSTRING SourceString ); +FORCEINLINE +VOID +NTAPI +RtlInitEmptyUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_ PWCHAR Buffer, + _In_ USHORT BufferSize + ) +{ + DestinationString->Length = 0; + DestinationString->MaximumLength = BufferSize; + DestinationString->Buffer = Buffer; +} + #ifndef PHNT_NO_INLINE_INIT_STRING FORCEINLINE VOID RtlInitUnicodeString( _Out_ PUNICODE_STRING DestinationString, From 095b9ae121fdf817775a6cc8b165c553149a3e41 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 01:17:05 +1000 Subject: [PATCH 235/839] Remove legacy import --- ProcessHacker/mainwnd.c | 2 +- phlib/guisup.c | 2 -- phlib/include/guisup.h | 6 ------ phlib/include/phbasesup.h | 17 +++++++++++++++++ phlib/include/phsup.h | 15 --------------- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index cb6592fbca56..f73c3b15d263 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -175,7 +175,7 @@ BOOLEAN PhMainWndInitialization( windowRectangle.Width, windowRectangle.Height, FALSE); // Allow WM_PH_ACTIVATE to pass through UIPI. - ChangeWindowMessageFilter_I(WM_PH_ACTIVATE, MSGFLT_ADD); + ChangeWindowMessageFilter(WM_PH_ACTIVATE, MSGFLT_ADD); PhMwpOnSettingChange(); diff --git a/phlib/guisup.c b/phlib/guisup.c index c7d3d1143d75..4ecd51fb4500 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -31,7 +31,6 @@ #define SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) -_ChangeWindowMessageFilter ChangeWindowMessageFilter_I; _IsImmersiveProcess IsImmersiveProcess_I; _RunFileDlg RunFileDlg; _SHAutoComplete SHAutoComplete_I; @@ -50,7 +49,6 @@ VOID PhGuiSupportInitialization( shell32Handle = LoadLibrary(L"shell32.dll"); shlwapiHandle = LoadLibrary(L"shlwapi.dll"); - ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter"); if (WINDOWS_HAS_IMMERSIVE) IsImmersiveProcess_I = PhGetModuleProcAddress(L"user32.dll", "IsImmersiveProcess"); RunFileDlg = PhGetProcedureAddress(shell32Handle, NULL, 61); diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index d825dd31fe48..1a5c9beabd5b 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -11,11 +11,6 @@ extern "C" { // guisup -typedef BOOL (WINAPI *_ChangeWindowMessageFilter)( - _In_ UINT message, - _In_ DWORD dwFlag - ); - typedef BOOL (WINAPI *_IsImmersiveProcess)( _In_ HANDLE hProcess ); @@ -60,7 +55,6 @@ typedef HRESULT (WINAPI *_SHAutoComplete)( _In_ DWORD dwFlags ); -extern _ChangeWindowMessageFilter ChangeWindowMessageFilter_I; extern _IsImmersiveProcess IsImmersiveProcess_I; extern _RunFileDlg RunFileDlg; extern _SHAutoComplete SHAutoComplete_I; diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index 3c75c1eecefe..b2c6a1838710 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -105,6 +105,23 @@ PhGetProcedureAddress( return procedureAddress; } +FORCEINLINE +PVOID +PhGetModuleProcAddress( + _In_ PWSTR ModuleName, + _In_ PSTR ProcName + ) +{ + HMODULE module; + + module = PhGetDllHandle(ModuleName); + + if (module) + return PhGetProcedureAddress(module, ProcName, 0); + else + return NULL; +} + // Misc. system PHLIBAPI diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 55e36cf5160f..2be65f1ddb46 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -528,19 +528,4 @@ FORCEINLINE NTSTATUS PhGetLastWin32ErrorAsNtStatus() 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 From 5138bfb8aa641702cd25da34905cf8740bb4bd5d Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 01:24:22 +1000 Subject: [PATCH 236/839] Fix named pipe permissions, Add RtlDefaultNpAcl --- phlib/native.c | 28 ++++++++++++++++++++++++++-- phnt/include/ntrtl.h | 7 +++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index c3ee08b66cd8..1a1f5f4e197f 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6184,6 +6184,7 @@ NTSTATUS PhCreatePipe( ) { NTSTATUS status; + PACL pipeAcl; HANDLE pipeDirectoryHandle; HANDLE pipeReadHandle; HANDLE pipeWriteHandle; @@ -6222,6 +6223,17 @@ NTSTATUS PhCreatePipe( NULL ); + if (NT_SUCCESS(RtlDefaultNpAcl(&pipeAcl))) + { + SECURITY_DESCRIPTOR securityDescriptor; + + RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + + oa.SecurityDescriptor = &securityDescriptor; + } + status = NtCreateNamedPipeFile( &pipeReadHandle, FILE_WRITE_ATTRIBUTES | GENERIC_READ | SYNCHRONIZE, @@ -6233,7 +6245,7 @@ NTSTATUS PhCreatePipe( FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE, FILE_PIPE_QUEUE_OPERATION, - PIPE_UNLIMITED_INSTANCES, // min: 1, max: ULONG_MAX + ULONG_MAX, PAGE_SIZE, PAGE_SIZE, PhTimeoutFromMilliseconds(&pipeTimeout, 500) @@ -6285,6 +6297,7 @@ NTSTATUS PhCreateNamedPipe( ) { NTSTATUS status; + PACL pipeAcl; HANDLE pipeHandle; PPH_STRING pipeName; LARGE_INTEGER pipeTimeout; @@ -6304,6 +6317,17 @@ NTSTATUS PhCreateNamedPipe( NULL ); + if (NT_SUCCESS(RtlDefaultNpAcl(&pipeAcl))) + { + SECURITY_DESCRIPTOR securityDescriptor; + + RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + + oa.SecurityDescriptor = &securityDescriptor; + } + status = NtCreateNamedPipeFile( &pipeHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE, @@ -6315,7 +6339,7 @@ NTSTATUS PhCreateNamedPipe( FILE_PIPE_MESSAGE_TYPE, FILE_PIPE_MESSAGE_MODE, FILE_PIPE_QUEUE_OPERATION, - PIPE_UNLIMITED_INSTANCES, // min: 1, max: ULONG_MAX + ULONG_MAX, PAGE_SIZE, PAGE_SIZE, &pipeTimeout diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 66b9565e80e6..0aa4a316e604 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -5429,6 +5429,13 @@ RtlCreateAcl( _In_ ULONG AclRevision ); +NTSYSAPI +NTSTATUS +NTAPI +RtlDefaultNpAcl( + _Out_ PACL *Acl + ); + NTSYSAPI BOOLEAN NTAPI From f9196ae1252cdc4d92fb5457a0a4eb6f49c119f6 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 01:26:05 +1000 Subject: [PATCH 237/839] Fix RtlInitEmptyUnicodeString definition --- phnt/include/ntrtl.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 0aa4a316e604..74bef7a7d37e 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -1371,18 +1371,28 @@ RtlUpperString( _In_ PSTRING SourceString ); -FORCEINLINE +FORCEINLINE VOID NTAPI RtlInitEmptyUnicodeString( _Out_ PUNICODE_STRING DestinationString, - _In_ PWCHAR Buffer, - _In_ USHORT BufferSize + _In_opt_ PWCHAR Buffer, + _In_opt_ USHORT MaximumLength ) { + if (Buffer) + { + DestinationString->Buffer = Buffer; + DestinationString->MaximumLength = MaximumLength; + } + else + { + PTEB currentTeb = NtCurrentTeb(); + DestinationString->Buffer = currentTeb->StaticUnicodeBuffer; + DestinationString->MaximumLength = sizeof(currentTeb->StaticUnicodeBuffer); + } + DestinationString->Length = 0; - DestinationString->MaximumLength = BufferSize; - DestinationString->Buffer = Buffer; } #ifndef PHNT_NO_INLINE_INIT_STRING From 840ed7fed5295093f02336b2fcbf92d45ca557c3 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 02:17:19 +1000 Subject: [PATCH 238/839] Improve PhUpdateDosDevicePrefixes lookup --- phlib/native.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 1a1f5f4e197f..df12c4479901 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4849,14 +4849,32 @@ VOID PhUpdateDosDevicePrefixes( ) { WCHAR deviceNameBuffer[7] = L"\\??\\ :"; - ULONG i; +#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 (i = 0; i < 26; 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); From 806a5ba3ce13a27eef4774e41ed0a12c1d50c0cf Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 02:17:35 +1000 Subject: [PATCH 239/839] Fix typo --- phlib/native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/phlib/native.c b/phlib/native.c index df12c4479901..2c2cbbb383a9 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4864,6 +4864,7 @@ VOID PhUpdateDosDevicePrefixes( NULL ); + for (ULONG i = 0; i < 0x1A; i++) { HANDLE linkHandle; OBJECT_ATTRIBUTES oa; From db9e33d7d3474568735fb1b4fb9fbab6b32714f4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 02:26:33 +1000 Subject: [PATCH 240/839] Fix duplicate DosDevicePrefixes initialization --- ProcessHacker/procprv.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index ebbff15f1ab8..cb48c8d9f16b 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1932,11 +1932,6 @@ VOID PhProcessProviderUpdate( // Pre-update tasks - if (runCount % 5 == 0) - { - PhUpdateDosDevicePrefixes(); - } - if (runCount % 512 == 0) // yes, a very long time { if (PhEnablePurgeProcessRecords) From 8986f4ad6d8f2e63d50d180f6841382f5f4b9e61 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 23 Jun 2017 16:54:31 +1000 Subject: [PATCH 241/839] Update ntpsapi.h #152 --- phnt/include/ntpsapi.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 7390ad399e2e..1168592532ab 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -630,6 +630,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 @@ -1475,12 +1493,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) From 59dcab4afda92b9b4ad3ec0f81541226b5350a4f Mon Sep 17 00:00:00 2001 From: Steven G Date: Sat, 1 Jul 2017 09:33:37 +1000 Subject: [PATCH 242/839] Improve startup performance (#155) * Improve startup performance * Improve commandline and .NET detection (removes multiple handles) --- ProcessHacker/procprv.c | 82 ++++++++++++++++++++-------------------- phlib/native.c | 84 +++++++++++++++++++++-------------------- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index cb48c8d9f16b..a6a1727ba6de 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1007,66 +1007,64 @@ VOID PhpProcessQueryStage1( } // Command line, .NET + if (processHandleLimited) { - HANDLE processHandle; - BOOLEAN queryAccess = FALSE; - - status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_VM_READ, - processId - ); + BOOLEAN isDotNet = FALSE; + PPH_STRING commandLine; + HANDLE processHandle = NULL; + ULONG processQueryFlags = 0; - if (!NT_SUCCESS(status) && WindowsVersion >= WINDOWS_8_1) + if (WindowsVersion >= WINDOWS_8_1) + { + processHandle = processHandleLimited; + processQueryFlags |= PH_CLR_USE_SECTION_CHECK; + status = STATUS_SUCCESS; + } + else { - queryAccess = TRUE; status = PhOpenProcess( &processHandle, - ProcessQueryAccess, + ProcessQueryAccess | PROCESS_VM_READ, processId ); } if (NT_SUCCESS(status)) { - BOOLEAN isDotNet = FALSE; - PPH_STRING commandLine; - ULONG i; + PhGetProcessIsDotNetEx( + processId, + processHandle, +#ifdef _WIN64 + processQueryFlags | PH_CLR_NO_WOW64_CHECK | (Data->IsWow64 ? PH_CLR_KNOWN_IS_WOW64 : 0), +#else + processQueryFlags, +#endif + &isDotNet, + NULL + ); + Data->IsDotNet = isDotNet; + } - 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)) + { + status = PhGetProcessCommandLine(processHandle, &commandLine); + } - if (NT_SUCCESS(status)) + if (NT_SUCCESS(status)) + { + // 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 / 2; i++) { - Data->CommandLine = commandLine; + if (commandLine->Buffer[i] == 0) + commandLine->Buffer[i] = ' '; } - 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; - } + Data->CommandLine = commandLine; + } + if (!(processQueryFlags & PH_CLR_USE_SECTION_CHECK) && processHandle) NtClose(processHandle); - } } // Token information diff --git a/phlib/native.c b/phlib/native.c index 2c2cbbb383a9..eaee8252253b 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4365,15 +4365,9 @@ NTSTATUS PhGetProcessIsDotNetEx( _Out_opt_ PULONG Flags ) { - NTSTATUS status = STATUS_SUCCESS; - HANDLE processHandle; - ULONG flags; -#ifdef _WIN64 - BOOLEAN isWow64; -#endif - if (InFlags & PH_CLR_USE_SECTION_CHECK) { + NTSTATUS status; HANDLE sectionHandle; OBJECT_ATTRIBUTES objectAttributes; PPH_STRING sectionName; @@ -4456,53 +4450,61 @@ NTSTATUS PhGetProcessIsDotNetEx( return STATUS_SUCCESS; } - } - flags = 0; - processHandle = NULL; - - if (!ProcessHandle) + return status; + } + else { - if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId))) - return status; + NTSTATUS status; + HANDLE processHandle = NULL; + ULONG flags = 0; +#ifdef _WIN64 + BOOLEAN isWow64; +#endif - ProcessHandle = processHandle; - } + 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 (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 - { + if (isWow64) + { + flags |= PH_CLR_PROCESS_IS_WOW64; + status = PhEnumProcessModules32(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); + } + else + { #endif - PhEnumProcessModules(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); + status = PhEnumProcessModules(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); #ifdef _WIN64 - } + } #endif - if (processHandle) - NtClose(processHandle); + if (processHandle) + NtClose(processHandle); - if (IsDotNet) - *IsDotNet = (flags & PH_CLR_VERSION_MASK) && (flags & (PH_CLR_MSCORLIB_PRESENT | PH_CLR_JIT_PRESENT)); + if (IsDotNet) + *IsDotNet = (flags & PH_CLR_VERSION_MASK) && (flags & (PH_CLR_MSCORLIB_PRESENT | PH_CLR_JIT_PRESENT)); - if (Flags) - *Flags = flags; + if (Flags) + *Flags = flags; - return status; + return status; + } } /** From 9af31675fbe34ae391f6a3c23cbe189410c63767 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 02:24:22 +1000 Subject: [PATCH 243/839] Add FileOpenExecutable setting Feature request: https://wj32.org/processhacker/forums/viewtopic.php?p=8673 --- ProcessHacker/prpggen.c | 14 ++++++++++---- ProcessHacker/settings.c | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 0fc4508d5593..d8a6a1dc9be7 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -478,9 +478,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( break; case WM_COMMAND: { - INT id = LOWORD(wParam); - - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_INSPECT: { @@ -499,7 +497,15 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( case IDC_OPENFILENAME: { if (processItem->FileName) - PhShellExploreFile(hwndDlg, processItem->FileName->Buffer); + { + PhShellExecuteUserString( + hwndDlg, + L"FileOpenExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } } break; case IDC_VIEWCOMMANDLINE: diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index a2f16f8c98c2..2642ccf91953 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -48,6 +48,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableWarnings", L"1"); PhpAddIntegerSetting(L"EnableWindowText", L"1"); PhpAddStringSetting(L"EnvironmentListViewColumns", L""); + PhpAddStringSetting(L"FileOpenExecutable", L"explorer.exe \"/select,%s\""); PhpAddIntegerSetting(L"FindObjRegex", L"0"); PhpAddStringSetting(L"FindObjListViewColumns", L""); PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"350,350"); From 99b37d5549d26b4835519c56e2860d9792a3f4f3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 02:37:31 +1000 Subject: [PATCH 244/839] Fix file encoding --- tools/GenerateZw/GenerateZw/Program.cs | 52 ++-- .../GenerateZw/Properties/AssemblyInfo.cs | 72 ++--- tools/GenerateZw/GenerateZw/ZwGen.cs | 246 +++++++++--------- 3 files changed, 185 insertions(+), 185 deletions(-) diff --git a/tools/GenerateZw/GenerateZw/Program.cs b/tools/GenerateZw/GenerateZw/Program.cs index 4962274d9ee0..20ba3a89ee3d 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..49da97e1ccc5 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..737610f9e935 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(); + } + } +} From c2f2bc8f74c7aa498e8ad66220fa6c84244bbb48 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 03:03:38 +1000 Subject: [PATCH 245/839] Fix typo --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 2642ccf91953..fba8a2719d3e 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -48,11 +48,11 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableWarnings", L"1"); PhpAddIntegerSetting(L"EnableWindowText", L"1"); PhpAddStringSetting(L"EnvironmentListViewColumns", L""); - PhpAddStringSetting(L"FileOpenExecutable", L"explorer.exe \"/select,%s\""); PhpAddIntegerSetting(L"FindObjRegex", L"0"); PhpAddStringSetting(L"FindObjListViewColumns", L""); PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"350,350"); PhpAddScalableIntegerPairSetting(L"FindObjWindowSize", L"@96|550,420"); + PhpAddStringSetting(L"FileOpenExecutable", L"explorer.exe \"/select,%s\""); PhpAddIntegerSetting(L"FirstRun", L"1"); PhpAddStringSetting(L"Font", L""); // null PhpAddIntegerSetting(L"ForceNoParent", L"0"); From 510e3ce870be6eee23ba18d05e583b849238b292 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 13:57:53 +1000 Subject: [PATCH 246/839] NetworkTools: Add hostname lookup support, Fix network tab GDI leak --- plugins/NetworkTools/CHANGELOG.txt | 1 + plugins/NetworkTools/NetworkTools.vcxproj | 16 +- plugins/NetworkTools/main.c | 173 +++++++++++----------- plugins/NetworkTools/nettools.h | 3 +- plugins/NetworkTools/tracetree.c | 1 + 5 files changed, 99 insertions(+), 95 deletions(-) diff --git a/plugins/NetworkTools/CHANGELOG.txt b/plugins/NetworkTools/CHANGELOG.txt index 17bdec0b9e13..ce778d88aafb 100644 --- a/plugins/NetworkTools/CHANGELOG.txt +++ b/plugins/NetworkTools/CHANGELOG.txt @@ -1,6 +1,7 @@ 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 diff --git a/plugins/NetworkTools/NetworkTools.vcxproj b/plugins/NetworkTools/NetworkTools.vcxproj index 307ef4de731f..3f96a14ded81 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj +++ b/plugins/NetworkTools/NetworkTools.vcxproj @@ -53,27 +53,27 @@ - iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) + dnsapi.dll;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) + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) + dnsapi.dll;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) + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) + dnsapi.dll;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) + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) + dnsapi.dll;iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 9cd8d0992746..1542ea7612e5 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -22,7 +22,6 @@ */ #include "nettools.h" -#include "tracert.h" #include PPH_PLUGIN PluginInstance; @@ -51,22 +50,80 @@ VOID NTAPI ShowOptionsCallback( ShowOptionsDialog((HWND)Parameter); } -HRESULT CALLBACK ElevateActionCallbackProc( - _In_ HWND hwnd, - _In_ UINT uNotification, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData +static BOOLEAN ValidAddressInfo( + _In_ PPH_IP_ENDPOINT RemoteEndpoint, + _In_ PPH_STRING Name ) { - switch (uNotification) + PWSTR terminator = NULL; + + if (DnsValidateName(Name->Buffer, DnsNameValidateTld) == ERROR_SUCCESS) { - case TDN_CREATED: - SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); - break; + BOOLEAN success = FALSE; + PADDRINFOT result; + WSADATA wsaData; + + WSAStartup(WINSOCK_VERSION, &wsaData); + + if (GetAddrInfo(Name->Buffer, NULL, 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 = ((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( + RemoteEndpoint->Address.In6Addr.s6_addr, + ((PSOCKADDR_IN6)i->ai_addr)->sin6_addr.s6_addr, + sizeof(RemoteEndpoint->Address.In6Addr.s6_addr) + ); + RemoteEndpoint->Port = ((PSOCKADDR_IN6)i->ai_addr)->sin6_port; + RemoteEndpoint->Address.Type = PH_IPV6_NETWORK_TYPE; + success = TRUE; + break; + } + } + + FreeAddrInfo(result); + } + + WSACleanup(); + + if (success) + return TRUE; + } + else + { + if (NT_SUCCESS(RtlIpv4StringToAddress( + Name->Buffer, + TRUE, + &terminator, + &RemoteEndpoint->Address.InAddr + ))) + { + RemoteEndpoint->Address.Type = PH_IPV4_NETWORK_TYPE; + return TRUE; + } + + if (NT_SUCCESS(RtlIpv6StringToAddress( + Name->Buffer, + &terminator, + &RemoteEndpoint->Address.In6Addr + ))) + { + RemoteEndpoint->Address.Type = PH_IPV6_NETWORK_TYPE; + return TRUE; + } } - return S_OK; + return FALSE; } VOID NTAPI MenuItemCallback( @@ -89,45 +146,26 @@ VOID NTAPI MenuItemCallback( ShowWhoisWindow(networkItem); break; case MAINMENU_ACTION_PING: - { - PH_IP_ENDPOINT RemoteEndpoint; + { PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; while (PhaChoiceDialog( menuItem->OwnerWindow, L"Ping", - L"IP address:", + L"Hostname or IP address:", NULL, 0, NULL, PH_CHOICE_DIALOG_USER_CHOICE, &selectedChoice, NULL, - SETTING_NAME_TRACERT_HISTORY + SETTING_NAME_ADDRESS_HISTORY )) { - PWSTR terminator = NULL; - - if (NT_SUCCESS(RtlIpv4StringToAddress( - selectedChoice->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) + if (ValidAddressInfo(&remoteEndpoint, selectedChoice)) { - 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); + ShowPingWindowFromAddress(remoteEndpoint); break; } } @@ -135,44 +173,25 @@ VOID NTAPI MenuItemCallback( break; case MAINMENU_ACTION_TRACERT: { - PH_IP_ENDPOINT RemoteEndpoint; PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; while (PhaChoiceDialog( menuItem->OwnerWindow, L"Tracert", - L"IP address:", + L"Hostname or IP address:", NULL, 0, NULL, PH_CHOICE_DIALOG_USER_CHOICE, &selectedChoice, NULL, - SETTING_NAME_TRACERT_HISTORY + SETTING_NAME_ADDRESS_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 - ))) + if (ValidAddressInfo(&remoteEndpoint, selectedChoice)) { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowTracertWindowFromAddress(RemoteEndpoint); + ShowTracertWindowFromAddress(remoteEndpoint); break; } } @@ -180,44 +199,25 @@ VOID NTAPI MenuItemCallback( break; case MAINMENU_ACTION_WHOIS: { - PH_IP_ENDPOINT RemoteEndpoint; PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; while (PhaChoiceDialog( menuItem->OwnerWindow, L"Whois", - L"IP address for Whois:", + L"Hostname or IP address:", NULL, 0, NULL, PH_CHOICE_DIALOG_USER_CHOICE, &selectedChoice, NULL, - SETTING_NAME_TRACERT_HISTORY + SETTING_NAME_ADDRESS_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 - ))) + if (ValidAddressInfo(&remoteEndpoint, selectedChoice)) { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowWhoisWindowFromAddress(RemoteEndpoint); + ShowWhoisWindowFromAddress(remoteEndpoint); break; } } @@ -528,6 +528,7 @@ VOID NTAPI TreeNewMessageCallback( if (countryBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) { extension->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); + DeleteObject(countryBitmap); } } } @@ -585,6 +586,7 @@ LOGICAL DllMain( 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 @@ -592,7 +594,6 @@ LOGICAL DllMain( { IntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_POSITION, L"0,0" }, { ScalableIntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_SIZE, L"@96|850,490" }, { 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" }, diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 64ff271dc3c5..d2ae5088edf9 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #define PLUGIN_NAME L"ProcessHacker.NetworkTools" #define SETTING_NAME_DB_TYPE (PLUGIN_NAME L".GeoIpType") +#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") @@ -52,7 +54,6 @@ #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") diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index a8deb46521b9..5b1a945edcc8 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -414,6 +414,7 @@ BOOLEAN NTAPI TracertTreeNewCallback( if (countryBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) { node->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); + DeleteObject(countryBitmap); } } } From 3281a6a0098c973a01e2355024eba1b42be26c13 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 14:07:04 +1000 Subject: [PATCH 247/839] BuildTools: Fix cleanup script bug --- build/build_clean.cmd | 2 - tools/CustomBuildTool/Source Files/Build.cs | 120 ++++++++---------- tools/CustomBuildTool/Source Files/Program.cs | 2 - tools/CustomBuildTool/Source Files/Utils.cs | 6 - .../bin/Release/CustomBuildTool.exe | Bin 160256 -> 160256 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 6 files changed, 53 insertions(+), 77 deletions(-) diff --git a/build/build_clean.cmd b/build/build_clean.cmd index 2d0f2c182fe3..40de19df02f2 100644 --- a/build/build_clean.cmd +++ b/build/build_clean.cmd @@ -3,5 +3,3 @@ @cd /d "%~dp0\..\" start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleanup" - -pause diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index b6cff3c57df3..f915c51c8408 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -228,10 +228,10 @@ public static void CleanupBuildEnvironment() try { - for (int i = 0; i < cleanupFileArray.Length; i++) + foreach (string file in cleanupFileArray) { - if (Directory.Exists(cleanupFileArray[i])) - Directory.Delete(cleanupFileArray[i], true); + if (Directory.Exists(file)) + Directory.Delete(file, true); } } catch (Exception ex) @@ -522,7 +522,7 @@ public static bool BuildPublicHeaderFiles() } catch (Exception ex) { - Program.PrintColorMessage("[ERROR] " + ex.ToString(), ConsoleColor.Red); + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); return false; } @@ -531,8 +531,6 @@ public static bool BuildPublicHeaderFiles() public static bool CopyKProcessHacker(BuildFlags Flags) { - Program.PrintColorMessage("Copying KPH driver...", ConsoleColor.Cyan, true, Flags); - if (!File.Exists(CustomSignToolPath)) return true; if (!File.Exists("build\\kph.key")) @@ -667,7 +665,7 @@ public static bool CopyKeyFiles() public static bool BuildSetupExe() { - Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan, true, BuildFlags.BuildVerbose); + Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan); if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildVerbose)) return false; @@ -918,7 +916,7 @@ public static void WebServiceUpdateConfig() if (!httpTask.Result.IsSuccessStatusCode) { - Program.PrintColorMessage("[UpdateBuildWebService] " + httpTask.Result.ToString(), ConsoleColor.Red); + Program.PrintColorMessage("[UpdateBuildWebService] " + httpTask.Result, ConsoleColor.Red); } } } @@ -946,14 +944,14 @@ public static bool AppveyorUploadBuildFiles() if (!BuildNightly) return false; - // Cleanup existing output files - for (int i = 0; i < releaseFileArray.Length; i++) + // Cleanup existing release files. + foreach (string file in releaseFileArray) { - if (File.Exists(releaseFileArray[i])) + if (File.Exists(file)) { try { - File.Delete(releaseFileArray[i]); + File.Delete(file); } catch (Exception ex) { @@ -963,7 +961,7 @@ public static bool AppveyorUploadBuildFiles() } } - // Rename files with the current build version -3.1- + // Rename build files with the current version processhacker-3.1-abc.ext for (int i = 0; i < buildFileArray.Length; i++) { if (File.Exists(buildFileArray[i])) @@ -980,16 +978,16 @@ public static bool AppveyorUploadBuildFiles() } } - // Upload build files to Appveyor storage. - for (int i = 0; i < releaseFileArray.Length; i++) + // Upload build files to download server. + foreach (string file in releaseFileArray) { - if (File.Exists(releaseFileArray[i])) + if (File.Exists(file)) { - Console.WriteLine("Uploading " + releaseFileArray[i] + "..."); + Console.WriteLine("Uploading " + file + "..."); try { - Win32.ShellExecute("appveyor", "PushArtifact " + releaseFileArray[i]); + Win32.ShellExecute("appveyor", "PushArtifact " + file); } catch (Exception ex) { @@ -999,13 +997,8 @@ public static bool AppveyorUploadBuildFiles() } } - try - { - // Update Appveyor build version string. - Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); - } - catch (Exception) - { } + // Update Appveyor build version string. + Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); return true; } @@ -1062,7 +1055,20 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) #region Appx Package public static void BuildAppxPackage(BuildFlags Flags) { + 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); + string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); try @@ -1088,10 +1094,8 @@ public static void BuildAppxPackage(BuildFlags Flags) packageMap32.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); var filesToAdd = Directory.GetFiles("bin\\Release32", "*", SearchOption.AllDirectories); - for (int i = 0; i < filesToAdd.Length; i++) + foreach (string filePath in filesToAdd) { - string filePath = filesToAdd[i]; - // Ignore junk files if (filePath.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || filePath.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || @@ -1103,13 +1107,13 @@ public static void BuildAppxPackage(BuildFlags Flags) } packageMap32.AppendLine("\"" + filePath + "\" \"" + filePath.Substring("bin\\Release32\\".Length) + "\""); - } + } File.WriteAllText(BuildOutputFolder + "\\package32.map", packageMap32.ToString()); // create the package Win32.ShellExecute( MakeAppxExePath, - "pack /o /f " + BuildOutputFolder + "\\package32.map /p " + + "pack /o /f " + BuildOutputFolder + "\\package32.map /p " + BuildOutputFolder + "\\processhacker-build-package-x32.appx" ); //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); @@ -1117,7 +1121,7 @@ public static void BuildAppxPackage(BuildFlags Flags) // sign the package Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + "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); @@ -1139,10 +1143,8 @@ public static void BuildAppxPackage(BuildFlags Flags) packageMap64.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); var filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); - for (int i = 0; i < filesToAdd.Length; i++) + foreach (string filePath in filesToAdd) { - string filePath = filesToAdd[i]; - // Ignore junk files if (filePath.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || filePath.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || @@ -1160,7 +1162,7 @@ public static void BuildAppxPackage(BuildFlags Flags) // create the package Win32.ShellExecute( MakeAppxExePath, - "pack /o /f " + BuildOutputFolder + "\\package64.map /p " + + "pack /o /f " + BuildOutputFolder + "\\package64.map /p " + BuildOutputFolder + "\\processhacker-build-package-x64.appx" ); //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); @@ -1168,7 +1170,7 @@ public static void BuildAppxPackage(BuildFlags Flags) // sign the package Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + "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); @@ -1188,7 +1190,7 @@ public static void BuildAppxPackage(BuildFlags Flags) // create the appx bundle package Win32.ShellExecute( MakeAppxExePath, - "bundle /f " + BuildOutputFolder + "\\bundle.map /p " + + "bundle /f " + BuildOutputFolder + "\\bundle.map /p " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); @@ -1196,33 +1198,17 @@ public static void BuildAppxPackage(BuildFlags Flags) // sign the appx bundle package Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } - try + foreach (string file in cleanupAppxArray) { - 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" - }; - - for (int i = 0; i < cleanupAppxArray.Length; i++) - { - if (File.Exists(cleanupAppxArray[i])) - File.Delete(cleanupAppxArray[i]); - } + if (File.Exists(file)) + File.Delete(file); } - catch (Exception) { } } catch (Exception ex) { @@ -1232,6 +1218,13 @@ public static void BuildAppxPackage(BuildFlags Flags) public static bool BuildAppxSignature() { + string[] cleanupAppxArray = + { + BuildOutputFolder + "\\processhacker-appx.pvk", + BuildOutputFolder + "\\processhacker-appx.cer", + BuildOutputFolder + "\\processhacker-appx.pfx" + }; + Program.PrintColorMessage("Building Appx Signature...", ConsoleColor.Cyan); var makeCertExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeCert.exe"); @@ -1239,17 +1232,10 @@ public static bool BuildAppxSignature() try { - string[] cleanupAppxArray = - { - BuildOutputFolder + "\\processhacker-appx.pvk", - BuildOutputFolder + "\\processhacker-appx.cer", - BuildOutputFolder + "\\processhacker-appx.pfx" - }; - - for (int i = 0; i < cleanupAppxArray.Length; i++) + foreach (string file in cleanupAppxArray) { - if (File.Exists(cleanupAppxArray[i])) - File.Delete(cleanupAppxArray[i]); + if (File.Exists(file)) + File.Delete(file); } string output = Win32.ShellExecute(makeCertExePath, diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 2633a5abf563..18cd8d9e43b6 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -352,8 +352,6 @@ private static bool Restart(string Arguments) if (principal.IsInRole(WindowsBuiltInRole.Administrator)) return false; - Win32.ShowWindow(Win32.GetConsoleWindow(), Win32.SW_HIDE); - try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 55a870ce71e8..dd7677d380e8 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -107,8 +107,6 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) } } - public const int SW_HIDE = 0; - public const int SW_SHOW = 5; public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); public static readonly IntPtr STD_OUTPUT_HANDLE = new IntPtr(-11); public static readonly IntPtr STD_INPUT_HANDLE = new IntPtr(-10); @@ -122,10 +120,6 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) public static extern bool SetConsoleMode(IntPtr ConsoleHandle, ConsoleMode Mode); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GetConsoleWindow(); - [DllImport("user32.dll", ExactSpelling = true)] - public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - public static extern int MessageBox(IntPtr hWnd, string m, string c, int type); } public static class Json where T : class diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 604822da66d7cf6ba84cfa11fd1f7a9e9515ee69..ac40ae99e063999800bb18b2a9740a8b0b41da61 100644 GIT binary patch delta 16322 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 delta 16535 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 QnYv*n{pEsi^Ttj8514)mAOHXW delta 13990 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 PKg8_)OZ!ms!9D*Ai`+4q From 68c42c23acd14cb79140b924d7700641c40820fd Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 2 Jul 2017 14:42:24 +1000 Subject: [PATCH 248/839] NetworkTools: Remove whois dialog uxtheme hack --- plugins/NetworkTools/whois.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 3cca5ef466df..0649bd0a9168 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -402,10 +402,7 @@ INT_PTR CALLBACK NetworkOutputDlgProc( 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_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); From 7b1f8b32181da651952497599e8ebb85a7441560 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 3 Jul 2017 01:57:44 +1000 Subject: [PATCH 249/839] Improve ImageBaseAddress handling, Fix bootstrap resource lookup (1/2) --- ProcessHacker/ProcessHacker.def | 2 +- ProcessHacker/chproc.c | 3 +++ ProcessHacker/findobj.c | 4 ++-- ProcessHacker/hndlprp.c | 2 ++ ProcessHacker/include/phapp.h | 1 - ProcessHacker/infodlg.c | 4 ++-- ProcessHacker/jobprp.c | 4 ++++ ProcessHacker/logwnd.c | 4 ++-- ProcessHacker/main.c | 21 +++++++--------- ProcessHacker/mainwnd.c | 10 ++++---- ProcessHacker/memedit.c | 4 ++-- ProcessHacker/memrslt.c | 4 ++-- ProcessHacker/miniinfo.c | 8 +++---- ProcessHacker/ntobjprp.c | 1 + ProcessHacker/options.c | 6 +++++ ProcessHacker/plugin.c | 36 +++++++++++----------------- ProcessHacker/procprp.c | 6 ++--- ProcessHacker/procrec.c | 2 +- ProcessHacker/prpgenv.c | 4 ++-- ProcessHacker/prpggen.c | 4 ++-- ProcessHacker/prpgthrd.c | 2 +- ProcessHacker/splitter.c | 4 ++-- ProcessHacker/srvlist.c | 8 +++---- ProcessHacker/srvprp.c | 2 ++ ProcessHacker/sysinfo.c | 4 ++-- ProcessHacker/thrdstk.c | 4 ++-- ProcessHacker/tokprp.c | 8 +++++++ phlib/colorbox.c | 2 +- phlib/global.c | 8 +++---- phlib/graph.c | 4 ++-- phlib/hexedit.c | 2 +- phlib/include/phconfig.h | 3 ++- phlib/native.c | 2 +- phlib/treenew.c | 2 +- phlib/util.c | 4 ++-- plugins/ExtendedTools/gpunodes.c | 4 ++-- plugins/ExtendedTools/unldll.c | 4 ++-- plugins/ExtraPlugins/dialog.c | 4 ++-- plugins/ExtraPlugins/setup/updater.c | 4 ++-- plugins/NetworkTools/ping.c | 4 ++-- plugins/NetworkTools/tracert.c | 4 ++-- plugins/NetworkTools/update.c | 4 ++-- plugins/NetworkTools/whois.c | 4 ++-- plugins/OnlineChecks/main.c | 2 +- plugins/OnlineChecks/upload.c | 4 ++-- plugins/Updater/options.c | 4 ++-- plugins/Updater/updater.c | 4 ++-- tools/peview/main.c | 2 +- tools/peview/prpsh.c | 4 ++-- tools/peview/searchbox.c | 8 +++---- 50 files changed, 132 insertions(+), 117 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 6804648802d8..9e693cb33b12 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -40,7 +40,7 @@ EXPORTS PhGlobalDpi DATA PhHeapHandle DATA PhIsExecutingInWow64 - PhLibImageBase DATA + PhInstanceHandle DATA PhOsVersion DATA PhSystemBasicInformation DATA ProcessAllAccess DATA diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index a39911ba828e..e9f3c532d482 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -205,6 +205,9 @@ INT_PTR CALLBACK PhpChooseProcessDlgProc( { 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)); SetDlgItemText(hwndDlg, IDC_MESSAGE, context->Message); diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 77e5ed193412..57a673c25e03 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -215,8 +215,8 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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)); PhFindObjectsListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); diff --git a/ProcessHacker/hndlprp.c b/ProcessHacker/hndlprp.c index 339a95476d44..e98ce8c949ad 100644 --- a/ProcessHacker/hndlprp.c +++ b/ProcessHacker/hndlprp.c @@ -94,6 +94,7 @@ VOID PhShowHandleProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = L"Handle"; propSheetHeader.nPages = 0; @@ -104,6 +105,7 @@ VOID PhShowHandleProperties( 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); diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 0237a4cb5ff1..82099e4e0225 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -76,7 +76,6 @@ 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; diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 8c8cf487cc57..73b8fa1916d3 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -46,8 +46,8 @@ static INT_PTR CALLBACK PhpInformationDlgProc( PINFORMATION_CONTEXT context = (PINFORMATION_CONTEXT)lParam; PPH_LAYOUT_MANAGER layoutManager; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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)); diff --git a/ProcessHacker/jobprp.c b/ProcessHacker/jobprp.c index a9668838d10d..a8151338c781 100644 --- a/ProcessHacker/jobprp.c +++ b/ProcessHacker/jobprp.c @@ -73,6 +73,7 @@ VOID PhShowJobProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = Title ? Title : L"Job"; propSheetHeader.nPages = 1; @@ -104,6 +105,7 @@ HPROPSHEETPAGE PhCreateJobPage( 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; @@ -500,6 +502,7 @@ VOID PhpShowJobAdvancedProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = L"Job"; propSheetHeader.nPages = 2; @@ -511,6 +514,7 @@ VOID PhpShowJobAdvancedProperties( 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); diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 1bcfe90a3f01..74d7e4fe7289 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -156,8 +156,8 @@ INT_PTR CALLBACK PhpLogDlgProc( { case WM_INITDIALOG: { - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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); diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 1515bf3caefb..2ed7ebff5408 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -75,11 +75,10 @@ VOID PhpEnablePrivileges( VOID ); -PPH_STRING PhApplicationDirectory; -PPH_STRING PhApplicationFileName; -PHAPPAPI HFONT PhApplicationFont; +PPH_STRING PhApplicationDirectory = NULL; +PPH_STRING PhApplicationFileName = NULL; +PHAPPAPI HFONT PhApplicationFont = NULL; PPH_STRING PhCurrentUserName = NULL; -HINSTANCE PhInstanceHandle; PPH_STRING PhLocalSystemName = NULL; BOOLEAN PhPluginsEnabled = FALSE; PPH_STRING PhSettingsFileName = NULL; @@ -95,10 +94,10 @@ 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 + _In_ HINSTANCE Instance, + _In_opt_ HINSTANCE PrevInstance, + _In_ PWSTR CmdLine, + _In_ INT CmdShow ) { LONG result; @@ -112,9 +111,7 @@ INT WINAPI wWinMain( SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #endif - PhInstanceHandle = (HINSTANCE)NtCurrentPeb()->ImageBaseAddress; - - if (!NT_SUCCESS(PhInitializePhLib())) + if (!NT_SUCCESS(PhInitializePhLibEx(ULONG_MAX, Instance, 0, 0))) return 1; if (!PhInitializeAppSystem()) return 1; @@ -287,7 +284,7 @@ INT WINAPI wWinMain( NtSetInformationProcess(NtCurrentProcess(), ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); } - if (!PhMainWndInitialization(nCmdShow)) + if (!PhMainWndInitialization(CmdShow)) { PhShowError(NULL, L"Unable to initialize the main window."); return 1; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index f73c3b15d263..6c2c41e4ea6a 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -352,8 +352,8 @@ RTL_ATOM PhMwpInitializeWindowClass( wcex.lpszClassName = PhGetStringOrDefault(className, L"MainWindowClassName"); wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAINWND); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); return RegisterClassEx(&wcex); } @@ -429,7 +429,7 @@ VOID PhMwpInitializeControls( 3, PhMainWndHandle, NULL, - PhLibImageBase, + PhInstanceHandle, NULL ); BringWindowToTop(PhMwpProcessTreeNewHandle); @@ -444,7 +444,7 @@ VOID PhMwpInitializeControls( 3, PhMainWndHandle, NULL, - PhLibImageBase, + PhInstanceHandle, NULL ); BringWindowToTop(PhMwpServiceTreeNewHandle); @@ -459,7 +459,7 @@ VOID PhMwpInitializeControls( 3, PhMainWndHandle, NULL, - PhLibImageBase, + PhInstanceHandle, NULL ); BringWindowToTop(PhMwpNetworkTreeNewHandle); diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index b76344d1c68a..1d78c2caa17b 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -189,8 +189,8 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( { NTSTATUS status; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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) { diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index a066c5770f2b..cb3c8b38e367 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -279,8 +279,8 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 3a7b394662f0..11be6ea18103 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -110,11 +110,11 @@ VOID PhPinMiniInformation( wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = PhInstanceHandle; - wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; - wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( @@ -448,10 +448,10 @@ VOID PhMipOnInitDialog( HICON cog; HICON pin; - cog = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); + cog = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); - pin = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PIN)); + pin = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PIN)); SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index 5235537c7e18..b2e892f6550f 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -108,6 +108,7 @@ static HPROPSHEETPAGE PhpCommonCreatePage( propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USECALLBACK; propSheetPage.pszTemplate = Template; + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = DlgProc; propSheetPage.lParam = (LPARAM)pageContext; propSheetPage.pfnCallback = PhpCommonPropPageProc; diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 4f27c293ce3e..21944675fa3e 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -121,6 +121,7 @@ VOID PhShowOptionsDialog( PSH_NOCONTEXTHELP | PSH_USECALLBACK | PSH_USEPSTARTPAGE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = L"Options"; propSheetHeader.nPages = 0; @@ -135,6 +136,7 @@ VOID PhShowOptionsDialog( memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTGENERAL); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = PhpOptionsGeneralDlgProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); } @@ -143,6 +145,7 @@ VOID PhShowOptionsDialog( memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTADVANCED); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = PhpOptionsAdvancedDlgProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -152,6 +155,7 @@ VOID PhShowOptionsDialog( memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTSYMBOLS); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = PhpOptionsSymbolsDlgProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); } @@ -162,6 +166,7 @@ VOID PhShowOptionsDialog( memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = PhpOptionsHighlightingDlgProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); } @@ -172,6 +177,7 @@ VOID PhShowOptionsDialog( memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTGRAPHS); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = PhpOptionsGraphsDlgProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); } diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 167a919d1454..495b5eeab934 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -291,8 +291,6 @@ VOID PhLoadPlugins( sb.String->Buffer ) == IDYES) { - ULONG i; - for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; @@ -334,43 +332,37 @@ BOOLEAN PhLoadPlugin( _In_ PPH_STRING FileName ) { - BOOLEAN success; PPH_STRING fileName; PPH_STRING errorMessage; + PPHP_PLUGIN_LOAD_ERROR loadError; fileName = PhGetFullPath(FileName->Buffer, NULL); if (!fileName) PhSetReference(&fileName, FileName); - success = TRUE; - - if (!LoadLibrary(fileName->Buffer)) + if (LoadLibrary(fileName->Buffer)) { - success = FALSE; - errorMessage = PhGetWin32Message(GetLastError()); + PhDereferenceObject(fileName); + return TRUE; } - if (!success) - { - PPHP_PLUGIN_LOAD_ERROR loadError; + errorMessage = PhGetWin32Message(GetLastError()); - loadError = PhAllocate(sizeof(PHP_PLUGIN_LOAD_ERROR)); - PhSetReference(&loadError->FileName, fileName); - PhSetReference(&loadError->ErrorMessage, errorMessage); + loadError = PhAllocate(sizeof(PHP_PLUGIN_LOAD_ERROR)); + PhSetReference(&loadError->FileName, fileName); + PhSetReference(&loadError->ErrorMessage, errorMessage); - if (!LoadErrors) - LoadErrors = PhCreateList(2); + if (!LoadErrors) + LoadErrors = PhCreateList(2); - PhAddItemList(LoadErrors, loadError); + PhAddItemList(LoadErrors, loadError); - if (errorMessage) - PhDereferenceObject(errorMessage); - } + if (errorMessage) + PhDereferenceObject(errorMessage); PhDereferenceObject(fileName); - - return success; + return FALSE; } VOID PhpExecuteCallbackForAllPlugins( diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index ae884db625e5..2380d83ec95b 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -85,6 +85,7 @@ PPH_PROCESS_PROPCONTEXT PhCreateProcessPropContext( PSH_PROPTITLE | PSH_USECALLBACK | PSH_USEHICON; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.hIcon = ProcessItem->SmallIcon; propSheetHeader.pszCaption = propContext->Title->Buffer; @@ -380,7 +381,7 @@ PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContext( _In_opt_ PVOID Context ) { - return PhCreateProcessPropPageContextEx(NULL, Template, DlgProc, Context); + return PhCreateProcessPropPageContextEx(PhInstanceHandle, Template, DlgProc, Context); } PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContextEx( @@ -396,8 +397,7 @@ PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContextEx( memset(propPageContext, 0, sizeof(PH_PROCESS_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; diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index dbcb2c98019a..07268bea41bb 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -186,7 +186,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( 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(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); + (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_FOLDER))); SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)context->FileIcon, 0); diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index 3806999f8fd1..cb16b9606a21 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -452,8 +452,8 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( { case WM_INITDIALOG: { - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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)); diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index d8a6a1dc9be7..f780a952ead1 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -147,8 +147,8 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( HICON folder; HICON magnifier; - folder = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER)); - magnifier = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, 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); diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 35601d2d7fdf..7b35b201c716 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -615,7 +615,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PhThreadProviderInitialUpdate(threadsContext->Provider); PhRegisterThreadProvider(threadsContext->Provider, &threadsContext->ProviderRegistration); - SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_FOLDER))); + SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_FOLDER))); } break; case WM_DESTROY: diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 930ebb1d450a..0516fc2cd81d 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -238,7 +238,7 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( c.lpfnWndProc = HSplitterWindowProc; c.cbClsExtra = 0; c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; + c.hInstance = PhInstanceHandle; c.hIcon = NULL; c.hCursor = LoadCursor(NULL, IDC_SIZENS); c.hbrBackground = NULL; @@ -262,7 +262,7 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( 10, ParentWindow, NULL, - PhLibImageBase, + PhInstanceHandle, context ); diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 1e2eaeb655df..eb10d23e993a 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -689,10 +689,10 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( if (!ServiceIconsLoaded) { - ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATION)); - ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); - ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COG)); - ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_COGGO)); + ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PHAPPLICATION)); + ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); + ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); + ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COGGO)); ServiceIconsLoaded = TRUE; } diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index 17508959dcc5..e9cc3f1ac26c 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -114,6 +114,7 @@ VOID PhShowServiceProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = ServiceItem->Name->Buffer; propSheetHeader.nPages = 0; @@ -130,6 +131,7 @@ VOID PhShowServiceProperties( 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); diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 1f6184e83044..4e8f019f4ff3 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -281,8 +281,8 @@ VOID PhSipOnInitDialog( VOID ) { - SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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))); PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 35ac287e38a5..050eaf9267ff 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -169,8 +169,8 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( HWND lvHandle; PPH_LAYOUT_MANAGER layoutManager; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + 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))); threadStackContext = (PTHREAD_STACK_CONTEXT)lParam; SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext); diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index d5b6caa775db..bfbd1f002a85 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -137,6 +137,7 @@ VOID PhShowTokenProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = Title ? Title : L"Token"; propSheetHeader.nPages = 1; @@ -168,6 +169,7 @@ HPROPSHEETPAGE PhCreateTokenPage( 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; @@ -948,6 +950,7 @@ VOID PhpShowTokenAdvancedProperties( PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = L"Token"; propSheetHeader.nStartPage = 0; @@ -960,6 +963,7 @@ VOID PhpShowTokenAdvancedProperties( 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); @@ -969,6 +973,7 @@ VOID PhpShowTokenAdvancedProperties( 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); @@ -980,6 +985,7 @@ VOID PhpShowTokenAdvancedProperties( 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); @@ -990,6 +996,7 @@ VOID PhpShowTokenAdvancedProperties( 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; @@ -1001,6 +1008,7 @@ VOID PhpShowTokenAdvancedProperties( 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; diff --git a/phlib/colorbox.c b/phlib/colorbox.c index 530690e9b458..89cdcdac3cc1 100644 --- a/phlib/colorbox.c +++ b/phlib/colorbox.c @@ -51,7 +51,7 @@ BOOLEAN PhColorBoxInitialization( c.lpfnWndProc = PhpColorBoxWndProc; c.cbClsExtra = 0; c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; + c.hInstance = PhInstanceHandle; c.hIcon = NULL; c.hCursor = LoadCursor(NULL, IDC_ARROW); c.hbrBackground = NULL; diff --git a/phlib/global.c b/phlib/global.c index 0afc6504abc1..8d8ed1026c9a 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -38,8 +38,7 @@ VOID PhInitializeWindowsVersion( VOID ); -PHLIBAPI PVOID PhLibImageBase; - +PHLIBAPI PVOID PhInstanceHandle; PHLIBAPI PWSTR PhApplicationName = L"Application"; PHLIBAPI ULONG PhGlobalDpi = 96; PHLIBAPI PVOID PhHeapHandle; @@ -64,6 +63,7 @@ NTSTATUS PhInitializePhLib( { return PhInitializePhLibEx( 0xffffffff, // all possible features + NtCurrentPeb()->ImageBaseAddress, 0, 0 ); @@ -71,10 +71,12 @@ NTSTATUS PhInitializePhLib( NTSTATUS PhInitializePhLibEx( _In_ ULONG Flags, + _In_ PVOID ImageBaseAddress, _In_opt_ SIZE_T HeapReserveSize, _In_opt_ SIZE_T HeapCommitSize ) { + PhInstanceHandle = ImageBaseAddress; PhHeapHandle = RtlCreateHeap( HEAP_GROWABLE | HEAP_CLASS_1, NULL, @@ -87,8 +89,6 @@ NTSTATUS PhInitializePhLibEx( if (!PhHeapHandle) return STATUS_INSUFFICIENT_RESOURCES; - PhLibImageBase = NtCurrentPeb()->ImageBaseAddress; - PhInitializeWindowsVersion(); PhInitializeSystemInformation(); diff --git a/phlib/graph.c b/phlib/graph.c index 8c47136d2ead..b74eeab067c1 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -76,7 +76,7 @@ BOOLEAN PhGraphControlInitialization( c.lpfnWndProc = PhpGraphWndProc; c.cbClsExtra = 0; c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; + c.hInstance = PhInstanceHandle; c.hIcon = NULL; c.hCursor = LoadCursor(NULL, IDC_ARROW); c.hbrBackground = NULL; @@ -1189,7 +1189,7 @@ LRESULT CALLBACK PhpGraphWndProc( CW_USEDEFAULT, NULL, NULL, - PhLibImageBase, + PhInstanceHandle, NULL ); diff --git a/phlib/hexedit.c b/phlib/hexedit.c index edd89248995d..82c1d614d83f 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -39,7 +39,7 @@ BOOLEAN PhHexEditInitialization( c.lpfnWndProc = PhpHexEditWndProc; c.cbClsExtra = 0; c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; + c.hInstance = PhInstanceHandle; c.hIcon = NULL; c.hCursor = LoadCursor(NULL, IDC_ARROW); c.hbrBackground = NULL; diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 420d3ed97dfc..8daf5185a7a6 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -7,7 +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; @@ -75,6 +75,7 @@ NTSTATUS NTAPI PhInitializePhLibEx( _In_ ULONG Flags, + _In_ PVOID ImageBaseAddress, _In_opt_ SIZE_T HeapReserveSize, _In_opt_ SIZE_T HeapCommitSize ); diff --git a/phlib/native.c b/phlib/native.c index 24e067a783f2..9366ab375b56 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4651,7 +4651,7 @@ NTSTATUS PhEnumDirectoryFile( { PFILE_DIRECTORY_INFORMATION information; - information = (PFILE_DIRECTORY_INFORMATION)(PTR_ADD_OFFSET(buffer, i)); + information = (PFILE_DIRECTORY_INFORMATION)PTR_ADD_OFFSET(buffer, i); if (!Callback( information, diff --git a/phlib/treenew.c b/phlib/treenew.c index f5bef8b20da0..bd96c52cfcc6 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -66,7 +66,7 @@ BOOLEAN PhTreeNewInitialization( c.lpfnWndProc = PhTnpWndProc; c.cbClsExtra = 0; c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; + c.hInstance = PhInstanceHandle; c.hIcon = NULL; c.hCursor = LoadCursor(NULL, IDC_ARROW); c.hbrBackground = NULL; diff --git a/phlib/util.c b/phlib/util.c index 8084ead139cf..44ba3a99ee4a 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -425,7 +425,7 @@ INT PhShowMessage2( return -1; config.hwndParent = hWnd; - config.hInstance = PhLibImageBase; + config.hInstance = PhInstanceHandle; config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); config.dwCommonButtons = Buttons; config.pszWindowTitle = PhApplicationName; @@ -614,7 +614,7 @@ BOOLEAN PhShowConfirmMessage( INT button; config.hwndParent = hWnd; - config.hInstance = PhLibImageBase; + config.hInstance = PhInstanceHandle; config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); config.pszWindowTitle = PhApplicationName; config.pszMainIcon = Warning ? TD_WARNING_ICON : NULL; diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index 1526bd358a45..fa1292a44ace 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -121,8 +121,8 @@ INT_PTR CALLBACK EtpGpuNodesDlgProc( WindowHandle = hwndDlg; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index f7643e977197..7640363288c0 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -306,8 +306,8 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( { HWND lvHandle; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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)); diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index 1e0c1b42c6b2..829a96ed8c49 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -232,8 +232,8 @@ INT_PTR CALLBACK CloudPluginsDlgProc( { case WM_INITDIALOG: { - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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->DialogHandle = hwndDlg; context->PluginMenuActiveId = IDC_INSTALLED; diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 7dfb62bb0acd..0dc634b825aa 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -60,8 +60,8 @@ VOID TaskDialogCreateIcons( ) { // Load the Process Hacker window icon - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); // Set the TaskDialog window icons SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 4dce3d99221e..0d94de17499a 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -285,8 +285,8 @@ INT_PTR CALLBACK NetworkPingWndProc( { PPH_LAYOUT_ITEM panelItem; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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 diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 1a0f3abff127..dd98669b9af2 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -566,8 +566,8 @@ INT_PTR CALLBACK TracertDlgProc( { case WM_INITDIALOG: { - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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, PhMainWndHandle); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 57d728413f45..f57bd05db5ba 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -50,8 +50,8 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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); diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 0649bd0a9168..0f8e0bf73469 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -394,8 +394,8 @@ INT_PTR CALLBACK NetworkOutputDlgProc( { case WM_INITDIALOG: { - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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); diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 652594742bfa..a7ec2e294b99 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -178,7 +178,7 @@ VOID NTAPI MenuItemCallback( 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(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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."; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 542063e168f3..11948fefec83 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -205,8 +205,8 @@ VOID TaskDialogCreateIcons( _In_ PUPLOAD_CONTEXT Context ) { - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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); diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 5b9deb95b995..de429ec1e990 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -86,8 +86,8 @@ INT_PTR CALLBACK TextDlgProc( { PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)lParam; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + 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(GetDlgItem(hwndDlg, IDC_TEXT), PhGetString(context->BuildMessage)); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 7e65a8ee4807..9affa094d471 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -70,8 +70,8 @@ VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhLibImageBase, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + 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); diff --git a/tools/peview/main.c b/tools/peview/main.c index a8de840ebf1a..f3bf35f5b892 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -63,7 +63,7 @@ INT WINAPI wWinMain( }; PH_STRINGREF commandLine; - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) + if (!NT_SUCCESS(PhInitializePhLib())) return 1; PhGuiSupportInitialization(); diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 717f1d1f4222..f697d1bcb282 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -92,6 +92,7 @@ PPV_PROPCONTEXT PvCreatePropContext( PSH_NOCONTEXTHELP | PSH_PROPTITLE | PSH_USECALLBACK; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = NULL; propSheetHeader.pszCaption = propContext->Title->Buffer; propSheetHeader.pfnCallback = PvpPropSheetProc; @@ -382,8 +383,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; diff --git a/tools/peview/searchbox.c b/tools/peview/searchbox.c index 17d89f752726..a758b0e2816b 100644 --- a/tools/peview/searchbox.c +++ b/tools/peview/searchbox.c @@ -323,23 +323,23 @@ VOID PhpSearchInitializeImages( Context->ImageWidth = GetSystemMetrics(SM_CXSMICON) + 4; Context->ImageHeight = GetSystemMetrics(SM_CYSMICON) + 4; - if (bitmap = PhLoadPngImageFromResource(PhLibImageBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) { Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(bitmap); } - else if (bitmap = LoadImage(PhLibImageBase, 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 = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(bitmap); } - if (bitmap = PhLoadPngImageFromResource(PhLibImageBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) { Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(bitmap); } - else if (bitmap = LoadImage(PhLibImageBase, 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 = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); DeleteObject(bitmap); From 518c283b9bfc31ac0cdd7ad6dc234ee83498fe07 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 3 Jul 2017 02:02:58 +1000 Subject: [PATCH 250/839] peview: Fix typo --- tools/peview/include/pdb.h | 2 +- tools/peview/include/peview.h | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h index 5b04419d4d39..f4891ac22cd1 100644 --- a/tools/peview/include/pdb.h +++ b/tools/peview/include/pdb.h @@ -1,6 +1,6 @@ /* * Process Hacker - - * property sheet + * PE viewer * * Copyright (C) 2017 dmex * diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index d076b7649ac5..b3cc6ccdab5d 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -1,3 +1,26 @@ +/* + * 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 From a47c56a700d25b1334cc2a4804a25b9a60d5d863 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 3 Jul 2017 02:11:12 +1000 Subject: [PATCH 251/839] Setup: Update to lastet sdk --- .../CustomSetupTool/CustomSetupTool/appsup.c | 20 +++++++++---------- .../CustomSetupTool/CustomSetupTool/extract.c | 6 +++--- tools/CustomSetupTool/CustomSetupTool/main.c | 18 +++++++++++------ tools/CustomSetupTool/CustomSetupTool/page1.c | 4 ++-- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 92bdf7e63846..e4a887d1f3c5 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -34,12 +34,12 @@ VOID ExtractResourceToFile( PVOID resourceBuffer; IO_STATUS_BLOCK isb; - if (!(resourceHandle = FindResource(PhLibImageBase, Resource, RT_RCDATA))) + if (!(resourceHandle = FindResource(PhInstanceHandle, Resource, RT_RCDATA))) goto CleanupExit; - resourceLength = SizeofResource(PhLibImageBase, resourceHandle); + resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) + if (!(resourceData = LoadResource(PhInstanceHandle, resourceHandle))) goto CleanupExit; if (!(resourceBuffer = LockResource(resourceData))) @@ -95,12 +95,12 @@ PVOID ExtractResourceToBuffer( PVOID resourceBuffer; PVOID buffer = NULL; - if (!(resourceHandle = FindResource(PhLibImageBase, Resource, RT_RCDATA))) + if (!(resourceHandle = FindResource(PhInstanceHandle, Resource, RT_RCDATA))) goto CleanupExit; - resourceLength = SizeofResource(PhLibImageBase, resourceHandle); + resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) + if (!(resourceData = LoadResource(PhInstanceHandle, resourceHandle))) goto CleanupExit; if (!(resourceBuffer = LockResource(resourceData))) @@ -148,14 +148,14 @@ HBITMAP LoadPngImageFromResources( goto CleanupExit; // Find the resource - if ((resourceHandleSource = FindResource(PhLibImageBase, Name, L"PNG")) == NULL) + if ((resourceHandleSource = FindResource(PhInstanceHandle, Name, L"PNG")) == NULL) goto CleanupExit; // Get the resource length - resourceLength = SizeofResource(PhLibImageBase, resourceHandleSource); + resourceLength = SizeofResource(PhInstanceHandle, resourceHandleSource); // Load the resource - if ((resourceHandle = LoadResource(PhLibImageBase, resourceHandleSource)) == NULL) + if ((resourceHandle = LoadResource(PhInstanceHandle, resourceHandleSource)) == NULL) goto CleanupExit; if ((resourceBuffer = (WICInProcPointer)LockResource(resourceHandle)) == NULL) @@ -390,7 +390,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; diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 850a2322074c..6d7113314572 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -30,12 +30,12 @@ PVOID GetZipResourceData( HGLOBAL resourceData; PVOID resourceBuffer = NULL; - if (!(resourceHandle = FindResource(PhLibImageBase, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA))) + if (!(resourceHandle = FindResource(PhInstanceHandle, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA))) goto CleanupExit; - *resourceLength = SizeofResource(PhLibImageBase, resourceHandle); + *resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) + if (!(resourceData = LoadResource(PhInstanceHandle, resourceHandle))) goto CleanupExit; if (!(resourceBuffer = LockResource(resourceData))) diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index fe45e4258011..7daea4ac97a9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -122,10 +122,10 @@ INT CALLBACK MainPropSheet_Callback( } INT WINAPI wWinMain( - _In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ PWSTR lpCmdLine, - _In_ INT nCmdShow + _In_ HINSTANCE Instance, + _In_opt_ HINSTANCE PrevInstance, + _In_ PWSTR CmdLine, + _In_ INT CmdShow ) { if (!NT_SUCCESS(CreateSetupMutant())) @@ -133,7 +133,7 @@ INT WINAPI wWinMain( CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) + if (!NT_SUCCESS(PhInitializePhLibEx(ULONG_MAX, Instance, 0, 0))) return 1; PhApplicationName = L"Process Hacker - Setup"; @@ -167,9 +167,9 @@ INT WINAPI wWinMain( HPROPSHEETPAGE pages[6]; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USECALLBACK | PSH_WIZARD_LITE; - propSheetHeader.hInstance = PhLibImageBase; propSheetHeader.pszIcon = MAKEINTRESOURCE(IDI_ICON1); propSheetHeader.pfnCallback = MainPropSheet_Callback; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.phpage = pages; // welcome page @@ -178,6 +178,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG1); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupPropPage1_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -187,6 +188,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupPropPage2_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -196,6 +198,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupPropPage3_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -205,6 +208,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG5); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupPropPage5_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -214,6 +218,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupPropPage4_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); @@ -223,6 +228,7 @@ INT WINAPI wWinMain( propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_ERROR); + propSheetPage.hInstance = PhInstanceHandle; propSheetPage.pfnDlgProc = SetupErrorPage_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/page1.c index 526f37d9d727..e473cf75ecaa 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/page1.c @@ -36,7 +36,7 @@ static VOID SetupLoadIcons( ) { HBITMAP smallIconHandle = (HBITMAP)LoadImage( - PhLibImageBase, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), @@ -44,7 +44,7 @@ static VOID SetupLoadIcons( LR_DEFAULTCOLOR ); HBITMAP largeIconHandle = (HBITMAP)LoadImage( - PhLibImageBase, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), From 30df8107a0ec1ca92e0656d5881799be0b2adb3b Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 3 Jul 2017 17:36:23 +1000 Subject: [PATCH 252/839] Fix DLL_INIT_BLOCK version check --- ProcessHacker/memprv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 0cd374fce86d..881a5d2500e7 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -494,7 +494,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( PVOID cfgBitmapAddress = NULL; PVOID cfgBitmapWow64Address = NULL; - if (ldrInitBlock.Size == sizeof(PS_SYSTEM_DLL_INIT_BLOCK)) + if (ldrInitBlock.Size >= (ULONG)FIELD_OFFSET(PS_SYSTEM_DLL_INIT_BLOCK, Wow64CfgBitMap)) { cfgBitmapAddress = (PVOID)ldrInitBlock.CfgBitMap; cfgBitmapWow64Address = (PVOID)ldrInitBlock.Wow64CfgBitMap; From 8a7bb0f383f124e965e654ce1c03ee6d7a4d6447 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 6 Jul 2017 10:47:32 +1000 Subject: [PATCH 253/839] Add more support for the FileOpenExecutable setting --- ProcessHacker/hndlmenu.c | 10 +++++++++- ProcessHacker/mainwnd.c | 16 ++++++++++++++-- ProcessHacker/miniinfo.c | 10 +++++++++- ProcessHacker/procrec.c | 10 +++++++++- ProcessHacker/prpgmod.c | 8 +++++++- ProcessHacker/prpgthrd.c | 8 +++++++- 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/hndlmenu.c b/ProcessHacker/hndlmenu.c index 8c842f7fd0d1..c4d2cdbef350 100644 --- a/ProcessHacker/hndlmenu.c +++ b/ProcessHacker/hndlmenu.c @@ -118,7 +118,15 @@ VOID PhShowHandleObjectProperties1( PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE)) { if (Info->BestObjectName) - PhShellExploreFile(hWnd, Info->BestObjectName->Buffer); + { + PhShellExecuteUserString( + PhMainWndHandle, + L"FileOpenExecutable", + 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."); } diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 6c2c41e4ea6a..0bca69a69d8f 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1309,7 +1309,13 @@ VOID PhMwpOnCommand( if (processItem && processItem->FileName) { PhReferenceObject(processItem); - PhShellExploreFile(PhMainWndHandle, processItem->FileName->Buffer); + PhShellExecuteUserString( + PhMainWndHandle, + L"FileOpenExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); PhDereferenceObject(processItem); } } @@ -1459,7 +1465,13 @@ VOID PhMwpOnCommand( if (fileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle)) { - PhShellExploreFile(PhMainWndHandle, fileName->Buffer); + PhShellExecuteUserString( + PhMainWndHandle, + L"FileOpenExecutable", + fileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); PhDereferenceObject(fileName); } diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 11be6ea18103..37becc411a4a 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1700,7 +1700,15 @@ BOOLEAN PhMipListSectionTreeNewCallback( else { if (node->ProcessGroup->Representative->FileName) - PhShellExploreFile(listSection->DialogHandle, node->ProcessGroup->Representative->FileName->Buffer); + { + PhShellExecuteUserString( + listSection->DialogHandle, + L"FileOpenExecutable", + node->ProcessGroup->Representative->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } } } break; diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 07268bea41bb..7dad38071e01 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -235,7 +235,15 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( case IDC_OPENFILENAME: { if (context->Record->FileName) - PhShellExploreFile(hwndDlg, context->Record->FileName->Buffer); + { + PhShellExecuteUserString( + PhMainWndHandle, + L"FileOpenExecutable", + context->Record->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } } break; case IDC_PROPERTIES: diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 2b2e217827c4..5efd8288fd26 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -633,7 +633,13 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( if (moduleItem) { - PhShellExploreFile(hwndDlg, moduleItem->FileName->Buffer); + PhShellExecuteUserString( + hwndDlg, + L"FileOpenExecutable", + moduleItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); } } break; diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 7b35b201c716..30670a03b358 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -996,7 +996,13 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( if (threadItem && threadItem->StartAddressFileName) { - PhShellExploreFile(hwndDlg, threadItem->StartAddressFileName->Buffer); + PhShellExecuteUserString( + hwndDlg, + L"FileOpenExecutable", + threadItem->StartAddressFileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); } } break; From 9290359081cebee1eee2215f8495c2d8295fa0b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 6 Jul 2017 10:50:13 +1000 Subject: [PATCH 254/839] Fix version bug, Remove non-existent KPH functions --- phlib/global.c | 4 ++-- phlib/include/kphuser.h | 35 ----------------------------------- phlib/include/phconfig.h | 9 ++++----- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/phlib/global.c b/phlib/global.c index 8d8ed1026c9a..26c69aa8a4a8 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -203,7 +203,7 @@ static VOID PhInitializeWindowsVersion( switch (buildVersion) { case 10240: - WindowsVersion = WINDOWS_10_TH1; + WindowsVersion = WINDOWS_10; break; case 10586: WindowsVersion = WINDOWS_10_TH2; @@ -215,7 +215,7 @@ static VOID PhInitializeWindowsVersion( WindowsVersion = WINDOWS_10_RS2; break; default: - WindowsVersion = WINDOWS_10; + WindowsVersion = WINDOWS_NEW; break; } } diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index a6905cb2f5ab..72eb102ed481 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -117,20 +117,6 @@ KphOpenProcessJob( _Out_ PHANDLE JobHandle ); -PHLIBAPI -NTSTATUS -NTAPI -KphSuspendProcess( - _In_ HANDLE ProcessHandle - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphResumeProcess( - _In_ HANDLE ProcessHandle - ); - PHLIBAPI NTSTATUS NTAPI @@ -189,14 +175,6 @@ KphOpenThreadProcess( _Out_ PHANDLE ProcessHandle ); -PHLIBAPI -NTSTATUS -NTAPI -KphTerminateThread( - _In_ HANDLE ThreadHandle, - _In_ NTSTATUS ExitStatus - ); - PHLIBAPI NTSTATUS NTAPI @@ -271,19 +249,6 @@ KphSetInformationObject( _In_ ULONG ObjectInformationLength ); -PHLIBAPI -NTSTATUS -NTAPI -KphDuplicateObject( - _In_ HANDLE SourceProcessHandle, - _In_ HANDLE SourceHandle, - _In_opt_ HANDLE TargetProcessHandle, - _Out_opt_ PHANDLE TargetHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ ULONG HandleAttributes, - _In_ ULONG Options - ); - PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 8daf5185a7a6..482921a90007 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -27,11 +27,10 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_7 61 #define WINDOWS_8 62 #define WINDOWS_8_1 63 -#define WINDOWS_10 100 -#define WINDOWS_10_TH1 101 -#define WINDOWS_10_TH2 102 -#define WINDOWS_10_RS1 103 -#define WINDOWS_10_RS2 104 +#define WINDOWS_10 100 // TH1 +#define WINDOWS_10_TH2 101 +#define WINDOWS_10_RS1 102 +#define WINDOWS_10_RS2 103 #define WINDOWS_NEW MAXLONG #define WINDOWS_HAS_IMMERSIVE (WindowsVersion >= WINDOWS_8) From 2cfb68356bd62539efa2b95f5f71289a6eb3c9a7 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 6 Jul 2017 13:05:40 +1000 Subject: [PATCH 255/839] Fix CreateProcess parameter usage --- phlib/util.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 44ba3a99ee4a..fee2e8b0dee0 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2583,6 +2583,7 @@ NTSTATUS PhCreateProcessWin32Ex( ) { NTSTATUS status; + PPH_STRING fileName = NULL; PPH_STRING commandLine = NULL; STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; @@ -2591,6 +2592,21 @@ NTSTATUS PhCreateProcessWin32Ex( if (CommandLine) // duplicate because CreateProcess modifies the string commandLine = PhCreateString(CommandLine); + if (FileName) + fileName = PhCreateString(FileName); + else + { + INT cmdlineArgCount; + PWSTR* cmdlineArgList; + + // Try extract the filename or CreateProcess might execute the wrong executable. + if (commandLine && (cmdlineArgList = CommandLineToArgvW(commandLine->Buffer, &cmdlineArgCount))) + { + PhMoveReference(&fileName, PhCreateString(cmdlineArgList[0])); + LocalFree(cmdlineArgList); + } + } + newFlags = 0; PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING)); @@ -2607,7 +2623,7 @@ NTSTATUS PhCreateProcessWin32Ex( if (!TokenHandle) { if (CreateProcess( - FileName, + PhGetString(fileName), PhGetString(commandLine), NULL, NULL, @@ -2626,7 +2642,7 @@ NTSTATUS PhCreateProcessWin32Ex( { if (CreateProcessAsUser( TokenHandle, - FileName, + PhGetString(fileName), PhGetString(commandLine), NULL, NULL, @@ -2642,6 +2658,8 @@ NTSTATUS PhCreateProcessWin32Ex( status = PhGetLastWin32ErrorAsNtStatus(); } + if (fileName) + PhDereferenceObject(fileName); if (commandLine) PhDereferenceObject(commandLine); From 036552a9e0d55052d584a5cf33ff910f75d6cc06 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 7 Jul 2017 12:30:40 +1000 Subject: [PATCH 256/839] Fix regression from previous commit --- ProcessHacker/appsup.c | 36 +++++++++++++++++++++++++++++++++++- phlib/util.c | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 4271dced71dc..f84bacb92f12 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -953,7 +954,40 @@ VOID PhShellExecuteUserString( // 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)); + { + 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"\"", PhApplicationDirectory->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"\"", PhApplicationDirectory->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)) diff --git a/phlib/util.c b/phlib/util.c index fee2e8b0dee0..0e68f3bd2136 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2599,7 +2599,7 @@ NTSTATUS PhCreateProcessWin32Ex( INT cmdlineArgCount; PWSTR* cmdlineArgList; - // Try extract the filename or CreateProcess might execute the wrong executable. + // (dmex) Try extract the filename or CreateProcess might execute the wrong executable. if (commandLine && (cmdlineArgList = CommandLineToArgvW(commandLine->Buffer, &cmdlineArgCount))) { PhMoveReference(&fileName, PhCreateString(cmdlineArgList[0])); From 4cca64a04c3571b9a4f81ae21d4d01ebcf6faf5c Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 7 Jul 2017 12:50:44 +1000 Subject: [PATCH 257/839] Temporarily disable KPH by default until dynamic loading support has been merged --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index fba8a2719d3e..807de6727f13 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -40,7 +40,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ElevationLevel", L"1"); // PromptElevateAction PhpAddIntegerSetting(L"EnableCycleCpuUsage", L"1"); PhpAddIntegerSetting(L"EnableInstantTooltips", L"0"); - PhpAddIntegerSetting(L"EnableKph", L"1"); + PhpAddIntegerSetting(L"EnableKph", L"0"); PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"1"); PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); From 431541c85c5acfba7eaf64cbbd986d9b6e90dbe6 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 7 Jul 2017 13:23:42 +1000 Subject: [PATCH 258/839] Fix regression launching explorer.exe --- ProcessHacker/appsup.c | 3 +++ ProcessHacker/hndlmenu.c | 2 +- ProcessHacker/mainwnd.c | 4 ++-- ProcessHacker/miniinfo.c | 2 +- ProcessHacker/procrec.c | 2 +- ProcessHacker/prpggen.c | 2 +- ProcessHacker/prpgmod.c | 2 +- ProcessHacker/prpgthrd.c | 2 +- ProcessHacker/settings.c | 2 +- 9 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index f84bacb92f12..4928ef98b55a 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -951,6 +951,9 @@ VOID PhShellExecuteUserString( 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) diff --git a/ProcessHacker/hndlmenu.c b/ProcessHacker/hndlmenu.c index c4d2cdbef350..802e41c26ff1 100644 --- a/ProcessHacker/hndlmenu.c +++ b/ProcessHacker/hndlmenu.c @@ -121,7 +121,7 @@ VOID PhShowHandleObjectProperties1( { PhShellExecuteUserString( PhMainWndHandle, - L"FileOpenExecutable", + L"FileBrowseExecutable", Info->BestObjectName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 0bca69a69d8f..f30c396d2eb5 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1311,7 +1311,7 @@ VOID PhMwpOnCommand( PhReferenceObject(processItem); PhShellExecuteUserString( PhMainWndHandle, - L"FileOpenExecutable", + L"FileBrowseExecutable", processItem->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." @@ -1467,7 +1467,7 @@ VOID PhMwpOnCommand( { PhShellExecuteUserString( PhMainWndHandle, - L"FileOpenExecutable", + L"FileBrowseExecutable", fileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 37becc411a4a..a6695a217eb1 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1703,7 +1703,7 @@ BOOLEAN PhMipListSectionTreeNewCallback( { PhShellExecuteUserString( listSection->DialogHandle, - L"FileOpenExecutable", + L"FileBrowseExecutable", node->ProcessGroup->Representative->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 7dad38071e01..8332a7180d2d 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -238,7 +238,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( { PhShellExecuteUserString( PhMainWndHandle, - L"FileOpenExecutable", + L"FileBrowseExecutable", context->Record->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index f780a952ead1..925398bfa7eb 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -500,7 +500,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { PhShellExecuteUserString( hwndDlg, - L"FileOpenExecutable", + L"FileBrowseExecutable", processItem->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 5efd8288fd26..c803d20f97b7 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -635,7 +635,7 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( { PhShellExecuteUserString( hwndDlg, - L"FileOpenExecutable", + L"FileBrowseExecutable", moduleItem->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 30670a03b358..68565ddee871 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -998,7 +998,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { PhShellExecuteUserString( hwndDlg, - L"FileOpenExecutable", + L"FileBrowseExecutable", threadItem->StartAddressFileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 807de6727f13..bda87b5abf52 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -52,7 +52,7 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"FindObjListViewColumns", L""); PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"350,350"); PhpAddScalableIntegerPairSetting(L"FindObjWindowSize", L"@96|550,420"); - PhpAddStringSetting(L"FileOpenExecutable", L"explorer.exe \"/select,%s\""); + PhpAddStringSetting(L"FileBrowseExecutable", L"%SystemRoot%\\explorer.exe /select,\"%s\""); PhpAddIntegerSetting(L"FirstRun", L"1"); PhpAddStringSetting(L"Font", L""); // null PhpAddIntegerSetting(L"ForceNoParent", L"0"); From ad70e7d77f1945e39de95eb6a7619902e2de71f9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 7 Jul 2017 13:42:26 +1000 Subject: [PATCH 259/839] Fix setup error during update, Fix KPH device name typo --- phlib/include/kphapi.h | 2 +- phlib/kph.c | 2 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index 36d03742e9e9..533fad2d78e8 100644 --- a/phlib/include/kphapi.h +++ b/phlib/include/kphapi.h @@ -114,7 +114,7 @@ typedef struct _ETWREG_BASIC_INFORMATION // Device -#define KPH_DEVICE_SHORT_NAME L"KProcessHacker2" +#define KPH_DEVICE_SHORT_NAME L"KProcessHacker3" #define KPH_DEVICE_TYPE 0x9999 #define KPH_DEVICE_NAME (L"\\Device\\" KPH_DEVICE_SHORT_NAME) diff --git a/phlib/kph.c b/phlib/kph.c index 07008b4c39b6..71bf12115977 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -256,7 +256,7 @@ NTSTATUS KphSetParameters( _In_ PKPH_PARAMETERS Parameters ) { - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker2\\Parameters"); + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker3\\Parameters"); NTSTATUS status; HANDLE parametersKeyHandle = NULL; ULONG disposition; diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 59e5ab6918c3..31009632e79a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -346,6 +346,23 @@ BOOLEAN SetupUninstallKph( _In_ PPH_SETUP_CONTEXT Context ) { + while (TRUE) + { + SC_HANDLE serviceHandle; + SERVICE_STATUS serviceStatus; + + if (!(serviceHandle = PhOpenService( + L"KProcessHacker2", + SERVICE_STOP | DELETE + ))) + break; + + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + DeleteService(serviceHandle); + + CloseServiceHandle(serviceHandle); + } + while (TRUE) { SC_HANDLE serviceHandle; From 5323554632562b7c30d4b25c8ed0a26aa59409cc Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 7 Jul 2017 14:20:41 +1000 Subject: [PATCH 260/839] Prevent KPH loading on unsupported builds of Win10 --- ProcessHacker/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 2ed7ebff5408..815ef58ee872 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -560,6 +560,9 @@ VOID PhInitializeKph( PPH_STRING kprocesshackerFileName; KPH_PARAMETERS parameters; + if (WindowsVersion == WINDOWS_NEW) + return; + kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); parameters.SecurityLevel = KphSecurityPrivilegeCheck; From a1ccdcebd568664b10b76701a3460052e0e24ed0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 8 Jul 2017 03:31:12 +1000 Subject: [PATCH 261/839] Fix runas crash (thanks to diversenok for reporting the bug!) --- ProcessHacker/runas.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index ed8eec32b283..c7131fbb28ff 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -425,15 +425,15 @@ INT_PTR CALLBACK PhpRunAsDlgProc( // we need to be in the target session. PH_CREATE_PROCESS_AS_USER_INFO createInfo; - PPH_STRING domainPart; - PPH_STRING userPart; + 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 = program->Buffer; - createInfo.UserName = userPart->Buffer; - createInfo.DomainName = domainPart->Buffer; + 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. From 9dc974beceff0c626324d7c706eb7534b1892e58 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 8 Jul 2017 03:32:01 +1000 Subject: [PATCH 262/839] Fix 64bit propertysheet style bug --- phlib/guisup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index 4ecd51fb4500..b9667e8d2509 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -906,7 +906,7 @@ BOOLEAN PhModalPropertySheet( oldFocus = GetFocus(); topLevelOwner = Header->hwndParent; - while (topLevelOwner && (GetWindowLong(topLevelOwner, GWL_STYLE) & WS_CHILD)) + while (topLevelOwner && (GetWindowLongPtr(topLevelOwner, GWL_STYLE) & WS_CHILD)) topLevelOwner = GetParent(topLevelOwner); if (topLevelOwner && (topLevelOwner == GetDesktopWindow() || EnableWindow(topLevelOwner, FALSE))) From 9df03a9ef02fa4d01b046142534d3f5ebaea7355 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 8 Jul 2017 15:06:31 +1000 Subject: [PATCH 263/839] Revert commit f922924765f2069a4dc924773bde8c1827b7a0c1 --- .../bin-signed/amd64/kprocesshacker.sys | Bin 40088 -> 45208 bytes .../bin-signed/i386/kprocesshacker.sys | Bin 36376 -> 41624 bytes ProcessHacker/main.c | 15 +- phlib/include/kphapi.h | 53 ++- phlib/include/kphuser.h | 16 + phlib/kph.c | 411 +++++++++++++----- phlib/native.c | 61 ++- phlib/phlib.vcxproj | 3 +- phlib/phlib.vcxproj.filters | 3 + 9 files changed, 431 insertions(+), 131 deletions(-) diff --git a/KProcessHacker/bin-signed/amd64/kprocesshacker.sys b/KProcessHacker/bin-signed/amd64/kprocesshacker.sys index 4da0af31c218fe951f263f863345fdc66545eced..7d4695902a56f21840791f0efcdf74d35bad3c34 100644 GIT binary patch delta 22087 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 delta 19905 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 Uc5kunx$lO(f8lPu>jT;U0^YzbjsO4v diff --git a/KProcessHacker/bin-signed/i386/kprocesshacker.sys b/KProcessHacker/bin-signed/i386/kprocesshacker.sys index 6771d7266b67b245778ed488935031d585c92f48..4f5372a8bd5b0e3530c2871c4dc771a9e4d6550d 100644 GIT binary patch delta 21033 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 literal 36376 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 Z5f6WBy*w?#JL%zwcX$1j^YQD<{{d(wt0w>e diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 815ef58ee872..0923076ecf9a 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -557,19 +557,32 @@ VOID PhInitializeKph( ) { 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_NEW) 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( @@ -971,7 +984,7 @@ VOID PhpProcessStartupParameters( kprocesshackerFileName = PhConcatStrings2(PhApplicationDirectory->Buffer, L"\\kprocesshacker.sys"); - parameters.SecurityLevel = KphSecurityPrivilegeCheck; + parameters.SecurityLevel = KphSecuritySignatureCheck; parameters.CreateDynamicConfiguration = TRUE; status = KphInstallEx(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index 533fad2d78e8..f905de9a09d3 100644 --- a/phlib/include/kphapi.h +++ b/phlib/include/kphapi.h @@ -124,6 +124,8 @@ 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; @@ -133,7 +135,7 @@ typedef struct _KPH_DYN_STRUCT_DATA SHORT EpObjectTable; SHORT Reserved0; SHORT Reserved1; - SHORT EpRundownProtect; + SHORT Reserved2; SHORT EreGuidEntry; SHORT HtHandleContentionEvent; SHORT OtName; @@ -152,7 +154,7 @@ typedef struct _KPH_DYN_PACKAGE KPH_DYN_STRUCT_DATA StructData; } KPH_DYN_PACKAGE, *PKPH_DYN_PACKAGE; -#define KPH_DYN_CONFIGURATION_VERSION 2 +#define KPH_DYN_CONFIGURATION_VERSION 3 #define KPH_DYN_MAXIMUM_PACKAGES 64 typedef struct _KPH_DYN_CONFIGURATION @@ -162,6 +164,33 @@ typedef struct _KPH_DYN_CONFIGURATION 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. @@ -172,24 +201,26 @@ typedef struct _KPH_DYN_CONFIGURATION // 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) -#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(51) +#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_SUSPENDPROCESS KPH_CTL_CODE(53) -#define KPH_RESUMEPROCESS KPH_CTL_CODE(54) -#define KPH_TERMINATEPROCESS KPH_CTL_CODE(55) +#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) +#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) +#define KPH_OPENTHREAD KPH_CTL_CODE(100) // L1/L2 protected API #define KPH_OPENTHREADPROCESS KPH_CTL_CODE(101) -#define KPH_TERMINATETHREAD KPH_CTL_CODE(102) +#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) @@ -201,7 +232,7 @@ typedef struct _KPH_DYN_CONFIGURATION #define KPH_ENUMERATEPROCESSHANDLES KPH_CTL_CODE(150) #define KPH_QUERYINFORMATIONOBJECT KPH_CTL_CODE(151) #define KPH_SETINFORMATIONOBJECT KPH_CTL_CODE(152) -#define KPH_DUPLICATEOBJECT KPH_CTL_CODE(153) +#define KPH_RESERVED153 KPH_CTL_CODE(153) // Misc. #define KPH_OPENDRIVER KPH_CTL_CODE(200) diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index 72eb102ed481..102534b55a62 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -51,6 +51,13 @@ KphIsConnected( VOID ); +PHLIBAPI +BOOLEAN +NTAPI +KphIsVerified( + VOID + ); + PHLIBAPI NTSTATUS NTAPI @@ -90,6 +97,14 @@ KphGetFeatures( _Out_ PULONG Features ); +PHLIBAPI +NTSTATUS +NTAPI +KphVerifyClient( + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize + ); + PHLIBAPI NTSTATUS NTAPI @@ -254,6 +269,7 @@ NTSTATUS NTAPI KphOpenDriver( _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes ); diff --git a/phlib/kph.c b/phlib/kph.c index 71bf12115977..0acd7d0baa27 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -22,8 +22,11 @@ #include #include +#include HANDLE PhKphHandle = NULL; +BOOLEAN PhKphVerified; +KPH_KEY PhKphL1Key; NTSTATUS KphConnect( _In_opt_ PWSTR DeviceName @@ -76,6 +79,8 @@ NTSTATUS KphConnect( ); PhKphHandle = kphHandle; + PhKphVerified = FALSE; + PhKphL1Key = 0; } return status; @@ -240,6 +245,8 @@ NTSTATUS KphDisconnect( status = NtClose(PhKphHandle); PhKphHandle = NULL; + PhKphVerified = FALSE; + PhKphL1Key = 0; return status; } @@ -251,42 +258,52 @@ BOOLEAN KphIsConnected( return PhKphHandle != NULL; } +BOOLEAN KphIsVerified( + VOID + ) +{ + return PhKphVerified; +} + NTSTATUS KphSetParameters( _In_opt_ PWSTR DeviceName, _In_ PKPH_PARAMETERS Parameters ) { - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker3\\Parameters"); NTSTATUS status; HANDLE parametersKeyHandle = NULL; + PPH_STRING parametersKeyName; ULONG disposition; UNICODE_STRING valueName; - if (!NT_SUCCESS(status = PhCreateKey( + 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, - &keyName, + ¶metersKeyName->sr, 0, 0, &disposition - ))) - { + ); + PhDereferenceObject(parametersKeyName); + + if (!NT_SUCCESS(status)) return status; - } RtlInitUnicodeString(&valueName, L"SecurityLevel"); - if (!NT_SUCCESS(status = NtSetValueKey( - parametersKeyHandle, - &valueName, - 0, - REG_DWORD, - &Parameters->SecurityLevel, - sizeof(ULONG) - ))) - { + status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_DWORD, &Parameters->SecurityLevel, sizeof(ULONG)); + + if (!NT_SUCCESS(status)) goto SetValuesEnd; - } if (Parameters->CreateDynamicConfiguration) { @@ -299,14 +316,10 @@ NTSTATUS KphSetParameters( if (NT_SUCCESS(KphInitializeDynamicPackage(&configuration.Packages[0]))) { - status = NtSetValueKey( - parametersKeyHandle, - &valueName, - 0, - REG_BINARY, - &configuration, - sizeof(KPH_DYN_CONFIGURATION) - ); + status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_BINARY, &configuration, sizeof(KPH_DYN_CONFIGURATION)); + + if (!NT_SUCCESS(status)) + goto SetValuesEnd; } } @@ -436,28 +449,6 @@ NTSTATUS KphUninstall( return status; } -NTSTATUS KphpDeviceIoControl( - _In_ ULONG KphControlCode, - _In_ PVOID InBuffer, - _In_ ULONG InBufferLength - ) -{ - IO_STATUS_BLOCK isb; - - return NtDeviceIoControlFile( - PhKphHandle, - NULL, - NULL, - NULL, - &isb, - KphControlCode, - InBuffer, - InBufferLength, - NULL, - 0 - ); -} - NTSTATUS KphGetFeatures( _Out_ PULONG Features ) @@ -474,24 +465,52 @@ NTSTATUS KphGetFeatures( ); } -NTSTATUS KphOpenProcess( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCLIENT_ID ClientId +NTSTATUS KphVerifyClient( + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize ) { + NTSTATUS status; struct { - PHANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PCLIENT_ID ClientId; - } input = { ProcessHandle, DesiredAccess, ClientId }; + PVOID CodeAddress; + PUCHAR Signature; + ULONG SignatureSize; + } input = { KphpWithKeyApcRoutine, Signature, SignatureSize }; - return KphpDeviceIoControl( - KPH_OPENPROCESS, + 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( @@ -500,18 +519,21 @@ NTSTATUS KphOpenProcessToken( _Out_ PHANDLE TokenHandle ) { - struct - { - HANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PHANDLE TokenHandle; - } input = { ProcessHandle, DesiredAccess, TokenHandle }; + KPH_OPEN_PROCESS_TOKEN_INPUT input = { ProcessHandle, DesiredAccess, TokenHandle, 0 }; - return KphpDeviceIoControl( - KPH_OPENPROCESSTOKEN, - &input, - sizeof(input) - ); + 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( @@ -540,20 +562,11 @@ NTSTATUS KphTerminateProcess( ) { NTSTATUS status; - struct - { - HANDLE ProcessHandle; - NTSTATUS ExitStatus; - } input = { ProcessHandle, ExitStatus }; + KPH_TERMINATE_PROCESS_INPUT input = { ProcessHandle, ExitStatus, 0 }; - status = KphpDeviceIoControl( - KPH_TERMINATEPROCESS, - &input, - sizeof(input) - ); + status = KphpWithKey(KphKeyLevel2, KphpTerminateProcessContinuation, &input); - // Check if we're trying to terminate the current process, - // because kernel-mode can't do it. + // 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); @@ -570,20 +583,9 @@ NTSTATUS KphReadVirtualMemoryUnsafe( _Out_opt_ PSIZE_T NumberOfBytesRead ) { - struct - { - HANDLE ProcessHandle; - PVOID BaseAddress; - PVOID Buffer; - SIZE_T BufferSize; - PSIZE_T NumberOfBytesRead; - } input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead }; + KPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead, 0 }; - return KphpDeviceIoControl( - KPH_READVIRTUALMEMORYUNSAFE, - &input, - sizeof(input) - ); + return KphpWithKey(KphKeyLevel2, KphpReadVirtualMemoryUnsafeContinuation, &input); } NTSTATUS KphQueryInformationProcess( @@ -638,18 +640,21 @@ NTSTATUS KphOpenThread( _In_ PCLIENT_ID ClientId ) { - struct - { - PHANDLE ThreadHandle; - ACCESS_MASK DesiredAccess; - PCLIENT_ID ClientId; - } input = { ThreadHandle, DesiredAccess, ClientId }; + KPH_OPEN_THREAD_INPUT input = { ThreadHandle, DesiredAccess, ClientId, 0 }; - return KphpDeviceIoControl( - KPH_OPENTHREAD, - &input, - sizeof(input) - ); + 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( @@ -860,14 +865,16 @@ NTSTATUS KphSetInformationObject( NTSTATUS KphOpenDriver( _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes ) { struct { PHANDLE DriverHandle; + ACCESS_MASK DesiredAccess; POBJECT_ATTRIBUTES ObjectAttributes; - } input = { DriverHandle, ObjectAttributes }; + } input = { DriverHandle, DesiredAccess, ObjectAttributes }; return KphpDeviceIoControl( KPH_OPENDRIVER, @@ -898,4 +905,198 @@ NTSTATUS KphQueryInformationDriver( &input, sizeof(input) ); -} \ No newline at end of file +} + +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/native.c b/phlib/native.c index 9366ab375b56..0a6acd15f6b1 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -113,15 +113,16 @@ NTSTATUS PhOpenProcess( _In_ HANDLE ProcessId ) { + NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; CLIENT_ID clientId; clientId.UniqueProcess = ProcessId; clientId.UniqueThread = NULL; - if (KphIsConnected()) + if (KphIsVerified() && (DesiredAccess & KPH_PROCESS_READ_ACCESS) == DesiredAccess) { - return KphOpenProcess( + status = KphOpenProcess( ProcessHandle, DesiredAccess, &clientId @@ -130,14 +131,24 @@ NTSTATUS PhOpenProcess( else { InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - - return NtOpenProcess( + 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. */ @@ -175,15 +186,16 @@ NTSTATUS PhOpenThread( _In_ HANDLE ThreadId ) { + NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; CLIENT_ID clientId; clientId.UniqueProcess = NULL; clientId.UniqueThread = ThreadId; - if (KphIsConnected()) + if (KphIsVerified() && (DesiredAccess & KPH_THREAD_READ_ACCESS) == DesiredAccess) { - return KphOpenThread( + status = KphOpenThread( ThreadHandle, DesiredAccess, &clientId @@ -192,14 +204,24 @@ NTSTATUS PhOpenThread( else { InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - - return NtOpenThread( + 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. */ @@ -271,9 +293,11 @@ NTSTATUS PhOpenProcessToken( _Out_ PHANDLE TokenHandle ) { - if (KphIsConnected()) + NTSTATUS status; + + if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) { - return KphOpenProcessToken( + status = KphOpenProcessToken( ProcessHandle, DesiredAccess, TokenHandle @@ -281,12 +305,23 @@ NTSTATUS PhOpenProcessToken( } else { - return NtOpenProcessToken( + status = NtOpenProcessToken( ProcessHandle, DesiredAccess, TokenHandle ); + + if (status == STATUS_ACCESS_DENIED && KphIsVerified()) + { + status = KphOpenProcessToken( + ProcessHandle, + DesiredAccess, + TokenHandle + ); + } } + + return status; } NTSTATUS PhGetObjectSecurity( @@ -365,7 +400,7 @@ NTSTATUS PhTerminateProcess( { NTSTATUS status; - if (KphIsConnected()) + if (KphIsVerified()) { status = KphTerminateProcess( ProcessHandle, @@ -2484,7 +2519,7 @@ BOOLEAN NTAPI PhpOpenDriverByBaseAddressCallback( NULL ); - status = KphOpenDriver(&driverHandle, &objectAttributes); + status = KphOpenDriver(&driverHandle, SYNCHRONIZE, &objectAttributes); PhDereferenceObject(driverName); if (!NT_SUCCESS(status)) diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index 042c17ad6cc6..e395b2239ea8 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,4 +1,4 @@ - + @@ -202,6 +202,7 @@ + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index cfd2a295a596..dee92e1b386b 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -346,6 +346,9 @@ Header Files + + Header Files + Header Files From 292847078a9842666632ff3b6ea19b39317a7090 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 9 Jul 2017 03:45:43 +1000 Subject: [PATCH 264/839] Update PCRE to v10.23 --- ProcessHacker/pcre/config.h | 168 +- ProcessHacker/pcre/pcre2.h | 482 +- ProcessHacker/pcre/pcre2_auto_possess.c | 18 +- ProcessHacker/pcre/pcre2_compile.c | 12884 +++++++++++----------- ProcessHacker/pcre/pcre2_dfa_match.c | 21 +- ProcessHacker/pcre/pcre2_error.c | 28 +- ProcessHacker/pcre/pcre2_find_bracket.c | 4 +- ProcessHacker/pcre/pcre2_internal.h | 34 +- ProcessHacker/pcre/pcre2_intmodedep.h | 38 +- ProcessHacker/pcre/pcre2_jit_compile.c | 1 + ProcessHacker/pcre/pcre2_maketables.c | 1 + ProcessHacker/pcre/pcre2_match.c | 80 +- ProcessHacker/pcre/pcre2_match_data.c | 1 + ProcessHacker/pcre/pcre2_newline.c | 1 + ProcessHacker/pcre/pcre2_ord2utf.c | 6 +- ProcessHacker/pcre/pcre2_pattern_info.c | 2 + ProcessHacker/pcre/pcre2_printint.c | 4 +- ProcessHacker/pcre/pcre2_serialize.c | 1 + ProcessHacker/pcre/pcre2_string_utils.c | 1 + ProcessHacker/pcre/pcre2_study.c | 157 +- ProcessHacker/pcre/pcre2_substitute.c | 15 +- ProcessHacker/pcre/pcre2_tables.c | 2 +- ProcessHacker/pcre/pcre2_ucp.h | 6 +- ProcessHacker/pcre/pcre2_valid_utf.c | 8 +- ProcessHacker/pcre/pcre2_xclass.c | 1 + ProcessHacker/pcre/pcre2posix.c | 14 +- ProcessHacker/pcre/pcre2posix.h | 2 +- 27 files changed, 7288 insertions(+), 6692 deletions(-) diff --git a/ProcessHacker/pcre/config.h b/ProcessHacker/pcre/config.h index 566feb63c5ed..b71d730d3561 100644 --- a/ProcessHacker/pcre/config.h +++ b/ProcessHacker/pcre/config.h @@ -1,6 +1,6 @@ -/* src/config.h. Generated from config.h.in by configure. */ /* src/config.h.in. Generated from configure.ac by autoheader. */ + /* PCRE2 is written in Standard C, but there are a few non-standard things it can cope with, allowing it to run on SunOS4 and other "close to standard" systems. @@ -33,7 +33,7 @@ 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 */ +#undef BSR_ANYCRLF /* 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 @@ -41,30 +41,30 @@ sure both macros are undefined; an emulation function will then be used. */ macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It is not possible to build a version of PCRE2 that supports both EBCDIC and UTF-8/16/32. */ -/* #undef EBCDIC */ +#undef EBCDIC /* In an EBCDIC environment, define this macro to any value to arrange for the NL character to be 0x25 instead of the default 0x15. NL plays the role that LF does in an ASCII/Unicode environment. */ -/* #undef EBCDIC_NL25 */ +#undef EBCDIC_NL25 /* Define to 1 if you have the `bcopy' function. */ -/* #undef HAVE_BCOPY */ +#undef HAVE_BCOPY /* Define to 1 if you have the header file. */ -/* #undef HAVE_BZLIB_H */ +#undef HAVE_BZLIB_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_DIRENT_H */ +#undef HAVE_DIRENT_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ +#undef HAVE_DLFCN_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_EDITLINE_READLINE_H */ +#undef HAVE_EDITLINE_READLINE_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_EDIT_READLINE_READLINE_H */ +#undef HAVE_EDIT_READLINE_READLINE_H /* Define to 1 if you have the header file. */ #ifndef HAVE_INTTYPES_H @@ -86,17 +86,23 @@ 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 */ +#undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ -/* #undef HAVE_PTHREAD_PRIO_INHERIT */ +#undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the header file. */ -/* #undef HAVE_READLINE_HISTORY_H */ +#undef HAVE_READLINE_HISTORY_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_READLINE_READLINE_H */ +#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 @@ -113,9 +119,8 @@ 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 */ +#undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #ifndef HAVE_STRING_H @@ -133,13 +138,13 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_WAIT_H */ +#undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ +#undef HAVE_UNISTD_H /* Define to 1 if the compiler supports simple visibility declarations. */ -/* #undef HAVE_VISIBILITY */ +#undef HAVE_VISIBILITY /* Define to 1 if you have the header file. */ #ifndef HAVE_WINDOWS_H @@ -147,7 +152,7 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to 1 if you have the header file. */ -/* #undef HAVE_ZLIB_H */ +#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 @@ -155,7 +160,7 @@ sure both macros are undefined; an emulation function will then be used. */ 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 */ +#undef HEAP_MATCH_RECURSE /* 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 @@ -167,10 +172,7 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to the sub-directory where libtool stores uninstalled libraries. */ -/* This is ignored unless you are using libtool. */ -#ifndef LT_OBJDIR -#define LT_OBJDIR ".libs/" -#endif +#undef LT_OBJDIR /* The value of MATCH_LIMIT determines the default number of times the internal match() function can be called during a single execution of @@ -209,36 +211,36 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */ -/* #undef NEVER_BACKSLASH_C */ +#undef NEVER_BACKSLASH_C /* 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). */ #ifndef NEWLINE_DEFAULT -#define NEWLINE_DEFAULT 2 +#define NEWLINE_DEFAULT 4 #endif /* Name of package */ -#define PACKAGE "pcre2" +#undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" +#undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ -#define PACKAGE_NAME "PCRE2" +#undef PACKAGE_NAME /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE2 10.22" +#undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "pcre2" +#undef PACKAGE_TARNAME /* Define to the home page for this package. */ -#define PACKAGE_URL "" +#undef PACKAGE_URL /* Define to the version of this package. */ -#define PACKAGE_VERSION "10.22" +#undef PACKAGE_VERSION /* 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,17 +249,36 @@ 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 + +/* to make a symbol visible */ +#undef PCRE2POSIX_EXP_DECL + +/* to make a symbol visible */ +#undef PCRE2POSIX_EXP_DEFN + /* Define to any value to include debugging code. */ -/* #undef PCRE2_DEBUG */ +#undef PCRE2_DEBUG + +/* to make a symbol visible */ +#undef PCRE2_EXP_DECL + /* If you are compiling for a system other than a Unix-like system or Win32, and it needs some magic to be inserted before the definition @@ -268,7 +289,7 @@ sure both macros are undefined; an emulation function will then be used. */ This macro apears at the start of every exported function that is part of the external API. It does not appear on functions that are "external" in the C sense, but which are internal to the library. */ -/* #undef PCRE2_EXP_DEFN */ +#undef PCRE2_EXP_DEFN /* Define to any value if linking statically (TODO: make nice with Libtool) */ #ifndef PCRE2_STATIC @@ -277,7 +298,7 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to necessary symbol if this constant uses a non-standard name on your system. */ -/* #undef PTHREAD_CREATE_JOINABLE */ +#undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ #ifndef STDC_HEADERS @@ -285,27 +306,28 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to any value to enable support for Just-In-Time compiling. */ -/* #undef SUPPORT_JIT */ +#undef SUPPORT_JIT /* Define to any value to allow pcre2grep to be linked with libbz2, so that it is able to handle .bz2 files. */ -/* #undef SUPPORT_LIBBZ2 */ +#undef SUPPORT_LIBBZ2 /* Define to any value to allow pcre2test to be linked with libedit. */ -/* #undef SUPPORT_LIBEDIT */ +#undef SUPPORT_LIBEDIT /* Define to any value to allow pcre2test to be linked with libreadline. */ -/* #undef SUPPORT_LIBREADLINE */ +#undef SUPPORT_LIBREADLINE /* Define to any value to allow pcre2grep to be linked with libz, so that it is able to handle .gz files. */ -/* #undef SUPPORT_LIBZ */ +#undef SUPPORT_LIBZ /* Define to any value to enable callout script support in pcre2grep. */ -/* #undef SUPPORT_PCRE2GREP_CALLOUT */ +#undef SUPPORT_PCRE2GREP_CALLOUT -/* Define to any value to enable JIT support in pcre2grep. */ -/* #undef SUPPORT_PCRE2GREP_JIT */ +/* 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. */ #ifndef SUPPORT_PCRE2_16 @@ -313,10 +335,10 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to any value to enable the 32 bit PCRE2 library. */ -/* #undef SUPPORT_PCRE2_32 */ +//#undef SUPPORT_PCRE2_32 /* Define to any value to enable the 8 bit PCRE2 library. */ -/* #undef SUPPORT_PCRE2_8 */ +//#undef SUPPORT_PCRE2_8 /* Define to any value to enable support for Unicode and UTF encoding. This will work even in an EBCDIC environment, but it is incompatible with the @@ -327,17 +349,49 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to any value for valgrind support to find invalid memory reads. */ -/* #undef SUPPORT_VALGRIND */ +#undef SUPPORT_VALGRIND + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + /* Version number of package */ -#define VERSION "10.22" +#undef VERSION + +/* 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 */ +#undef const /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ -/* #undef int64_t */ +#undef int64_t /* Define to `unsigned int' if does not define. */ -/* #undef size_t */ +#undef size_t diff --git a/ProcessHacker/pcre/pcre2.h b/ProcessHacker/pcre/pcre2.h index 757784e97254..550a2b93d81f 100644 --- a/ProcessHacker/pcre/pcre2.h +++ b/ProcessHacker/pcre/pcre2.h @@ -5,22 +5,22 @@ /* 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 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 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. +* 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. +* 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 @@ -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,15 @@ 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_MINOR 23 #define PCRE2_PRERELEASE -#define PCRE2_DATE 2016-07-29 +#define PCRE2_DATE 2017-02-14 /* 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,6 +73,20 @@ don't change existing definitions of PCRE2_EXP_DECL. */ # endif #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 + /* Have to include limits.h, stdlib.h and stdint.h to ensure that size_t and uint8_t, UCHAR_MAX, etc are defined. */ @@ -87,23 +100,23 @@ 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 -/* 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 */ @@ -130,16 +143,16 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ #define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ -/* These are for pcre2_jit_compile(). */ + /* These are for pcre2_jit_compile(). */ #define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */ #define PCRE2_JIT_PARTIAL_SOFT 0x00000002u #define PCRE2_JIT_PARTIAL_HARD 0x00000004u -/* These are for pcre2_match(), pcre2_dfa_match(), and pcre2_jit_match(). Note -that PCRE2_ANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these -functions (though pcre2_jit_match() ignores the latter since it bypasses all -sanity checks). */ + /* These are for pcre2_match(), pcre2_dfa_match(), and pcre2_jit_match(). Note + that PCRE2_ANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these + functions (though pcre2_jit_match() ignores the latter since it bypasses all + sanity checks). */ #define PCRE2_NOTBOL 0x00000001u #define PCRE2_NOTEOL 0x00000002u @@ -148,13 +161,13 @@ sanity checks). */ #define PCRE2_PARTIAL_SOFT 0x00000010u #define PCRE2_PARTIAL_HARD 0x00000020u -/* These are additional options for pcre2_dfa_match(). */ + /* These are additional options for pcre2_dfa_match(). */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u -/* These are additional options for pcre2_substitute(), which passes any others -through to pcre2_match(). */ + /* These are additional options for pcre2_substitute(), which passes any others + through to pcre2_match(). */ #define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u #define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u @@ -162,14 +175,14 @@ through to pcre2_match(). */ #define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u #define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u -/* A further option for pcre2_match(), not allowed for pcre2_dfa_match(), -ignored for pcre2_jit_match(). */ + /* A further option for pcre2_match(), not allowed for pcre2_dfa_match(), + ignored for pcre2_jit_match(). */ #define PCRE2_NO_JIT 0x00002000u -/* 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. */ + /* 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. */ #define PCRE2_NEWLINE_CR 1 #define PCRE2_NEWLINE_LF 2 @@ -180,12 +193,12 @@ greater than zero. */ #define PCRE2_BSR_UNICODE 1 #define PCRE2_BSR_ANYCRLF 2 -/* Error codes: no match and partial match are "expected" errors. */ + /* Error codes: no match and partial match are "expected" errors. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) -/* Error codes for UTF-8 validity checks */ + /* Error codes for UTF-8 validity checks */ #define PCRE2_ERROR_UTF8_ERR1 (-3) #define PCRE2_ERROR_UTF8_ERR2 (-4) @@ -209,21 +222,21 @@ greater than zero. */ #define PCRE2_ERROR_UTF8_ERR20 (-22) #define PCRE2_ERROR_UTF8_ERR21 (-23) -/* Error codes for UTF-16 validity checks */ + /* Error codes for UTF-16 validity checks */ #define PCRE2_ERROR_UTF16_ERR1 (-24) #define PCRE2_ERROR_UTF16_ERR2 (-25) #define PCRE2_ERROR_UTF16_ERR3 (-26) -/* Error codes for UTF-32 validity checks */ + /* Error codes for UTF-32 validity checks */ #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. */ + /* 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 */ @@ -260,7 +273,7 @@ numbers must not be changed. */ #define PCRE2_ERROR_TOOMANYREPLACE (-61) #define PCRE2_ERROR_BADSERIALIZEDDATA (-62) -/* Request types for pcre2_pattern_info() */ + /* Request types for pcre2_pattern_info() */ #define PCRE2_INFO_ALLOPTIONS 0 #define PCRE2_INFO_ARGOPTIONS 1 @@ -287,7 +300,7 @@ numbers must not be changed. */ #define PCRE2_INFO_SIZE 22 #define PCRE2_INFO_HASBACKSLASHC 23 -/* Request types for pcre2_config(). */ + /* Request types for pcre2_config(). */ #define PCRE2_CONFIG_BSR 0 #define PCRE2_CONFIG_JIT 1 @@ -302,28 +315,28 @@ numbers must not be changed. */ #define PCRE2_CONFIG_UNICODE_VERSION 10 #define PCRE2_CONFIG_VERSION 11 -/* Types for code units in patterns and subject strings. */ + /* 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; \ @@ -347,11 +360,11 @@ 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. */ #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ @@ -388,190 +401,212 @@ typedef struct pcre2_callout_enumerate_block { \ } pcre2_callout_enumerate_block; -/* List the generic forms of all other functions in macros, which will be -expanded for each width below. Start with functions that give general -information. */ + /* List the generic forms of all other functions in macros, which will be + 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. */ + /* 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_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 *); - - -/* Functions concerned with compiling a pattern to PCRE internal code. */ +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_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 *); + + + /* 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. */ + /* 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. */ + /* 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 *); - - -/* Convenience functions for handling matched substrings. */ +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 **); - -/* Functions for serializing / deserializing compiled patterns. */ +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. */ + /* 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 JIT processing */ + /* 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 *); - - -/* Other miscellaneous functions. */ +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 -three-level macro scheme is necessary to get the macros expanded when we want -them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for -generating three versions of everything below. After that, PCRE2_SUFFIX will be -re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as -pcre2_compile are called by application code. */ + /* Define macros that generate width-specific names from generic versions. The + three-level macro scheme is necessary to get the macros expanded when we want + them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for + generating three versions of everything below. After that, PCRE2_SUFFIX will be + re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as + pcre2_compile are called by application code. */ #define PCRE2_JOIN(a,b) a ## b #define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b) #define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH) -/* Data types */ + /* Data types */ #define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR) #define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR) @@ -588,7 +623,7 @@ pcre2_compile are called by application code. */ #define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) -/* Data blocks */ + /* Data blocks */ #define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_) #define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) @@ -598,10 +633,11 @@ pcre2_compile are called by application code. */ #define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) -/* Functions: the complete list in alphabetical order */ + /* Functions: the complete list in alphabetical order */ #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_) @@ -661,8 +697,8 @@ pcre2_compile are called by application code. */ #define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) -/* Now generate all three sets of width-specific structures and function -prototypes. */ + /* Now generate all three sets of width-specific structures and function + prototypes. */ #define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \ PCRE2_TYPES_LIST \ @@ -681,18 +717,18 @@ 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 @@ -710,9 +746,9 @@ PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_OTHER_FUNCTIONS #undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS -/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine -PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make -PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ + /* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine + PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make + PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ #undef PCRE2_SUFFIX #ifndef PCRE2_CODE_UNIT_WIDTH @@ -736,4 +772,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..2c987bbf5b76 100644 --- a/ProcessHacker/pcre/pcre2_auto_possess.c +++ b/ProcessHacker/pcre/pcre2_auto_possess.c @@ -41,7 +41,9 @@ 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 @@ -589,6 +591,7 @@ for(;;) 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. */ @@ -606,6 +609,9 @@ for(;;) 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)) @@ -1046,8 +1052,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,11 +1069,11 @@ 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 (;;) { @@ -1080,7 +1088,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 +1144,6 @@ for (;;) list[1] = (c & 1) == 0; - rec_limit = 1000; if (compare_opcodes(end, utf, cb, list, end, &rec_limit)) { switch (c) diff --git a/ProcessHacker/pcre/pcre2_compile.c b/ProcessHacker/pcre/pcre2_compile.c index 0677e1fc6f19..0e8199ff4123 100644 --- a/ProcessHacker/pcre/pcre2_compile.c +++ b/ProcessHacker/pcre/pcre2_compile.c @@ -38,11 +38,11 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -// dmex: Disable warnings +// dmex: Disable warnings. #pragma warning(push) -#pragma warning(disable : 4244 4267) - +#pragma warning(disable : 4244 4267) #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -62,9 +62,14 @@ POSSIBILITY OF SUCH DAMAGE. #define PRINTABLE(c) ((c) >= 32 && (c) < 127) #endif #include "pcre2_printint.c" -#define CALL_PRINTINT +#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. */ @@ -83,16 +88,56 @@ by defining macros in order to minimize #if usage. */ #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(uint8_t *, PCRE2_UCHAR **, uint32_t, compile_block *, - const uint32_t *, 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 - 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 *); + set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *, + compile_block *); @@ -100,9 +145,15 @@ static BOOL * 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. +#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 @@ -111,16 +162,18 @@ 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. */ +In the real compile phase, this workspace is not currently used. */ #define COMPILE_WORK_SIZE (2048*LINK_SIZE) /* Size in code units */ -#define C32_WORK_SIZE \ - ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint32_t)) +#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. */ @@ -134,25 +187,176 @@ 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. */ +/* 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 COPIED_PATTERN_SIZE 1024 +#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 bytes, so that we don't -have to check them every time. */ +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_COMMIT */ + 0, /* META_FAIL */ + 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: @@ -174,17 +378,10 @@ compiler is clever with identical subexpressions. */ /* 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_SET_FIXED_LENGTH 0x80000000u +#define GI_NOT_FIXED_LENGTH 0x40000000u #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). */ @@ -195,8 +392,8 @@ comparison). */ 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 +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 @@ -384,9 +581,9 @@ 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 */ + unsigned int len; /* Length of verb name */ + uint32_t meta; /* Base META_ code */ + int has_arg; /* Argument requirement */ } verbitem; static const char verbnames[] = @@ -401,32 +598,30 @@ static const char verbnames[] = 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 } + { 0, META_MARK, +1 }, /* > 0 => must have an argument */ + { 4, META_MARK, +1 }, + { 6, META_ACCEPT, -1 }, /* < 0 => must not have an argument */ + { 6, META_COMMIT, -1 }, + { 1, META_FAIL, -1 }, + { 4, META_FAIL, -1 }, + { 5, META_PRUNE, 0 }, /* Argument is optional; 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. */ -/* Substitutes for [[:<:]] and [[:>:]], which mean start and end of word in -another regex library. */ +static const uint32_t verbops[] = { + OP_MARK, OP_ACCEPT, OP_COMMIT, OP_FAIL, OP_PRUNE, OP_PRUNE_ARG, OP_SKIP, + OP_SKIP_ARG, OP_THEN, OP_THEN_ARG }; -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' }; +/* 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 @@ -448,7 +643,6 @@ static const uint8_t posix_name_lengths[] = { #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 @@ -476,117 +670,28 @@ static const int posix_class_maps[] = { 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 */ +/* 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) / sizeof(PCRE2_UCHAR *)) +#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t))) #endif /* SUPPORT_UNICODE */ /* Masks for checking option settings. */ @@ -615,21 +720,7 @@ enum { ERR0 = COMPILE_ERROR_BASE, 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 */ - }; + ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90 }; /* 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 @@ -732,6 +823,265 @@ static const uint8_t opcode_possessify[] = { }; +#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_COMMIT: fprintf(stderr, "META (*COMMIT)"); break; + case META_FAIL: fprintf(stderr, "META (*FAIL)"); 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_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 * @@ -766,6 +1116,45 @@ 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 * *************************************************/ @@ -802,1043 +1191,212 @@ if (code != NULL) /************************************************* -* Insert an automatic callout point * +* Read a number, possibly signed * *************************************************/ -/* This function is called when the PCRE2_AUTO_CALLOUT option is set, to insert -callout points before each pattern item. +/* 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: - code current code pointer - ptr current pattern pointer - cb general compile-time data - -Returns: new code pointer + 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 PCRE2_UCHAR * -auto_callout(PCRE2_UCHAR *code, PCRE2_SPTR ptr, compile_block *cb) +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) { -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]; -} +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++; + } + } -/************************************************* -* Complete a callout item * -*************************************************/ +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; + } + } -/* 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. +if (allow_sign >= 0 && sign != 0) + { + if (n == 0) + { + *errorcodeptr = ERR26; /* +0 and -0 are not allowed */ + goto EXIT; + } -Arguments: - previous_callout points to previous callout item - ptr current pattern pointer - cb general compile-time data + 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; + } -Returns: nothing -*/ +yield = TRUE; -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); +EXIT: +*intptr = n; +*ptrptr = ptr; +return yield; } /************************************************* -* Find the fixed length of a branch * +* Read repeat counts * *************************************************/ -/* 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. +/* 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: - 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) -*/ + 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 -#define FFL_LATER (-1) -#define FFL_NOTFIXED (-2) -#define FFL_BACKSLASHC (-3) -#define FFL_TOOLONG (-4) -#define FFL_TOOCOMPLICATED (-5) -#define FFL_UNKNOWNOP (-6) +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 int -find_fixedlength(PCRE2_UCHAR *code, BOOL utf, BOOL atend, compile_block *cb, - recurse_check *recurses, int *countptr) +static BOOL +read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp, + uint32_t *maxp, int *errorcodeptr) { -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; - } - } +PCRE2_SPTR p = *ptrptr; +BOOL yield = FALSE; +int32_t min = 0; +int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */ -/* A large and/or complex regex can take too long to process. This can happen -more often when (?| groups are present in the pattern. */ +/* NB read_number() initializes the error code to zero. The only error is for a +number that is too big. */ -if ((*countptr)++ > 2000) return FFL_TOOCOMPLICATED; +if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr)) + goto EXIT; -/* 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. */ +if (p >= ptrend) goto EXIT; -for (;;) +if (*p == CHAR_RIGHT_CURLY_BRACKET) { - int d; - PCRE2_UCHAR *ce, *cs; - register PCRE2_UCHAR op = *cc; - - if (branchlength > LOOKBEHIND_MAX) return FFL_TOOLONG; + p++; + max = min; + } - switch (op) +else + { + if (*p++ != CHAR_COMMA || p >= ptrend) goto EXIT; + if (*p != CHAR_RIGHT_CURLY_BRACKET) { - /* 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 */ + if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, + errorcodeptr) || p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + goto EXIT; + if (max < min) { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ + *errorcodeptr = ERR4; + goto EXIT; } - 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; + } + p++; + } - /* 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. */ +yield = TRUE; +if (minp != NULL) *minp = (uint32_t)min; +if (maxp != NULL) *maxp = (uint32_t)max; - 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; +/* Update the pattern pointer on success, or after an error, but not when +the result is "not a repeat quantifier". */ - /* Skip over things that don't match chars */ +EXIT: +if (yield || *errorcodeptr != 0) *ptrptr = p; +return yield; - 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. */ +/************************************************* +* Handle escapes * +*************************************************/ - 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; +/* 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. - 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; +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. - /* Handle single-char matchers */ +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 - 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 -*/ +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 + 1; -register uint32_t c, cc; +PCRE2_SPTR ptr = *ptrptr; +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) @@ -1848,7 +1406,7 @@ if (ptr >= ptrend) } GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ -ptr--; /* Set pointer back to the last code unit */ +*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 @@ -1862,7 +1420,7 @@ 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) + if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X)) cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ } } @@ -1874,8 +1432,8 @@ when BSUX is set). */ else { PCRE2_SPTR oldptr; - BOOL braced, negated, overflow; - unsigned int s; + BOOL overflow; + int s; /* Filter calls from pcre2_substitute(). */ @@ -1904,12 +1462,13 @@ else if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; else { uint32_t xc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + 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 */ - cc = (cc << 4) | xc; - if ((xc = XDIGIT(ptr[4])) == 0xff) break; /* Not a hex digit */ c = (cc << 4) | xc; ptr += 4; if (utf) @@ -1921,9 +1480,10 @@ else } break; - case CHAR_U: /* \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; @@ -1937,87 +1497,71 @@ else (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. + 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. Just return - the ESC_g code (cf \k). */ + (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[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE) + + if (ptr >= ptrend) + { + *errorcodeptr = ERR57; + break; + } + + if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE) { escape = ESC_g; break; } - /* Handle the Perl-compatible cases */ + /* 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[1] == CHAR_LEFT_CURLY_BRACKET) + if (*ptr == 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) + PCRE2_SPTR p = ptr + 1; + if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) { - escape = ESC_k; + if (*errorcodeptr == 0) escape = ESC_k; /* No number found */ break; } - braced = TRUE; - ptr++; + if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR57; + break; + } + ptr = p + 1; } - else braced = FALSE; - if (ptr[1] == CHAR_MINUS) - { - negated = TRUE; - ptr++; - } - else negated = FALSE; + /* Read an undelimited number */ - /* The integer range is limited by the machine's int representation. */ - s = 0; - overflow = FALSE; - while (IS_DIGIT(ptr[1])) + else { - if (s > INT_MAX / 10 - 1) /* Integer overflow */ + if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) { - overflow = TRUE; + if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */ 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) + if (s <= 0) { - *errorcodeptr = ERR57; - break; - } - - if (s == 0) - { - *errorcodeptr = ERR58; + *errorcodeptr = ERR15; break; } - if (negated) - { - if (s > cb->bracount) - { - *errorcodeptr = ERR15; - break; - } - s = cb->bracount - (s - 1); - } - - escape = -(int)s; + escape = -s; break; /* The handling of escape sequences consisting of a string of digits @@ -2040,31 +1584,18 @@ else 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; + 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 >= CHAR_8 || s <= cb->bracount) + if (s < 10 || oldptr[-1] >= CHAR_8 || s <= (int)cb->bracount) { - escape = -(int)s; /* Indicates a back reference */ + 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 */ @@ -2072,11 +1603,10 @@ else /* 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. */ + 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 = *ptr) >= CHAR_8) break; + if (c >= CHAR_8) break; /* Fall through with a digit less than 8 */ @@ -2088,8 +1618,8 @@ else case CHAR_0: c -= CHAR_0; - while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7) - c = c * 8 + *(++ptr) - 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 @@ -2099,13 +1629,18 @@ else 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 + if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET) + { + ptr--; + *errorcodeptr = ERR55; + } + else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) + *errorcodeptr = ERR78; + else { - ptr += 2; c = 0; overflow = FALSE; - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) { cc = *ptr++; if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ @@ -2123,14 +1658,22 @@ else } if (overflow) { - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; *errorcodeptr = ERR34; } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff) + { + ptr--; + *errorcodeptr = ERR73; + } + } + else { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; + ptr--; + *errorcodeptr = ERR64; } - else *errorcodeptr = ERR64; } break; @@ -2141,8 +1684,9 @@ else 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 */ + 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 */ @@ -2156,10 +1700,9 @@ else else { - if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) + if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) { - ptr += 2; - if (*ptr == CHAR_RIGHT_CURLY_BRACKET) + if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) { *errorcodeptr = ERR78; break; @@ -2167,7 +1710,7 @@ else c = 0; overflow = FALSE; - while ((cc = XDIGIT(*ptr)) != 0xff) + while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff) { ptr++; if (c == 0 && cc == 0) continue; /* Leading zeroes */ @@ -2184,12 +1727,16 @@ else if (overflow) { - while (XDIGIT(*ptr) != 0xff) ptr++; + while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++; *errorcodeptr = ERR34; } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; + if (utf && c >= 0xd800 && c <= 0xdfff) + { + ptr--; + *errorcodeptr = ERR73; + } } /* If the sequence of hex digits does not end with '}', give an error. @@ -2197,18 +1744,22 @@ else \x handling, but nowadays Perl gives an error, which seems much more sensible, so we do too. */ - else *errorcodeptr = ERR67; + else + { + ptr--; + *errorcodeptr = ERR67; + } } /* End of \x{} processing */ - /* Read a single-byte hex-defined char (up to two hex digits after \x) */ + /* Read a up to two hex digits after \x */ else { c = 0; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ ptr++; c = cc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ ptr++; c = (c << 4) | cc; } /* End of \xdd handling */ @@ -2234,14 +1785,13 @@ else #else case CHAR_c: #endif - - c = *(++ptr); - if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); - if (c == CHAR_NULL && ptr >= ptrend) + 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. */ @@ -2271,6 +1821,7 @@ else } #endif /* EBCDIC */ + ptr++; break; /* Any other alphanumeric following \ is an error. Perl gives an error only @@ -2278,7 +1829,8 @@ else default: *errorcodeptr = ERR3; - break; + *ptrptr = ptr - 1; /* Point to the character at fault */ + return 0; } } @@ -2286,16 +1838,16 @@ else 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); +if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET && + ptrend - ptr > 2) + { + PCRE2_SPTR p = ptr + 1; + if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) && + *errorcodeptr == 0) + *errorcodeptr = ERR37; + } -/* Set the pointer to the final character before returning. */ +/* Set the pointer to the next character before returning. */ *ptrptr = ptr; *chptr = c; @@ -2311,8 +1863,8 @@ return escape; /* 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. +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 @@ -2326,30 +1878,33 @@ 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) +get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr, + uint16_t *pdataptr, int *errorcodeptr, compile_block *cb) { -register PCRE2_UCHAR c; -size_t i, bot, top; +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; -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) + 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++) { - c = *(++ptr); + if (ptr >= cb->end_pattern) goto ERROR_RETURN; + c = *ptr++; if (c == CHAR_NULL) goto ERROR_RETURN; if (c == CHAR_RIGHT_CURLY_BRACKET) break; name[i] = c; @@ -2400,217 +1955,6 @@ return FALSE; -/************************************************* -* 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 * *************************************************/ @@ -2649,25 +1993,28 @@ seem right at all. PCRE does not allow closing square brackets in POSIX class names. Arguments: - ptr pointer to the initial [ + 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 *endptr) +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. */ +terminator = *ptr++; /* compiler warns about "non-constant" initializer. */ -for (++ptr; *ptr != CHAR_NULL; ptr++) +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; @@ -2698,7 +2045,7 @@ static int check_posix_name(PCRE2_SPTR ptr, int len) { const char *pn = posix_names; -register int yield = 0; +int yield = 0; while (posix_name_lengths[yield] != 0) { if (len == posix_name_lengths[yield] && @@ -2711,4338 +2058,3715 @@ return -1; -#ifdef SUPPORT_UNICODE /************************************************* -* Get othercase range * +* Read a subpattern or VERB name * *************************************************/ -/* 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. +/* 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: - 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 + 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 int -get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, - uint32_t *odptr) +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) { -uint32_t c, othercase, next; -unsigned int co; +PCRE2_SPTR ptr = *ptrptr; +BOOL is_verb = (*ptr == CHAR_ASTERISK); +uint32_t namelen = 0; +uint32_t ctype = is_verb? ctype_letter : ctype_word; -/* Find the first character that has an other case. If it has multiple other -cases, return its case offset value. */ +if (++ptr >= ptrend) + { + *errorcodeptr = is_verb? ERR60: /* Verb not recognized or malformed */ + ERR62; /* Subpattern name expected */ + goto FAILED; + } -for (c = *cptr; c <= d; c++) +*nameptr = ptr; +*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern); + +if (IS_DIGIT(*ptr)) { - if ((co = UCD_CASESET(c)) != 0) + *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) { - *ocptr = c++; /* Character that has the set */ - *cptr = c; /* Rest of input range */ - return (int)co; + *errorcodeptr = ERR48; + goto FAILED; } - 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; +/* Subpattern names must not be empty, and their terminator is checked here. +(What follows a verb name is checked separately.) */ -for (++c; c <= d; c++) +if (!is_verb) { - if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; - next++; + if (namelen == 0) + { + *errorcodeptr = ERR62; /* Subpattern name expected */ + goto FAILED; + } + if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + ptr++; } -*odptr = next - 1; /* End of othercase range */ -*cptr = c; /* Rest of input range */ -return 0; +*namelenptr = namelen; +*ptrptr = ptr; +return TRUE; + +FAILED: +*ptrptr = ptr; +return FALSE; } -#endif /* SUPPORT_UNICODE */ /************************************************* -* Add a character or range to a class * +* Manage callouts at start of cycle * *************************************************/ -/* 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. +/* 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: - 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 + ptr current pattern pointer + pcalloutptr points to a pointer to previous callout, or NULL + options the compiling options + parsed_pattern the parsed pattern pointer + cb compile block -Returns: the number of < 256 characters added - the pointer to extra data is updated +Returns: possibly updated parsed_pattern pointer. */ -static unsigned int -add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, - compile_block *cb, uint32_t start, uint32_t end) +static uint32_t * +manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, uint32_t options, + uint32_t *parsed_pattern, compile_block *cb) { -uint32_t c; -uint32_t classbits_end = (end <= 0xff ? end : 0xff); -unsigned int n8 = 0; +uint32_t *previous_callout = *pcalloutptr; -/* 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 (previous_callout != NULL) previous_callout[2] = ptr - cb->start_pattern - + (PCRE2_SIZE)previous_callout[1]; -if ((options & PCRE2_CASELESS) != 0) +if ((options & PCRE2_AUTO_CALLOUT) == 0) previous_callout = NULL; else { -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) + if (previous_callout == NULL || + previous_callout != parsed_pattern - 4 || + previous_callout[3] != 255) { - int rc; - uint32_t oc, od; + 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); + } - options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ - c = start; +*pcalloutptr = previous_callout; +return parsed_pattern; +} - 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. */ +/************************************************* +* Parse regex and identify named groups * +*************************************************/ - else if (oc >= start && od <= end) continue; +/* 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. - /* 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. */ +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 - 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 */ +Returns: zero on success or a non-zero error code, with the + error offset placed in the cb field +*/ - /* Not UTF mode */ +/* A structure and some flags for dealing with nested groups. */ - for (c = start; c <= classbits_end; c++) - { - SETBIT(classbits, cb->fcc[c]); - n8++; - } - } +typedef struct nest_save { + uint16_t nest_depth; + uint16_t reset_group; + uint16_t max_group; + uint16_t flags; +} nest_save; -/* 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. */ +#define NSF_RESET 0x0001u +#define NSF_EXTENDED 0x0002u +#define NSF_DUPNAMES 0x0004u +#define NSF_CONDASSERT 0x0008u -if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) - end = MAX_NON_UTF_CHAR; +/* States used for analyzing ranges in character classes. The two OK values +must be last. */ -/* Use the bitmap for characters < 256. Otherwise use extra data.*/ +enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL }; -for (c = start; c <= classbits_end; c++) - { - /* Regardless of start, c will always be <= 255. */ - SETBIT(classbits, c); - n8++; +/* 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 -#ifdef SUPPORT_WIDE_CHARS -if (start <= 0xff) start = 0xff + 1; +/* Here's the actual function. */ -if (end >= start) +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; +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 isdupname; +BOOL negate_class; +BOOL okquantifier = FALSE; +PCRE2_SPTR name; +PCRE2_SPTR ptrend = cb->end_pattern; +PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */ +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 */ + +*has_lookbehind = FALSE; + +while (ptr < ptrend) { - PCRE2_UCHAR *uchardata = *uchardptr; + 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_SPTR thisptr; + PCRE2_SIZE offset; + + if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) + if (nest_depth > cb->cx->parens_nest_limit) { - if (start < end) + errorcode = ERR19; + goto FAILED; + } + + /* 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) { - *uchardata++ = XCL_RANGE; - uchardata += PRIV(ord2utf)(start, uchardata); - uchardata += PRIV(ord2utf)(end, uchardata); + inescq = FALSE; + ptr++; /* Skip E */ } - else if (start == end) + else { - *uchardata++ = XCL_SINGLE; - uchardata += PRIV(ord2utf)(start, uchardata); + 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, options, + parsed_pattern, cb); + PARSED_LITERAL(c, parsed_pattern); + meta_quantifier = 0; } + continue; /* Next character */ } - 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) + /* 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)) || + /* OR: character > 255 */ + c > 255 || + /* OR: not a # comment or white space */ + (c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0) + )) { - *uchardata++ = XCL_SINGLE; - *uchardata++ = start; - } -#endif - *uchardptr = uchardata; /* Updata extra data pointer */ - } -#else - (void)uchardptr; /* Avoid compiler warning */ -#endif /* SUPPORT_WIDE_CHARS */ + PCRE2_SIZE verbnamelength; -return n8; /* Number of 8-bit characters */ -} + 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; + 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 */ -/************************************************* -* Add a list of characters to a class * -*************************************************/ + switch(escape) + { + case 0: + PARSED_LITERAL(c, parsed_pattern); + break; -/* 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. + case ESC_Q: + inescq = TRUE; + break; -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 + case ESC_E: /* Ignore */ + break; -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ + default: + errorcode = ERR40; /* Invalid in verb name */ + goto FAILED; + } + } + continue; /* Next character in pattern */ + } -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; + /* 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. */ - /* Skip over literals */ - - if (inescq) + if (c == CHAR_BACKSLASH && ptr < ptrend) { - if (x == CHAR_BACKSLASH && ptr[1] == CHAR_E) + if (*ptr == CHAR_Q || *ptr == CHAR_E) { - inescq = FALSE; - ptr++;; + inescq = *ptr == CHAR_Q; + ptr++; continue; } } - else /* Not a literal character */ - { - if (x == CHAR_RIGHT_PARENTHESIS) break; - - /* Skip over comments and whitespace in extended mode. */ + /* 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. */ - if ((options & PCRE2_EXTENDED) != 0) + if ((options & PCRE2_EXTENDED) != 0) + { + if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue; + if (c == CHAR_NUMBER_SIGN) { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(x) && (cb->ctypes[x] & ctype_space) != 0) x = *(++ptr); - if (x == 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++; - 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); + if (utf) FORWARDCHARTEST(ptr, ptrend); #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; } + continue; /* Next character in pattern */ } } - /* We have the next character in the name. */ + /* Skip over bracketed comments */ -#ifdef SUPPORT_UNICODE - if (utf) + if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 && + ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN) { - 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 + while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS); + if (ptr >= ptrend) { - PCRE2_UCHAR cbuff[8]; - x = PRIV(ord2utf)(x, cbuff); - memcpy(code, cbuff, CU2BYTES(x)); - code += x; + errorcode = ERR18; /* A special error for missing ) in a comment */ + goto FAILED; /* to make it easier to debug. */ } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF */ - { - if (code != NULL) *code++ = (PCRE2_UCHAR)x; + ptr++; + continue; /* Next character in pattern */ } - arglen++; + /* If the next item is not a quantifier, fill in length of any previous + callout and create an auto callout if required. */ - if ((unsigned int)arglen > MAX_MARK) + 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)))) { - *errorcodeptr = ERR76; - *ptrptr = ptr; - return -1; + if (after_manual_callout-- <= 0) + parsed_pattern = manage_callouts(thisptr, &previous_callout, options, + parsed_pattern, cb); } - } -/* Update the pointers before returning. */ + /* 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. */ -*ptrptr = ptr; -if (codeptr != NULL) *codeptr = code; -return arglen; -} + 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; -/************************************************* -* Macro for the next two functions * -*************************************************/ + default: + ok = FALSE; + } -/* 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; \ - } \ + 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; -/************************************************* -* 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). */ + /* Remember quantification status for the previous significant item, then set + default for this item. */ - if (skiptoket != 0) - { - if (c != CHAR_RIGHT_PARENTHESIS) continue; - skiptoket = 0; - } + prev_okquantifier = okquantifier; + prev_meta_quantifier = meta_quantifier; + okquantifier = FALSE; + meta_quantifier = 0; - /* Skip over literals */ + /* 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 (inescq) + if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS)) { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++; - } - continue; + parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] = + prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)? + 0x00020000u : 0x00010000u); + continue; /* Next character in pattern */ } - /* 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. */ + /* Process the next item in the main part of a pattern. */ switch(c) { - default: /* Most characters are just skipped */ + default: /* Non-special character */ + PARSED_LITERAL(c, parsed_pattern); break; - /* Skip escapes except for \Q */ + + /* ---- Escape sequence ---- */ case CHAR_BACKSLASH: - errorcode = 0; - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, options, + escape = PRIV(check_escape)(&ptr, ptrend, &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. */ + /* The escape was a data character. */ - 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) + if (escape == 0) { - ptr += 6; - break; + PARSED_LITERAL(c, parsed_pattern); } - /* 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. */ + /* 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. */ - negate_class = FALSE; - for (;;) + else if (escape < 0) { - c = *(++ptr); /* First character in class */ - if (c == CHAR_BACKSLASH) + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1); + escape = -escape; + *parsed_pattern++ = META_BACKREF | (uint32_t)escape; + if (escape < 10) { - 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; + if (cb->small_ref_offset[escape] == PCRE2_UNSET) + cb->small_ref_offset[escape] = offset; } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; + else + { + PUTOFFSET(offset, parsed_pattern); + } + okquantifier = TRUE; } - if (c == CHAR_RIGHT_SQUARE_BRACKET && - (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) - break; - - /* Loop for the contents of the class */ - - for (;;) + /* 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) { - PCRE2_SPTR tempptr; - - if (c == CHAR_NULL && ptr >= cb->end_pattern) + case ESC_C: +#ifdef NEVER_BACKSLASH_C + errorcode = ERR85; + goto FAILED; +#else + if ((options & PCRE2_NEVER_BACKSLASH_C) != 0) { - errorcode = ERR6; /* Missing terminating ']' */ + errorcode = ERR83; goto FAILED; } +#endif + okquantifier = TRUE; + *parsed_pattern++ = META_ESCAPE + escape; + break; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } + case ESC_X: +#ifndef SUPPORT_UNICODE + errorcode = ERR45; /* Supported only with Unicode support */ + goto 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; - /* Inside \Q...\E everything is literal except \E */ + default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */ + *parsed_pattern++ = META_ESCAPE + escape; + break; - if (inescq) + /* 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) { - 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; + *parsed_pattern++ = META_ESCAPE + escape; } - - /* 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)) + else { - 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; - } + *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; - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; - } /* End of class-processing loop */ - break; + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; - /* This is the real work of this function - handling parentheses. */ + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; - case CHAR_LEFT_PARENTHESIS: - nest_depth++; + /* Unicode property matching */ - if (ptr[1] != CHAR_QUESTION_MARK) - { - if (ptr[1] != CHAR_ASTERISK) + 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; + okquantifier = TRUE; + } +#else + errorcode = ERR45; + goto 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)) { - if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) cb->bracount++; + errorcode = (escape == ESC_g)? ERR57 : ERR69; + goto FAILED; } + terminator = (*ptr == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? + CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; - /* (*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. */ + /* For a non-braced \g, check for a numerical recursion. */ - else + if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET) { - ptr += 2; - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; - if (*ptr == CHAR_COLON && (options & PCRE2_ALT_VERBNAMES) != 0) + PCRE2_SPTR p = ptr + 1; + + if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) { - ptr++; - if (process_verb_name(&ptr, NULL, &errorcode, options, utf, cb) < 0) + if (p >= ptrend || *p != terminator) + { + errorcode = ERR57; goto FAILED; + } + ptr = p; + goto SET_RECURSION; } - else - { - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) - ptr++; - } - nest_depth--; + if (errorcode != 0) goto FAILED; } - } - /* Handle (?...) groups */ + /* Not a numerical recursion */ - 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; - } + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; - /* Handle (?| and (?imsxJU: which are the only other valid forms. Both - need a new block on the nest stack. */ + /* \k and \g when used with braces are back references, whereas \g used + with quotes or angle brackets is a recursion */ - 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; + *parsed_pattern++ = + (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)? + META_BACKREF_BYNAME : META_RECURSE_BYNAME; + *parsed_pattern++ = namelen; - 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; - } + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; + } + break; /* End escape sequence processing */ - /* Scan options */ - top_nest->reset_group = 0; - top_nest->max_group = 0; + /* ---- Single-character special items ---- */ - set = unset = 0; - optset = &set; + case CHAR_CIRCUMFLEX_ACCENT: + *parsed_pattern++ = META_CIRCUMFLEX; + break; - /* Need only track (?x: and (?J: at this stage */ + case CHAR_DOLLAR_SIGN: + *parsed_pattern++ = META_DOLLAR; + break; - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; break; + case CHAR_DOT: + *parsed_pattern++ = META_DOT; + okquantifier = TRUE; + break; - case CHAR_x: *optset |= PCRE2_EXTENDED; break; - case CHAR_J: - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; + /* ---- Single-character quantifiers ---- */ - case CHAR_i: - case CHAR_m: - case CHAR_s: - case CHAR_U: - break; + case CHAR_ASTERISK: + meta_quantifier = META_ASTERISK; + goto CHECK_QUANTIFIER; - default: - errorcode = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; - } - } + case CHAR_PLUS: + meta_quantifier = META_PLUS; + goto CHECK_QUANTIFIER; - options = (options | set) & (~unset); + case CHAR_QUESTION_MARK: + meta_quantifier = META_QUERY; + goto CHECK_QUANTIFIER; - /* 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; + /* ---- Potential {n,m} quantifier ---- */ - /* Skip over a numerical or string argument for a callout. */ + 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 */ - 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 */ + /* ---- Quantifier post-processing ---- */ - 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; - } - } + /* Check that a quantifier is allowed after the previous item. */ - if (delimiter == 0) - { - errorcode = ERR82; - goto FAILED; - } + CHECK_QUANTIFIER: + if (!prev_okquantifier) + { + errorcode = ERR9; + goto FAILED_BACK; + } - 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); - } + /* 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. */ - /* Check terminating ) */ + *parsed_pattern++ = meta_quantifier; + if (c == CHAR_LEFT_CURLY_BRACKET) + { + *parsed_pattern++ = min_repeat; + *parsed_pattern++ = max_repeat; + } + break; - if (ptr[1] != CHAR_RIGHT_PARENTHESIS) - { - errorcode = ERR39; - ptr++; - goto FAILED; - } - break; - /* Conditional group */ + /* ---- Character class ---- */ - case CHAR_LEFT_PARENTHESIS: - if (ptr[3] != CHAR_QUESTION_MARK) /* Not assertion or callout */ - { - nest_depth++; - ptr += 2; - break; - } + case CHAR_LEFT_SQUARE_BRACKET: + okquantifier = TRUE; - /* Must be an assertion or a callout */ + /* 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. */ - 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; + 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; - default: - MISSING_ASSERTION: - ptr += 3; /* To improve error message */ - errorcode = ERR28; - goto FAILED; - } - break; + if (ptr[2] == CHAR_LESS_THAN_SIGN) + { + *parsed_pattern++ = META_LOOKAHEAD; + } + else + { + *parsed_pattern++ = META_LOOKBEHIND; + *has_lookbehind = TRUE; - 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; + /* The offset is used only for the "non-fixed length" error; this won't + occur here, so just store zero. */ - case CHAR_P: - if (ptr[3] != CHAR_LESS_THAN_SIGN) - { - ptr += 3; - break; + PUTOFFSET((PCRE2_SIZE)0, parsed_pattern); } - 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) + if ((options & PCRE2_UCP) == 0) + *parsed_pattern++ = META_ESCAPE + ESC_w; + else { - ptr += 3; - break; + *parsed_pattern++ = META_ESCAPE + ESC_p; + *parsed_pattern++ = PT_WORD << 16; } - c = CHAR_GREATER_THAN_SIGN; /* Terminator */ - goto DEFINE_NAME; + *parsed_pattern++ = META_KET; + ptr += 6; + break; + } - case CHAR_APOSTROPHE: - c = CHAR_APOSTROPHE; /* Terminator */ + /* 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. */ - DEFINE_NAME: - name = ptr = ptr + 3; + 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; + } - if (*ptr == c) /* Empty name */ - { - errorcode = ERR62; - 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 we skip them too. This makes for compatibility with Perl. */ - if (IS_DIGIT(*ptr)) + negate_class = FALSE; + while (ptr < ptrend) + { + GETCHARINCTEST(c, ptr); + if (c == CHAR_BACKSLASH) { - errorcode = ERR44; /* Group name must start with non-digit */ - goto FAILED; + 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 (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) + negate_class = TRUE; + else break; + } - if (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) == 0) - { - errorcode = ERR24; - goto FAILED; - } + /* Now the real contents of the class; c has the first "real" character. + Empty classes are permitted only if the option is set. */ - /* Advance ptr, set namelen and check its length. */ - READ_NAME(ctype_word, ERR48, errorcode); + 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 */ + } - if (*ptr != c) - { - errorcode = ERR42; - goto FAILED; - } + /* Process a non-empty class. */ - if (cb->names_found >= MAX_NAME_COUNT) - { - errorcode = ERR49; - goto FAILED; - } + *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS; + class_range_state = RANGE_NO; - if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) - cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); + /* 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. */ - /* We have a valid name for this capturing group. */ + /* Loop for the contents of the class */ - cb->bracount++; + for (;;) + { + BOOL char_is_literal = TRUE; - /* 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. */ + /* Inside \Q...\E everything is literal except \E */ - isdupname = FALSE; - ng = cb->named_groups; - for (i = 0; i < cb->names_found; i++, ng++) + if (inescq) { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, (size_t)namelen) == 0) + if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) { - 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 */ + inescq = FALSE; /* Reset literal state */ + ptr++; /* Skip the 'E' */ + goto CLASS_CONTINUE; } - else if (ng->number == cb->bracount) + goto CLASS_LITERAL; + } + + /* 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 = ERR65; + errorcode = ERR50; goto FAILED; } - } - if (i < cb->names_found) break; /* Ignore duplicate with same number */ + if (*ptr != CHAR_COLON) + { + errorcode = ERR13; + goto FAILED_BACK; + } - /* Increase the list size if necessary */ + if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT) + { + posix_negate = TRUE; + ptr++; + } - 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) + posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); + if (posix_class < 0) { - errorcode = ERR21; + errorcode = ERR30; goto FAILED; } + ptr = tempptr + 2; - 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; - } + /* 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. PCRE + does not have a warning mode, so we give an error, because this is + likely an error on the user's part. */ - /* Add this name to the list */ + if (ptr < ptrend && *ptr == CHAR_MINUS) + { + errorcode = ERR50; + goto FAILED; + } - 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 */ + /* Set "a hyphen is not the start of a range" just 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. */ - /* At an alternation, reset the capture count if we are in a (?| group. */ + class_range_state = RANGE_NO; - 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; + /* 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. */ - /* At a right parenthesis, reset the capture count to the maximum if we - are in a (?| group and/or reset the extended option. */ +#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; + } - 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 (pvalue != 0) + { + *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h); + goto CLASS_CONTINUE; + } -if (nest_depth == 0) - { - cb->final_bracount = cb->bracount; - return 0; - } + /* Fall through */ + } +#endif /* SUPPORT_UNICODE */ -/* We give a special error for a missing closing parentheses after (?# because -it might otherwise be hard to see where the missing character is. */ + /* Non-UCP POSIX class */ -errorcode = (skiptoket == CHAR_NUMBER_SIGN)? ERR18 : ERR14; + *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX; + *parsed_pattern++ = posix_class; + } -FAILED: -*ptrptr = ptr; -return errorcode; -} + /* 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 */ -/************************************************* -* Compile one branch * -*************************************************/ + 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); + } + } -/* 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. + /* Handle escapes in a class */ -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 + else + { + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, + options, TRUE, cb); -Returns: TRUE on success - FALSE, with *errorcodeptr set non-zero on error -*/ + if (errorcode != 0) goto FAILED; + if (escape == 0) /* Escaped character code point is in c */ + { + char_is_literal = FALSE; + goto CLASS_LITERAL; + } -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]; + /* These three escapes do not alter the class range state. */ -/* 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. */ + if (escape == ESC_b) + { + c = CHAR_BS; /* \b is backspace in a class */ + char_is_literal = FALSE; + goto CLASS_LITERAL; + } -#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 if (escape == ESC_Q) + { + inescq = TRUE; /* Enter literal mode */ + goto CLASS_CONTINUE; + } -#else /* No UTF support */ -BOOL utf = FALSE; -#endif + else if (escape == ESC_E) /* Ignore orphan \E */ + goto CLASS_CONTINUE; -/* 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. */ + /* 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. */ -PCRE2_UCHAR *class_uchardata; -#ifdef SUPPORT_WIDE_CHARS -BOOL xclass; -PCRE2_UCHAR *class_uchardata_base; -#endif + if (class_range_state == RANGE_STARTED) + { + errorcode = ERR50; + goto FAILED; + } -/* Set up the default and non-default settings for greediness */ + /* Of the remaining escapes, only those that define characters are + allowed in a class. None may start a range. */ -greedy_default = ((options & PCRE2_UNGREEDY) != 0); -greedy_non_default = greedy_default ^ 1; + class_range_state = RANGE_NO; + switch(escape) + { + case ESC_N: + errorcode = ERR71; /* Not supported in a class */ + goto FAILED; -/* 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. + case ESC_H: + case ESC_h: + case ESC_V: + case ESC_v: + *parsed_pattern++ = META_ESCAPE + escape; + break; -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. */ + /* These escapes are converted to Unicode property tests when + PCRE2_UCP is set. */ -firstcu = reqcu = zerofirstcu = zeroreqcu = 0; -firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; + 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; -/* 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. -*/ + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; -req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; -/* Switch on next character until the end of the branch */ + /* Explicit Unicode property matching */ -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; + 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 FAILED; #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: + break; /* End \P and \p */ - /* 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. */ + default: /* All others are not allowed in a class */ + errorcode = ERR7; + goto FAILED_BACK; + } + } - if (c == CHAR_NULL && cb->nestptr[0] != NULL) - { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *ptr; - } + /* Proceed to next thing in the class. */ - /* If we are in the pre-compile phase, accumulate the length used for the - previous cycle of this loop. */ + 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 (lengthptr != NULL) - { - if (code > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ + if (class_range_state == RANGE_STARTED) { - *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? - ERR52 : ERR86; - goto FAILED; + parsed_pattern[-1] = CHAR_MINUS; + class_range_state = RANGE_NO; } - /* 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. - */ + *parsed_pattern++ = META_CLASS_END; + break; /* End of character class */ - if (code < last_code) code = last_code; - /* Paranoid check for integer overflow */ + /* ---- Opening parenthesis ---- */ - if (OFLOW_MAX - *lengthptr < (size_t)(code - last_code)) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += (size_t)(code - last_code); + case CHAR_LEFT_PARENTHESIS: + if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; - /* 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 ( is not followed by ? it is either a capture or a special verb. */ - if (previous != NULL) + if (*ptr != CHAR_QUESTION_MARK) { - if (previous > orig_code) + const char *vn; + + /* Handle capturing brackets (or non-capturing if auto-capture is turned + off). */ + + if (*ptr != CHAR_ASTERISK) { - memmove(orig_code, previous, (size_t)CU2BYTES(code - previous)); - code -= previous - orig_code; - previous = orig_code; + nest_depth++; + if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) + { + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + } + else *parsed_pattern++ = META_NOCAPTURE; } - } - 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. */ + /* ---- Handle (*VERB) and (*VERB:NAME) ---- */ - /* 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. */ + /* Do nothing for (*) so it gives a "bad quantifier" error rather than + "(*MARK) must have an argument". */ - 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) + else if (ptrend - ptr > 1 && ptr[1] != CHAR_RIGHT_PARENTHESIS) { - 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; - } + 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; + } - /* 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. */ + /* Scan the table of verb names */ - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_Q) - { - inescq = TRUE; - ptr++; - continue; - } - } - - /* In extended mode, skip white space and #-comments that end at newline. */ + 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 ((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; + if (i >= verbcount) + { + errorcode = ERR60; /* Verb not recognized */ + goto FAILED; } - ptr++; -#ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(ptr); -#endif - } - } - /* If we skipped any characters, restart the loop. Otherwise, we didn't see - a comment. */ + /* An empty argument is treated as no argument. */ - if (ptr > wscptr) goto REDO_LOOP; - } + if (*ptr == CHAR_COLON && ptr + 1 < ptrend && + ptr[1] == CHAR_RIGHT_PARENTHESIS) + ptr++; /* Advance to the closing parens */ - /* Skip over (?# comments. */ + /* Check for mandatory non-empty argument; this is (*MARK) */ - 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; - } + if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON) + { + errorcode = ERR66; + goto FAILED; + } - /* End of processing "do nothing" items. See if the next thing is a - quantifier. */ + /* 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. */ - is_quantifier = - c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK || - (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1)); + if (*ptr++ == CHAR_COLON) /* Skip past : or ) */ + { + if (verbs[i].has_arg < 0) /* Argument is forbidden */ + { + errorcode = ERR59; + goto FAILED; + } + *parsed_pattern++ = verbs[i].meta + + ((verbs[i].meta != META_MARK)? 0x00010000u:0); + 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 (? */ - /* 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; - } + /* ---- Items starting (? ---- */ - if ((options & PCRE2_AUTO_CALLOUT) != 0) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); - } - } + /* 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. */ - /* Process the next pattern item. */ + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; - switch(c) - { - /* ===================================================================*/ - /* The branch terminates at string end or | or ) */ + switch(*ptr) + { + default: + if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1])) + goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */ - case CHAR_NULL: - if (ptr < cb->end_pattern) goto NORMAL_CHAR; /* Zero data character */ - /* Fall through */ + /* We now have either (?| or a (possibly empty) option setting, + optionally followed by a non-capturing group. */ - 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)) + nest_depth++; + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) { - *errorcodeptr = ERR20; + errorcode = ERR84; goto FAILED; } - *lengthptr += (size_t)(code - last_code); /* To include callout length */ - } - return TRUE; + 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; + /* Start of non-capturing group that resets the capture count for each + branch. */ - /* ===================================================================*/ - /* Handle single-character metacharacters. In multiline mode, ^ disables - the setting of any following char as a first character. */ + 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++; + } - 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; + /* Scan for options imsxJU. We need to keep track of (?x) and (?J) for + use while scanning. The other options are used during the compiling + phases. */ - case CHAR_DOLLAR_SIGN: - previous = NULL; - *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; - break; + else + { + top_nest->reset_group = 0; + top_nest->max_group = 0; + set = unset = 0; + optset = &set; - /* There can never be a first char if '.' is first, whatever happens about - repeats. The value of reqcu doesn't change either. */ + while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS && + *ptr != CHAR_COLON) + { + switch (*ptr++) + { + case CHAR_MINUS: optset = &unset; break; - 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; + 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; - /* ===================================================================*/ - /* 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. + default: + errorcode = ERR11; + ptr--; /* Correct the offset */ + goto FAILED; + } + } + options = (options | set) & (~unset); - 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. + /* 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; - 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. + /* If nothing changed, no need to record. */ - 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. */ + if (set != 0 || unset != 0) + { + *parsed_pattern++ = META_OPTIONS; + *parsed_pattern++ = options; + } + } /* End options processing */ + break; /* End default case after (? */ - 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; - } + /* ---- Python syntax support ---- */ - /* Handle a real character class. */ + case CHAR_P: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; - previous = code; + /* (?P is the same as (?, which defines a named group. */ - /* 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 == CHAR_LESS_THAN_SIGN) + { + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; + } - 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; - } + /* (?P>name) is the same as (?&name), which is a recursion or subroutine + call. */ - /* 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. */ + if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME; - negate_class = FALSE; - for (;;) - { - c = *(++ptr); - if (c == CHAR_BACKSLASH) + /* (?P=name) is the same as \k, a back reference by name. Anything + else after (?P is an error. */ + + if (*ptr != CHAR_EQUALS_SIGN) { - 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; + errorcode = ERR41; + goto FAILED; } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; - } + 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 */ - /* 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; - } + /* ---- Recursion/subroutine calls by number ---- */ - /* 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. - */ + case CHAR_R: + i = 0; /* (?R) == (?R0) */ + ptr++; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR58; + goto FAILED; + } + goto SET_RECURSION; - should_flip_negation = match_all_or_no_wide_chars = FALSE; + /* An item starting (?- followed by a digit comes here via the "default" + case because (?- followed by a non-digit is an options setting. */ - /* Extended class (xclass) will be used when characters > 255 - might match. */ + 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; -#ifdef SUPPORT_WIDE_CHARS - xclass = FALSE; - class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ - class_uchardata_base = class_uchardata; /* Save the start */ -#endif + 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 */ - /* 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 + /* ---- Recursion/subroutine calls by name ---- */ - /* 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. */ + 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; - memset(classbits, 0, 32 * sizeof(uint8_t)); + /* ---- Callout with numerical or string argument ---- */ - /* 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. */ + case CHAR_C: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; - for(;;) - { - PCRE2_SPTR oldptr; -#ifdef EBCDIC - BOOL range_is_literal = TRUE; -#endif + /* 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. */ - if (c == CHAR_NULL && ptr >= cb->end_pattern) - { - *errorcodeptr = ERR6; /* Missing terminating ']' */ - goto FAILED; - } + expect_cond_assert = prev_expect_cond_assert - 1; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } -#endif + /* 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. */ - /* Inside \Q...\E everything is literal except \E */ + if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 && + previous_callout == parsed_pattern - 4 && + parsed_pattern[-1] == 255) + parsed_pattern = previous_callout; - 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 */ - } + /* Save for updating next pattern item length, and skip one item before + completing. */ - /* 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. */ + previous_callout = parsed_pattern; + after_manual_callout = 1; - if (c == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) + /* Handle a string argument; specific delimiter is required. */ + + if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) { - BOOL local_negate = FALSE; - int posix_class, taboffset, tabopt; - register const uint8_t *cbits = cb->cbits; - uint8_t pbits[32]; + PCRE2_SIZE calloutlength; + PCRE2_SPTR startptr = ptr; - if (ptr[1] != CHAR_COLON) + 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 = ERR13; + errorcode = ERR82; goto FAILED; } - ptr += 2; - if (*ptr == CHAR_CIRCUMFLEX_ACCENT) + *parsed_pattern = META_CALLOUT_STRING; + parsed_pattern += 3; /* Skip pattern info */ + + for (;;) { - local_negate = TRUE; - should_flip_negation = TRUE; /* Note negative special */ - ptr++; + if (++ptr >= ptrend) + { + errorcode = ERR81; + ptr = startptr; /* To give a more useful message */ + goto FAILED; + } + if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter)) + break; } - posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); - if (posix_class < 0) + calloutlength = (PCRE2_SIZE)(ptr - startptr); + if (calloutlength > UINT32_MAX) { - *errorcodeptr = ERR30; + errorcode = ERR72; goto FAILED; } + *parsed_pattern++ = (uint32_t)calloutlength; + offset = (PCRE2_SIZE)(startptr - cb->start_pattern); + PUTOFFSET(offset, parsed_pattern); + } - /* 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; + /* Handle a callout with an optional numerical argument, which must be + less than or equal to 255. A missing argument gives 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) + else + { + int n = 0; + *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */ + parsed_pattern += 3; /* Skip pattern info */ + while (ptr < ptrend && IS_DIGIT(*ptr)) { - 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) + n = n * 10 + *ptr++ - CHAR_0; + if (n > 255) { - 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; + errorcode = ERR38; + goto FAILED; } } -#endif /* SUPPORT_UNICODE */ + *parsed_pattern++ = n; + } - /* 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. */ + /* Both formats must have a closing parenthesis */ - posix_class *= 3; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR39; + goto FAILED; + } + ptr++; - /* Copy in the first table (always present) */ + /* 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. */ - memcpy(pbits, cbits + posix_class_maps[posix_class], - 32 * sizeof(uint8_t)); + previous_callout[1] = ptr - cb->start_pattern; + previous_callout[2] = 0; + break; /* End callout */ - /* 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]; + /* ---- Conditional group ---- */ - 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]; - } + /* 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. - /* Now see if we need to remove any special characters. An option - value of 1 removes vertical space and 2 removes underscore. */ + There are several syntaxes for testing a named group: (?(name)) is used + by Python; Perl 5.10 onwards uses (?() or (?('name')). - if (tabopt < 0) tabopt = -tabopt; - if (tabopt == 1) pbits[1] &= ~0x3c; - else if (tabopt == 2) pbits[11] &= 0x7f; + 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. - /* Add the POSIX table or its complement into the main table that is - being built and we are done. */ + For compatibility with auto-callouts, we allow a callout to be specified + before a condition that is an assertion. */ - if (local_negate) - for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c]; - else - for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; + case CHAR_LEFT_PARENTHESIS: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + nest_depth++; - 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 */ + /* 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 */ } - /* 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. */ + /* Handle (?([+-]number)... */ - if (c == CHAR_BACKSLASH) + if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) { - 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 */ + if (i <= 0) { - *errorcodeptr = ERR71; + errorcode = ERR15; 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; + *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 */ - /* The rest apply in both UCP and non-UCP cases. */ + /* No number found. Handle the special case (?(VERSION[>]=n.m)... */ - case ESC_h: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(hspace_list), NOTACHAR); - break; + 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; - case ESC_H: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(hspace_list)); - break; + ptr += 7; + if (*ptr == CHAR_GREATER_THAN_SIGN) + { + ge = 1; + ptr++; + } - case ESC_v: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(vspace_list), NOTACHAR); - break; + /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT + references its argument twice. */ - case ESC_V: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(vspace_list)); - break; + if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) + goto BAD_VERSION_CONDITION; - 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. */ + if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode)) + goto FAILED; - default: - *errorcodeptr = ERR7; + if (ptr >= ptrend) goto BAD_VERSION_CONDITION; + if (*ptr == CHAR_DOT) + { + if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION; + if (!read_number(&ptr, ptrend, -1, 99 , ERR79, &minor, &errorcode)) goto FAILED; - } - - /* Handled \d-type escape */ - - goto CONTINUE_CLASS; + if (minor < 10) minor *= 10; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + goto BAD_VERSION_CONDITION; } - /* 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; + *parsed_pattern++ = META_COND_VERSION; + *parsed_pattern++ = ge; + *parsed_pattern++ = major; + *parsed_pattern++ = minor; } - 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 */ + /* 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. */ - if (!inescq && ptr[1] == CHAR_MINUS) + else { - 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. */ + BOOL was_r_ampersand = FALSE; - while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q) + if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND) { - ptr += 2; - if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { ptr += 2; continue; } - inescq = TRUE; - break; + terminator = CHAR_RIGHT_PARENTHESIS; + was_r_ampersand = TRUE; + ptr++; } - - /* 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)) + else if (*ptr == CHAR_LESS_THAN_SIGN) + terminator = CHAR_GREATER_THAN_SIGN; + else if (*ptr == CHAR_APOSTROPHE) + terminator = CHAR_APOSTROPHE; + else { - ptr = oldptr; - goto CLASS_SINGLE_CHARACTER; + terminator = CHAR_RIGHT_PARENTHESIS; + ptr--; /* Point to char before name */ } + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; - /* Otherwise, we have a potential range; pick up the next character */ + /* Handle (?(R&name) */ -#ifdef SUPPORT_UNICODE - if (utf) - { /* Braces are required because the */ - GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ + if (was_r_ampersand) + { + *parsed_pattern = META_COND_RNAME; + ptr--; /* Back to closing parens */ } - 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. */ + /* 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. */ - if (!inescq) + else if (terminator == CHAR_RIGHT_PARENTHESIS) { - 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)) + if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) + *parsed_pattern = META_COND_DEFINE; + else { - *errorcodeptr = ERR50; - goto FAILED; + 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 */ } - /* 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]. */ + /* Handle (?('name') or (?() */ -#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; + else *parsed_pattern = META_COND_NAME; - 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; - } + /* All these cases except DEFINE end with the name length and offset; + DEFINE just has an offset (for the "too many branches" error). */ - 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 (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + } /* End cases that read a name */ - 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 */ - } + /* Check the closing parenthesis of the condition */ - /* 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) + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) { - 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; + errorcode = ERR24; + goto FAILED; + } + ptr++; + break; /* End of condition processing */ - /* 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 */ + /* ---- Atomic group ---- */ - { - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; - code += PUTCHAR(c, code); - } + case CHAR_GREATER_THAN_SIGN: + *parsed_pattern++ = META_ATOMIC; + nest_depth++; + ptr++; + break; - /* We are finished with this character class */ - goto END_CLASS; - } + /* ---- Lookahead assertions ---- */ - /* For a single, positive character, get the value into mcbuffer, and - then we can handle this with the normal one-character code. */ + case CHAR_EQUALS_SIGN: + *parsed_pattern++ = META_LOOKAHEAD; + ptr++; + goto POST_ASSERTION; - mclength = PUTCHAR(c, mcbuffer); - goto ONE_CHAR; - } /* End of 1-char optimization */ + case CHAR_EXCLAMATION_MARK: + *parsed_pattern++ = META_LOOKAHEADNOT; + ptr++; + goto POST_ASSERTION; - /* 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); + /* ---- Lookbehind assertions ---- */ - /* 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). */ + /* (?< followed by = or ! is a lookbehind assertion. Otherwise (?< is the + start of the name of a capturing group. */ - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_NULL && cb->nestptr[0] != NULL) + case CHAR_LESS_THAN_SIGN: + if (ptrend - ptr <= 1 || + (ptr[1] != CHAR_EQUALS_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK)) { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *(++ptr); + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; } - -#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) + *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) { - xclass = TRUE; - if (lengthptr != NULL) + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) { - *lengthptr += class_uchardata - class_uchardata_base; - class_uchardata = class_uchardata_base; + errorcode = ERR84; + goto FAILED; } + top_nest->nest_depth = nest_depth; + top_nest->flags = NSF_CONDASSERT; + if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED; + if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES; } -#endif - /* An unescaped ] ends the class */ + break; - 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. */ + /* ---- Define a named group ---- */ - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; + /* 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 '>'. */ - /* 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. + case CHAR_APOSTROPHE: + terminator = CHAR_APOSTROPHE; /* Terminator */ - 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. + DEFINE_NAME: + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; - If, when generating an xclass, there are no characters < 256, we can omit - the bitmap in the actual compiled code. */ + /* We have a name for this capturing group. It is also assigned a number, + which is its primary means of identification. */ -#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; + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + nest_depth++; - /* 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. */ + /* Check not too many names */ - if (class_has_8bitchar > 0) + if (cb->names_found >= MAX_NAME_COUNT) { - *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)); + errorcode = ERR49; + goto FAILED; } - else code = class_uchardata; - /* Now fill in the complete length of the item */ + /* Adjust the entry size to accommodate the longest name found. */ - 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; + if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) + cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); - case CHAR_ASTERISK: - repeat_min = 0; - repeat_max = -1; - goto REPEAT; + /* 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. */ - case CHAR_PLUS: - repeat_min = 1; - repeat_max = -1; - goto REPEAT; + 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; + } + } - case CHAR_QUESTION_MARK: - repeat_min = 0; - repeat_max = 1; + if (i < cb->names_found) break; /* Ignore duplicate with same number */ - REPEAT: - if (previous == NULL) - { - *errorcodeptr = ERR9; - goto FAILED; - } + /* Increase the list size if necessary */ - if (repeat_min == 0) - { - firstcu = zerofirstcu; /* Adjust for zero repeat */ - firstcuflags = zerofirstcuflags; - reqcu = zeroreqcu; /* Ditto */ - reqcuflags = zeroreqcuflags; - } + 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; + } - /* Remember whether this is a variable length repeat */ + 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; + } - reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + /* Add this name to the list */ - op_type = 0; /* Default single-char op codes */ - possessive_quantifier = FALSE; /* Default not possessive quantifier */ + 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 */ - /* Save start of previous item, in case we have to move it up in order to - insert something before it. */ - tempcode = previous; + /* ---- Branch terminators ---- */ - /* Before checking for a possessive quantifier, we must skip over - whitespace and comments in extended mode because Perl allows white space at - this point. */ + /* Alternation: reset the capture count if we are in a (?| group. */ - if ((options & PCRE2_EXTENDED) != 0) + case CHAR_VERTICAL_LINE: + if (top_nest != NULL && top_nest->nest_depth == nest_depth && + (top_nest->flags & NSF_RESET) != 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 (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; - /* 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. */ + /* End of group; reset the capture count to the maximum if we are in a (?| + group and/or reset the extended and dupnames options. Disallow quantifier + for a condition that is an assertion. */ - if (ptr[1] == CHAR_PLUS) + case CHAR_RIGHT_PARENTHESIS: + okquantifier = TRUE; + if (top_nest != NULL && top_nest->nest_depth == nest_depth) { - repeat_type = 0; /* Force greedy */ - possessive_quantifier = TRUE; - ptr++; + 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->flags & NSF_CONDASSERT) != 0) + okquantifier = FALSE; + if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; + else top_nest--; } - else if (ptr[1] == CHAR_QUESTION_MARK) + if (nest_depth == 0) /* Unmatched closing parenthesis */ { - repeat_type = greedy_non_default; - ptr++; + errorcode = ERR22; + goto FAILED_BACK; } - else repeat_type = greedy_default; + nest_depth--; + *parsed_pattern++ = META_KET; + break; + } /* End of switch on pattern character */ + } /* End of main character scan loop */ - /* If the repeat is {1} we can ignore it. */ +/* End of pattern reached. Check for missing ) at the end of a verb name. */ - if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; +if (inverbname && ptr >= ptrend) + { + errorcode = ERR60; + goto FAILED; + } - /* 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. */ +/* Manage callout for the final item */ - 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; - } +parsed_pattern = manage_callouts(ptr, &previous_callout, options, + parsed_pattern, cb); - /* Now handle repetition for the different types of item. */ +/* Terminate the parsed pattern, then return success if all groups are closed. +Otherwise we have unclosed parentheses. */ - /* 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 (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } +*parsed_pattern = META_END; +if (nest_depth == 0) return 0; - 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; - } +UNCLOSED_PARENTHESIS: +errorcode = ERR14; - /* 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. */ +/* Come here for all failures. */ -#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 */ +FAILED: +cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern); +return errorcode; - /* 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; - } - } +/* Some errors need to indicate the previous character. */ - goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ - } +FAILED_BACK: +ptr--; +goto FAILED; - /* 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. */ +/* This failure happens several times. */ - 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; - } +BAD_VERSION_CONDITION: +errorcode = ERR79; +goto FAILED; +} - /* 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. */ +/************************************************* +* Find first significant op code * +*************************************************/ - if (repeat_max == 0) goto END_REPEAT; +/* 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. - /* Combine the op_type with the repeat_type */ +Arguments: + code pointer to the start of the group + skipassert TRUE if certain assertions are to be skipped - repeat_type += op_type; +Returns: pointer to the first significant opcode +*/ - /* A minimum of zero is handled either as the special case * or ?, or as - an UPTO, with the maximum given. */ +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; - 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); - } - } + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + if (!skipassert) return code; + /* Fall through */ - /* 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. */ + 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; - 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); - } - } + case OP_CALLOUT_STR: + code += GET(code, 1 + 2*LINK_SIZE); + break; - /* 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. */ + default: + return code; + } + } +/* Control never reaches here */ +} - 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 */ +#ifdef SUPPORT_UNICODE +/************************************************* +* Get othercase range * +*************************************************/ - 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); - } - } - } - } +/* 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. - /* Fill in the character or character type for the final opcode. */ +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 -#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; - } - } - } +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 +*/ - /* 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}. */ +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; - 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; - } +/* Find the first character that has an other case. If it has multiple other +cases, return its case offset value. */ - 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); - } - } +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 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. */ +if (c > d) return -1; /* Reached end of range */ - else if (*previous >= OP_ASSERT && *previous <= OP_COND) - { - register int i; - int len = (int)(code - previous); - PCRE2_UCHAR *bralink = NULL; - PCRE2_UCHAR *brazeroptr = NULL; +/* 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. */ - /* 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. */ +*ocptr = othercase; +next = othercase + 1; - if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && - previous[GET(previous, 1)] != OP_ALT) - goto END_REPEAT; +for (++c; c <= d; c++) + { + if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; + next++; + } - /* 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. */ +*odptr = next - 1; /* End of othercase range */ +*cptr = c; /* Rest of input range */ +return 0; +} +#endif /* SUPPORT_UNICODE */ - 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: +/************************************************* +* Add a character or range to a class (internal) * +*************************************************/ - ** if (repeat_max == 0) - ** { - ** code = previous; - ** goto END_REPEAT; - ** } +/* 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(). - 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. +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 - If the maximum is 1 or unlimited, we just have to stick in the BRAZERO - and do no more at this point. */ +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ - 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; - } +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 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. */ +/* 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. */ - 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); - } +if ((options & PCRE2_CASELESS) != 0) + { +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UTF) != 0) + { + int rc; + uint32_t oc, od; - repeat_max--; - } + options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ + c = start; - /* 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. */ + while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) + { + /* Handle a single character that has more than one other case. */ - 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 (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb, + PRIV(ucd_caseless_sets) + rc, oc); - 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; - } + /* Do nothing if the other case range is within the original range. */ - /* 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 (oc >= cb->class_range_start && od <= cb->class_range_end) continue; - else - { - if (groupsetfirstcu && reqcuflags < 0) - { - reqcu = firstcu; - reqcuflags = firstcuflags; - } - for (i = 1; i < repeat_min; i++) - { - memcpy(code, previous, CU2BYTES(len)); - code += len; - } - } - } + /* 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. */ - if (repeat_max > 0) repeat_max -= repeat_min; + 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 */ - /* 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. */ + /* Not UTF mode */ - 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; - } + for (c = start; c <= classbits_end; c++) + { + SETBIT(classbits, cb->fcc[c]); + n8++; + } + } - /* This is compiling for real */ +/* 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. */ - else for (i = repeat_max - 1; i >= 0; i--) - { - *code++ = OP_BRAZERO + repeat_type; +if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) + end = MAX_NON_UTF_CHAR; - /* All but the final copy start a new nesting, maintaining the - chain of brackets outstanding. */ +if (start > cb->class_range_start && end < cb->class_range_end) return n8; - if (i != 0) - { - int offset; - *code++ = OP_BRA; - offset = (bralink == NULL)? 0 : (int)(code - bralink); - bralink = code; - PUTINC(code, 0, offset); - } +/* Use the bitmap for characters < 256. Otherwise use extra data.*/ - memcpy(code, previous, CU2BYTES(len)); - code += len; - } +for (c = start; c <= classbits_end; c++) + { + /* Regardless of start, c will always be <= 255. */ + SETBIT(classbits, c); + n8++; + } - /* Now chain through the pending brackets, and fill in their length - fields (which are holding the chain links pro tem). */ +#ifdef SUPPORT_WIDE_CHARS +if (start <= 0xff) start = 0xff + 1; - 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 (end >= start) + { + PCRE2_UCHAR *uchardata = *uchardptr; - /* 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. */ +#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 */ - else - { - PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; - PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); + /* Without UTF support, character values are constrained by the bit length, + and can only be > 256 for 16-bit and 32-bit libraries. */ - /* Convert possessive ONCE brackets to non-capturing */ +#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 */ - if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && - possessive_quantifier) *bracode = OP_BRA; +return n8; /* Number of 8-bit characters */ +} - /* 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). */ +#ifdef SUPPORT_UNICODE +/************************************************* +* Add a list of characters to a class (internal) * +*************************************************/ - else - { - /* In the compile phase, check whether the group could match an empty - string. */ +/* 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. - 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); +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 - /* A conditional group with only one branch has an implicit empty - alternative branch. */ +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ - if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) - *bracode = OP_SCOND; - } +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 - /* 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); - } +/************************************************* +* External entry point for add range to class * +*************************************************/ - /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ +/* This function sets the overall range so that the internal functions can try +to avoid duplication when handling case-independence. - else - { - *bracode += 1; /* Switch to xxxPOS opcodes */ - *ketcode = OP_KETRPOS; - } +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 - /* If the minimum is zero, mark it as possessive, then unset the - possessive flag when the minimum is 0 or 1. */ +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ - if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; - if (repeat_min < 2) possessive_quantifier = FALSE; - } +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); +} - /* Non-possessive quantifier */ - else *ketcode = OP_KETRMAX + repeat_type; - } - } - } +/************************************************* +* External entry point for add list to class * +*************************************************/ - /* 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. */ +/* 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. - else if (*previous == OP_FAIL) goto END_REPEAT; +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 - /* Else there's some kind of shambles */ +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ - else - { - *errorcodeptr = ERR10; - goto FAILED; - } +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; +} - /* 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; +/************************************************* +* Add characters not in a list to a class * +*************************************************/ - /* 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. */ +/* 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. - 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; +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 - /* CHAR opcodes are used for exacts whose count is 1. */ +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ - 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; +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; +} - /* 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 - } +/************************************************* +* Find details of duplicate group names * +*************************************************/ - /* 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. */ +/* 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. - len = (int)(code - tempcode); - if (len > 0) - { - unsigned int repcode = *tempcode; +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 +*/ - /* There is a table for possessifying opcodes, all of which are less - than OP_CALLOUT. A zero entry means there is no possessified version. - */ +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; - if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) - *tempcode = opcode_possessify[repcode]; +/* Find the first entry in the table */ - /* For opcode without a special possessified version, wrap the item in - ONCE brackets. */ +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; + } - 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); - } - } - } +/* This should not occur, because this function is called only when we know we +have duplicate names. Give an internal error. */ - /* 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. */ +if (i >= cb->names_found) + { + *errorcodeptr = ERR53; + cb->erroroffset = name - cb->start_pattern; + return FALSE; + } - END_REPEAT: - previous = NULL; - cb->req_varyopt |= reqvary; - break; +/* 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; - /* ===================================================================*/ - /* 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. */ +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; + } - case CHAR_LEFT_PARENTHESIS: - ptr++; +*countptr = count; +return TRUE; +} - /* 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 */ +/************************************************* +* Compile one branch * +*************************************************/ - READ_NAME(ctype_letter, ERR60, *errorcodeptr); +/* 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. - /* 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. */ +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 - if (*ptr == CHAR_COLON) - { - arg = ++ptr; +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 +*/ - 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 */ +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]; - for (i = 0; i < verbcount; i++) - { - if (namelen == verbs[i].len && - PRIV(strncmp_c8)(name, vn, namelen) == 0) - { - int setverb; +/* 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. */ - /* Check for open captures before ACCEPT and convert it to - ASSERT_ACCEPT if in an assertion. */ +#ifdef SUPPORT_UNICODE +BOOL utf = (options & PCRE2_UTF) != 0; +#else /* No UTF support */ +BOOL utf = FALSE; +#endif - if (verbs[i].op == OP_ACCEPT) - { - open_capitem *oc; - if (arglen != 0) - { - *errorcodeptr = ERR59; - goto FAILED; - } - cb->had_accept = TRUE; +/* 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. */ - /* In the first pass, just accumulate the length required; - otherwise hitting (*ACCEPT) inside many nested parentheses can - cause workspace overflow. */ +PCRE2_UCHAR *class_uchardata; +#ifdef SUPPORT_WIDE_CHARS +BOOL xclass; +PCRE2_UCHAR *class_uchardata_base; +#endif - 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; +/* Set up the default and non-default settings for greediness */ - /* Do not set firstcu after *ACCEPT */ - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - } +greedy_default = ((options & PCRE2_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; - /* Handle other cases with/without an argument */ +/* 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. - 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; - } +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. */ - else /* An argument is present */ - { - if (verbs[i].op_arg < 0) /* Argument is forbidden */ - { - *errorcodeptr = ERR59; - goto FAILED; - } - setverb = *code++ = verbs[i].op_arg; +firstcu = reqcu = zerofirstcu = zeroreqcu = 0; +firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; - /* 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. */ +/* 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. +*/ - 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; - } - } +req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; - *code++ = 0; - } +/* Switch on next META item until the end of the branch */ - switch (setverb) - { - case OP_THEN: - case OP_THEN_ARG: - cb->external_flags |= PCRE2_HASTHEN; - break; +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 templastcapture; + 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]; - case OP_PRUNE: - case OP_PRUNE_ARG: - case OP_SKIP: - case OP_SKIP_ARG: - cb->had_pruneorskip = TRUE; - break; - } + /* Get next META item in the pattern and its potential argument. */ - break; /* Found verb, exit loop */ - } + meta = META_CODE(*pptr); + meta_arg = META_DATA(*pptr); - vn += verbs[i].len + 1; - } + /* 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 (i < verbcount) continue; /* Successfully handled a verb */ - *errorcodeptr = ERR60; /* Verb not recognized */ - goto FAILED; + 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; } - /* Initialization for "real" parentheses */ + /* 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. */ - newoptions = options; - skipunits = 0; - bravalue = OP_CBRA; - reset_bracount = FALSE; + if (code < last_code) code = last_code; - /* 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 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 (*ptr == CHAR_QUESTION_MARK) + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) { - 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)) + if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code)) { - /* ------------------------------------------------------------ */ - 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; + *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; + } - /* ------------------------------------------------------------ */ - case CHAR_LEFT_PARENTHESIS: - bravalue = OP_COND; /* Conditional group */ - tempptr = ptr; + /* Remember where this code item starts so we can catch the "backwards" + case above next time round. */ - /* 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. + last_code = code; + } - There are ways of testing a named group: (?(name)) is used by Python; - Perl 5.10 onwards uses (?() or (?('name')). + /* 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. */ - 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. + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) + { + previous = code; + if (matched_char) okreturn = 1; + } - 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! */ + previous_matched_char = matched_char; + matched_char = FALSE; + note_group_empty = FALSE; + skipunits = 0; /* Default value for most subgroups */ - 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; - } - } - } - } - } + switch(meta) + { + /* ===================================================================*/ + /* The branch terminates at pattern end or | or ) */ - /* tempptr should now be pointing to the opening parenthesis of the - assertion condition. */ + case META_END: + case META_ALT: + case META_KET: + *firstcuptr = firstcu; + *firstcuflagsptr = firstcuflags; + *reqcuptr = reqcu; + *reqcuflagsptr = reqcuflags; + *codeptr = code; + *pptrptr = pptr; + return okreturn; - 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; - } + /* ===================================================================*/ + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ - /* 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. */ + case META_CIRCUMFLEX: + if ((options & PCRE2_MULTILINE) != 0) + { + if (firstcuflags == REQ_UNSET) + zerofirstcuflags = firstcuflags = REQ_NONE; + *code++ = OP_CIRCM; + } + else *code++ = OP_CIRC; + break; - 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 */ + case META_DOLLAR: + *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; + break; - /* Point at character after (?( */ + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqcu doesn't change either. */ - ptr++; + 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; - /* 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; + /* ===================================================================*/ + /* 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; - 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. */ + /* ===================================================================*/ + /* 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 (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) - { - *errorcodeptr = ERR79; - goto FAILED; - } + 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. */ - 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; - } + case META_CLASS_NOT: + case META_CLASS: + matched_char = TRUE; + negate_class = meta == META_CLASS_NOT; - if (*ptr != CHAR_RIGHT_PARENTHESIS || minor > 99) - { - *errorcodeptr = ERR79; - goto FAILED; - } + /* 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. */ - 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; + /* 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. */ - ptr++; - skipunits = 1; - break; /* End of condition processing */ - } + if (pptr[1] < META_END && pptr[2] == META_CLASS_END) + { +#ifdef SUPPORT_UNICODE + uint32_t d; +#endif + uint32_t c = pptr[1]; - /* Check for a test for recursion in a named group. */ + 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; + } - if (*ptr == CHAR_R && ptr[1] == CHAR_AMPERSAND) - { - terminator = -1; - ptr += 2; - code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */ - } + /* Handle a negative one-character class */ - /* 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). */ + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; - 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; - } + /* 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. */ - /* Handle a number */ +#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 */ - 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++; - } - } + *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 */ - /* 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; - } + /* Handle character classes that contain more than just one literal + character. */ - /* Check the terminator */ + /* 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. + */ - if ((terminator > 0 && *ptr++ != (PCRE2_UCHAR)terminator) || - *ptr++ != CHAR_RIGHT_PARENTHESIS) - { - ptr--; /* Error offset */ - *errorcodeptr = ERR26; /* Malformed number or name */ - goto FAILED; - } + should_flip_negation = match_all_or_no_wide_chars = FALSE; - /* Do no further checking in the pre-compile phase. */ + /* Extended class (xclass) will be used when characters > 255 + might match. */ - if (lengthptr != NULL) break; +#ifdef SUPPORT_WIDE_CHARS + xclass = FALSE; + class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ + class_uchardata_base = class_uchardata; /* Save the start */ +#endif - /* 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. */ + /* 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. */ - 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; - } + class_has_8bitchar = 0; +#ifdef SUPPORT_WIDE_CHARS + xclass_has_prop = FALSE; +#endif - /* Otherwise look for the name. */ + /* 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. */ - 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; - } + memset(classbits, 0, 32 * sizeof(uint8_t)); - /* 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. */ + /* Process items until META_CLASS_END is reached. */ - if (i < cb->names_found) - { - int offset = i; /* Offset of first name found */ + while ((meta = *(++pptr)) != META_CLASS_END) + { + /* Handle POSIX classes such as [:alpha:] etc. */ - 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 (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]; - 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); - } - } + should_flip_negation = local_negate; /* Note negative special */ - /* 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. */ + /* 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. */ - else if (terminator != CHAR_NULL) - { - *errorcodeptr = ERR15; - goto FAILED; - } + if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; - /* Check for (?(R) for recursion. Allow digits after R to specify a - specific group number. */ + /* 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. */ - else if (*name == CHAR_R) +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UCP) != 0) switch(posix_class) { - 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); - } + 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; - /* 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. */ + /* 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. */ - else if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) - { - code[1+LINK_SIZE] = OP_DEFINE; - skipunits = 1; + default: +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (utf) +#endif + match_all_or_no_wide_chars |= local_negate; + break; } +#endif /* SUPPORT_UNICODE */ - /* Reference to an unidentified subpattern. */ + /* 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. */ - else - { - *errorcodeptr = ERR15; - goto FAILED; - } - break; + posix_class *= 3; + /* Copy in the first table (always present) */ - /* ------------------------------------------------------------ */ - case CHAR_EQUALS_SIGN: /* Positive lookahead */ - bravalue = OP_ASSERT; - cb->assert_depth += 1; - ptr++; - break; + memcpy(pbits, cbits + posix_class_maps[posix_class], + 32 * sizeof(uint8_t)); - /* 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. */ + /* If there is a second table, add or remove it as required. */ - /* ------------------------------------------------------------ */ - 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))) + taboffset = posix_class_maps[posix_class + 1]; + tabopt = posix_class_maps[posix_class + 2]; + + if (taboffset >= 0) { - *code++ = OP_FAIL; - previous = NULL; - continue; + 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]; } - 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; + /* Now see if we need to remove any special characters. An option + value of 1 removes vertical space and 2 removes underscore. */ - case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */ - bravalue = OP_ASSERTBACK_NOT; - cb->assert_depth += 1; - ptr += 2; - break; + if (tabopt < 0) tabopt = -tabopt; + if (tabopt == 1) pbits[1] &= ~0x3c; + else if (tabopt == 2) pbits[11] &= 0x7f; - /* 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. */ + /* Add the POSIX table or its complement into the main table that is + being built and we are done. */ - default: - while (*(++ptr) != CHAR_GREATER_THAN_SIGN); - ptr++; - goto NUMBERED_GROUP; - } - break; + 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. */ - /* ------------------------------------------------------------ */ - case CHAR_GREATER_THAN_SIGN: /* One-time brackets */ - bravalue = OP_ONCE; - ptr++; - break; + 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). */ - /* ------------------------------------------------------------ */ - case CHAR_C: /* Callout */ - previous_callout = code; /* Save for later completion */ - after_manual_callout = 1; /* Skip one item before completing */ - ptr++; /* Character after (?C */ + if (meta == META_BIGVALUE) + { + meta = *(++pptr); + goto CLASS_LITERAL; + } - /* 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. */ + /* Any other non-literal must be an escape */ - if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) + if (meta >= META_END) + { + if (META_CODE(meta) != META_ESCAPE) { - 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.) */ +#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); - 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); + /* Every class contains at least one < 256 character. */ - /* 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. */ + class_has_8bitchar++; - (*lengthptr) += (ptr - start) + 2 + (1 + 4*LINK_SIZE); - } + switch(escape) + { + case ESC_d: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit]; + break; - /* 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. */ + case ESC_D: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_digit]; + break; - 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; - } + case ESC_w: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word]; + break; - /* Advance to what should be the closing parenthesis, which is - checked below. */ + case ESC_W: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_word]; + break; - ptr++; - } + /* 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. */ - /* Handle a callout with an optional numerical argument, which must be - less than or equal to 255. A missing argument gives 0. */ + case ESC_s: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space]; + break; - 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]; - } + case ESC_S: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_space]; + break; - /* Both formats must have a closing parenthesis */ + /* 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). */ - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR39; - goto FAILED; - } + case ESC_h: + (void)add_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR); + break; - /* Callouts cannot be quantified. */ + case ESC_H: + (void)add_not_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list)); + break; - previous = NULL; - continue; + 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; - /* ------------------------------------------------------------ */ - 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; + 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; +#ifdef SUPPORT_WIDE_CHARS + xclass_has_prop = TRUE; +#endif + class_has_8bitchar--; /* Undo! */ + } + break; } - /* Fall through to handle (?P< as (?< is handled */ + goto CONTINUE_CLASS; + } /* End handling \d-type escapes */ - /* ------------------------------------------------------------ */ - case CHAR_APOSTROPHE: /* Define a name - note fall through above */ + /* 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. */ - /* 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. */ + else + { + uint32_t c, d; - 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 */ + CLASS_LITERAL: + c = d = meta; + /* Remember if \r or \n were explicitly used */ - /* ------------------------------------------------------------ */ - case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */ - terminator = CHAR_RIGHT_PARENTHESIS; - is_recurse = TRUE; - /* Fall through */ + if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; - /* 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. */ + /* Process a character range */ - NAMED_REF_OR_RECURSE: - name = ++ptr; - if (IS_DIGIT(*ptr)) + if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED) { - *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. */ +#ifdef EBCDIC + BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL); +#endif + pptr += 2; + d = *pptr; + if (d == META_BIGVALUE) d = *(++pptr); - if (lengthptr != NULL) - { - if (namelen == 0) - { - *errorcodeptr = ERR62; - goto FAILED; - } - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR42; - goto FAILED; - } - } + /* Remember an explicit \r or \n, and add the range to the class. */ - /* 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 (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; - recno = 0; - is_dupname = FALSE; - ng = cb->named_groups; + /* 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]. */ - for (i = 0; i < cb->names_found; i++, ng++) - { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, namelen) == 0) +#ifdef EBCDIC + if (range_is_literal && + (cb->ctypes[c] & ctype_letter) != 0 && + (cb->ctypes[d] & ctype_letter) != 0 && + (d <= CHAR_z) == (d <= CHAR_z)) { - 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. */ + uint32_t uc = (d <= CHAR_z)? 0 : 64; + uint32_t C = d - uc; + uint32_t D = d - uc; - 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. */ + 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; + } - cb->backref_map |= (recno < 32)? (1u << recno) : 1; - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; + 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; + } - for (oc = cb->open_caps; oc != NULL; oc = oc->next) + if (C <= D) { - if (oc->number == recno) - { - oc->flag = TRUE; - break; - } + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + D + uc); } } - } - - /* 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. */ + else +#endif + /* Not an EBCDIC special range */ - if (!is_dupname) goto HANDLE_REFERENCE; + 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 */ - /* 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; + /* Handle a single character. */ - 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; - } + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, meta, meta); + } - if (count == 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - } + /* Continue to the next item in the class. */ - 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 */ + 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 work space (which is on the stack). */ - /* ------------------------------------------------------------ */ - case CHAR_R: /* Recursion, same as (?0) */ - recno = 0; - if (*(++ptr) != CHAR_RIGHT_PARENTHESIS) + if (class_uchardata > class_uchardata_base) + { + xclass = TRUE; + if (lengthptr != NULL) { - *errorcodeptr = ERR29; - goto FAILED; + *lengthptr += class_uchardata - class_uchardata_base; + class_uchardata = class_uchardata_base; } - goto HANDLE_RECURSION; + } +#endif + continue; /* Needed to avoid error when not supporting wide chars */ + } /* End of main class-processing loop */ - /* ------------------------------------------------------------ */ - 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: + /* 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 */ { - terminator = CHAR_RIGHT_PARENTHESIS; + 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; - /* 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. */ + /* 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. */ - HANDLE_NUMERICAL_RECURSION: + 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 (i = 0; i < 32; i++) classbits[i] = ~classbits[i]; + memcpy(code, classbits, 32); + code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); + } + else code = class_uchardata; - 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++; - } + /* Now fill in the complete length of the item */ - 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; - } + PUT(previous, 1, (int)(code - previous)); + break; /* End of class handling */ + } +#endif /* SUPPORT_WIDE_CHARS */ - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR29; - goto FAILED; - } + /* 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. */ - 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; - } + *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 */ - 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; - } + /* ===================================================================*/ + /* Deal with (*VERB)s. */ - /* Can't determine a first byte now */ + /* Check for open captures before ACCEPT and convert it to ASSERT_ACCEPT if + 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. */ - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - continue; + case META_ACCEPT: + cb->had_accept = TRUE; + 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); + } + } + *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; - /* ------------------------------------------------------------ */ - default: /* Other characters: check option setting */ - OTHER_CHAR_AFTER_QUERY: - set = unset = 0; - optset = &set; + case META_THEN: + cb->external_flags |= PCRE2_HASTHEN; + *code++ = OP_THEN; + break; - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; 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 CHAR_J: /* Record that it changed in the external options */ - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; + case META_THEN_ARG: + cb->external_flags |= PCRE2_HASTHEN; + goto VERB_ARG; - 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; + case META_PRUNE_ARG: + case META_SKIP_ARG: + cb->had_pruneorskip = TRUE; + /* Fall through */ + case META_MARK: + 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. */ - default: *errorcodeptr = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; + 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 */ } + } - /* Set up the changed option bits, but don't change anything yet. */ - - newoptions = (options | set) & (~unset); + /* 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 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 (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 (*ptr == CHAR_RIGHT_PARENTHESIS) + if (meta != META_COND_RNUMBER || groupnumber > cb->bracount) { - *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 */ + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; } - /* 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. */ + /* (?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). */ - bravalue = OP_BRA; - ptr++; - } /* End of switch for character following (? */ - } /* End of (? handling */ + 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; + } - /* Opening parenthesis not followed by '*' or '?'. If PCRE2_NO_AUTO_CAPTURE - is set, all unadorned brackets become non-capturing and behave like (?:...) - brackets. */ + /* 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. */ - else if ((options & PCRE2_NO_AUTO_CAPTURE) != 0) - { - bravalue = OP_BRA; - } + code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; - /* Else we have a capturing group. */ + /* 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. */ - else - { - NUMBERED_GROUP: - cb->bracount += 1; - PUT2(code, 1+LINK_SIZE, cb->bracount); - skipunits = IMM2_SIZE; - } + 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; - /* Process nested bracketed regex. First check for parentheses nested too - deeply. */ + /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and + insert appropriate data values. */ - if ((cb->parens_depth += 1) > (int)(cb->cx->parens_nest_limit)) + 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 = ERR19; - goto FAILED; + *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. */ - /* 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. */ + case META_COND_ASSERT: + bravalue = OP_COND; + goto GROUP_PROCESS_NOTE_EMPTY; - if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT && - cb->iscondassert) + + /* ===================================================================*/ + /* 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)) { - previous = NULL; - cb->iscondassert = FALSE; + *code++ = OP_FAIL; + pptr++; } else { - previous = code; + 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 bracket */ - tempbracount = cb->bracount; /* Save value before bracket */ + tempreqvary = cb->req_varyopt; /* Save value before group */ + templastcapture = cb->lastcapture; /* Save value before group */ length_prevgroup = 0; /* Initialize for pre-compile phase */ - if (!compile_regex( - newoptions, /* The complete new option state */ + if ((group_return = + compile_regex( + options, /* The option state */ &tempcode, /* Where to put code (updated) */ - &ptr, /* Input pointer (updated) */ + &pptr, /* 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 */ @@ -7051,23 +5775,32 @@ for (;; ptr++) cb, /* Compile data block */ (lengthptr == NULL)? NULL : /* Actual compile phase */ &length_prevgroup /* Pre-compile phase */ - )) - goto FAILED; + )) == 0) + return 0; /* Error */ cb->parens_depth -= 1; - /* If this was an atomic group and there are no capturing groups within it, + /* 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 that 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) + if (bravalue == OP_ONCE && cb->lastcapture <= templastcapture) *code = OP_ONCE_NC; + /* 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 pattern pointer (ptr) is on the bracket. + 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 @@ -7093,38 +5826,32 @@ for (;; ptr++) { if (condcount > 1) { + cb->erroroffset = offset; *errorcodeptr = ERR54; - goto FAILED; + return 0; } code[LINK_SIZE+1] = OP_FALSE; - bravalue = OP_DEFINE; /* Just a flag to suppress char handling below */ + 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. */ + 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; - goto FAILED; + return 0; } if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; + else if (group_return > 0) matched_char = TRUE; } } - /* 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 @@ -7135,7 +5862,7 @@ for (;; ptr++) if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) { *errorcodeptr = ERR20; - goto FAILED; + return 0; } *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; code++; /* This already contains bravalue */ @@ -7154,12 +5881,12 @@ for (;; ptr++) if (bravalue == OP_DEFINE) break; - /* Handle updating of the required and first characters for other types of + /* 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. */ + zerofirstcu outside the main loop so that they can be accessed for the back + off. */ zeroreqcu = reqcu; zeroreqcuflags = reqcuflags; @@ -7167,7 +5894,7 @@ for (;; ptr++) zerofirstcuflags = firstcuflags; groupsetfirstcu = FALSE; - if (bravalue >= OP_ONCE) + 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 @@ -7197,8 +5924,8 @@ for (;; ptr++) 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 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) { @@ -7207,1088 +5934,2808 @@ for (;; ptr++) } } - /* 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) + /* 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 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. + break; /* End of nested group handling */ - 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; + /* ===================================================================*/ + /* Handle named backreferences and recursions. */ - if (escape == 0) /* The escape coded a single character */ - c = ec; - else + case META_BACKREF_BYNAME: + case META_RECURSE_BYNAME: { - /* 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. */ + int count, index; + PCRE2_SPTR name; + BOOL is_dupname = FALSE; + named_group *ng = cb->named_groups; + uint32_t length = *(++pptr); - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; - /* \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). */ + /* 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 (escape == ESC_g) + groupnumber = 0; + for (i = 0; i < cb->names_found; i++, ng++) { - PCRE2_SPTR p; - uint32_t cf; + if (length == ng->length && + PRIV(strncmp)(name, ng->name, length) == 0) + { + is_dupname = ng->isdup; + groupnumber = ng->number; - terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; + /* 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. */ - /* 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. */ + if (meta == META_RECURSE_BYNAME) + { + meta_arg = groupnumber; + goto HANDLE_NUMERICAL_RECURSION; + } - skipunits = 0; - reset_bracount = FALSE; + /* 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. + */ - /* If it's not a signed or unsigned number, treat it as a name. */ + cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; + if (groupnumber > cb->top_backref) + cb->top_backref = groupnumber; - cf = ptr[1]; - if (cf != CHAR_PLUS && cf != CHAR_MINUS && !IS_DIGIT(cf)) - { - is_recurse = TRUE; - goto NAMED_REF_OR_RECURSE; + for (oc = cb->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == groupnumber) + { + oc->flag = TRUE; + break; + } + } } + } - /* Signed or unsigned number (cf = ptr[1]) is known to be plus or minus - or a digit. */ + /* If the name was not found we have a bad reference. */ - p = ptr + 2; - while (IS_DIGIT(*p)) p++; - if (*p != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR57; - goto FAILED; - } - ptr++; - goto HANDLE_NUMERICAL_RECURSION; + if (groupnumber == 0) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; } - /* \k or \k'name' is a back reference by name (Perl syntax). - We also support \k{name} (.NET syntax). */ + /* If a back reference name is not duplicated, we can handle it as + a numerical reference. */ - if (escape == ESC_k) + if (!is_dupname) { - 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; + meta_arg = groupnumber; + goto HANDLE_SINGLE_REFERENCE; } - /* Back references are handled specially; must disable firstcu if - not set to cope with cases like (?=(\w+))\1: which would otherwise set - ':' later. */ + /* 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. */ - if (escape < 0) - { - open_capitem *oc; - recno = -escape; + 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; - /* Come here from named backref handling when the reference is to a - single group (i.e. not to a duplicated name). */ + 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_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. */ + /* ===================================================================*/ + /* 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; -#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. */ + /* ===================================================================*/ + /* 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. */ -#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 + case META_CALLOUT_STRING: + if (lengthptr != NULL) + { + *lengthptr += pptr[3] + (1 + 4*LINK_SIZE); + pptr += 3; + SKIPOFFSET(pptr); + } - /* 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. */ + /* 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 + 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 (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. */ - + if (*pp == delimiter && pp[1] == delimiter) { - 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 + *callout_string++ = delimiter; + pp += 2; + length--; } + else *callout_string++ = *pp++; } - continue; - } + *callout_string++ = CHAR_NULL; - /* 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. */ + /* Set the length of the entire item, the advance to its end. */ - mclength = PUTCHAR(c, mcbuffer); - goto ONE_CHAR; + PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code)); + code = callout_string; + } + break; /* ===================================================================*/ - /* 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. */ + /* 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; - default: - NORMAL_CHAR: - mclength = 1; - mcbuffer[0] = c; + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + repeat_min = 0; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - ACROSSCHAR(TRUE, ptr[1], mcbuffer[mclength++] = *(++ptr)); -#endif + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + repeat_min = 1; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; - /* 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. */ + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + repeat_min = 0; + repeat_max = 1; - ONE_CHAR: - previous = code; + REPEAT: + if (previous_matched_char && repeat_min > 0) 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. */ + /* Remember whether this is a variable length repeat, and default to + single-char opcodes. */ -#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 + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + op_type = 0; - /* Caseful matches, or not one of the multicase characters. */ + /* If the repeat is {1} we can ignore it. */ - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; - for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; + if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; - /* Remember if \r or \n were seen */ + /* Adjust first and required code units for a zero repeat. */ - if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) - cb->external_flags |= PCRE2_HASCRORLF; + if (repeat_min == 0) + { + firstcu = zerofirstcu; + firstcuflags = zerofirstcuflags; + reqcu = zeroreqcu; + reqcuflags = zeroreqcuflags; + } - /* 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. */ + /* Note the greediness and possessiveness. */ - if (firstcuflags == REQ_UNSET) + switch (meta) { - 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. */ + 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; - if (mclength == 1 || req_caseopt == 0) - { - firstcu = mcbuffer[0] | req_caseopt; - firstcu = mcbuffer[0]; - firstcuflags = req_caseopt; + 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; - if (mclength != 1) - { - reqcu = code[-1]; - reqcuflags = cb->req_varyopt; - } - } - else firstcuflags = reqcuflags = REQ_NONE; + default: + repeat_type = greedy_default; + possessive_quantifier = FALSE; + break; } - /* firstcu was previously set; we can set reqcu only if the length is - 1 or the matching is caseful. */ + /* 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. */ - else + tempcode = previous; + op_previous = *previous; + + /* 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 (op_previous == OP_RECURSE) { - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - if (mclength == 1 || req_caseopt == 0) - { - reqcu = code[-1]; - reqcuflags = req_caseopt | cb->req_varyopt; - } + memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); + op_previous = *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; + group_return = -1; /* Set "may match empty string" */ } - 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. */ + /* Now handle repetition for the different types of item. */ -FAILED: -*ptrptr = ptr; -return FALSE; -} + 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. */ -/************************************************* -* Compile regex: a sequence of alternatives * -*************************************************/ +#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 */ -/* 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. + /* 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 */ -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 + /* 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}. */ -Returns: TRUE on success -*/ +#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: -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 (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } -/* If set, call the external function that checks for stack availability. */ + 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 (cb->cx->stack_guard != NULL && - cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) - { - *errorcodeptr= ERR33; - return FALSE; - } + /* 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; + + /* 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_ONCE_NC: + case OP_BRA: + case OP_CBRA: + case OP_COND: + { + int len = (int)(code - previous); + PCRE2_UCHAR *bralink = NULL; + PCRE2_UCHAR *brazeroptr = NULL; -/* Miscellaneous initialization */ + /* 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. */ -bc.outer = bcptr; -bc.current_branch = code; + if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && + previous[GET(previous, 1)] != OP_ALT) + goto END_REPEAT; -firstcu = reqcu = 0; -firstcuflags = reqcuflags = REQ_UNSET; + /* 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. */ -/* 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. */ + if (op_previous < OP_ONCE) /* Assertion */ + { + if (repeat_min > 0) goto END_REPEAT; + if (repeat_max > 1) repeat_max = 1; + } -length = 2 + 2*LINK_SIZE + skipunits; + /* 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. */ -/* 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 (repeat_min == 0) + { + /* If the maximum is also zero, we used to just omit the group from + the output altogether, like this: -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 (repeat_max == 0) + ** { + ** code = previous; + ** goto END_REPEAT; + ** } -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; - } + 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. -/* Offset is set zero to mark that this bracket is still open */ + If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. */ -PUT(code, 1, 0); -code += 1 + LINK_SIZE + skipunits; + if (repeat_max <= 1 || repeat_max == REPEAT_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; + } -/* Loop for each alternative branch */ + /* 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. */ -orig_bracount = max_bracount = cb->bracount; + else + { + int linkoffset; + 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); + } -for (;;) - { - /* For a (?| group, reset the capturing bracket count so that each branch - uses the same numbers. */ + if (repeat_max != REPEAT_UNLIMITED) repeat_max--; + } - if (reset_bracount) cb->bracount = orig_bracount; + /* 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. */ - /* Set up dummy OP_REVERSE if lookbehind assertion */ + 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 (lookbehind) - { - *code++ = OP_REVERSE; - reverse_count = code; - PUTINC(code, 0, 0); - length += 1 + LINK_SIZE; - } + 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; + } - /* Now compile the branch; in the pre-compile phase its length gets added - into the length. */ + /* 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. */ - if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstcu, - &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, - cond_depth, cb, (lengthptr == NULL)? NULL : &length)) - { - *ptrptr = ptr; - return FALSE; - } + 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; + } + } + } - /* Keep the highest bracket count in case (?| was used and some branch - has fewer than the rest. */ + if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; + } - if (cb->bracount > max_bracount) max_bracount = cb->bracount; + /* 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. */ - /* In the real compile phase, there is some post-processing to be done. */ + 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) - { - /* If this is the first branch, the firstcu and reqcu values for the - branch become the values for the regex. */ + 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; + } - if (*last_branch != OP_ALT) - { - firstcu = branchfirstcu; - firstcuflags = branchfirstcuflags; - reqcu = branchreqcu; - reqcuflags = branchreqcuflags; - } + /* This is compiling for real */ - /* 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 for (i = repeat_max - 1; i >= 0; i--) + { + *code++ = OP_BRAZERO + repeat_type; - 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. */ + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ - if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) - { - if (firstcuflags >= 0) - { - if (reqcuflags < 0) + 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) { - reqcu = firstcu; - reqcuflags = firstcuflags; + 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); } } - 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 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. */ - if (firstcuflags < 0 && branchfirstcuflags >= 0 && - branchreqcuflags < 0) - { - branchreqcu = branchfirstcu; - branchreqcuflags = branchfirstcuflags; - } + else + { + PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; + PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); - /* Now ensure that the reqcus match */ + /* Convert possessive ONCE brackets to non-capturing */ - if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || - reqcu != branchreqcu) - reqcuflags = REQ_NONE; - else - { - reqcu = branchreqcu; - reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY */ - } - } + if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && + possessive_quantifier) *bracode = OP_BRA; - /* 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. */ + /* For non-possessive ONCE brackets, all we need to do is to + set the KET. */ - 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); - } - } - } + if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC) + *ketcode = OP_KETRMAX + repeat_type; - /* 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. */ + /* Handle non-ONCE brackets and possessive ONCEs (which have been + converted to non-capturing above). */ - 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); - } + 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. */ - /* Fill in the ket */ + 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; + } - *code = OP_KET; - PUT(code, 1, (int)(code - start_bracket)); - code += 1 + LINK_SIZE; + /* Handle possessive quantifiers. */ - /* 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 (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 (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; - } + 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); + } - /* Retain the highest bracket number, in case resetting was used. */ + /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ - cb->bracount = max_bracount; + else + { + *bracode += 1; /* Switch to xxxPOS opcodes */ + *ketcode = OP_KETRPOS; + } - /* Set values to pass back */ + /* If the minimum is zero, mark it as possessive, then unset the + possessive flag when the minimum is 0 or 1. */ - *codeptr = code; - *ptrptr = ptr; - *firstcuptr = firstcu; - *firstcuflagsptr = firstcuflags; - *reqcuptr = reqcu; - *reqcuflagsptr = reqcuflags; - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < length) + 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 = ERR20; - return FALSE; + *errorcodeptr = ERR10; + return 0; } - *lengthptr += length; - } - return TRUE; - } + else + { + int prop_type, prop_value; + PCRE2_UCHAR *oldcode; - /* 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.) + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + mclength = 0; /* Not a character */ - 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 (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; + } - 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; - } + /* 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. */ - /* Advance past the vertical bar */ + oldcode = code; /* Save where we were */ + code = previous; /* Usually overwrite previous item */ - ptr++; - } -/* Control never reaches here */ -} + /* 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 */ -/************************************************* -* Check for anchored pattern * -*************************************************/ + repeat_type += op_type; -/* 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. + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ -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". + 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 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 .... + /* 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. */ -.... 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. + 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); + } + } -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. + /* 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. */ -... A second exception is when the .* appears inside an atomic group, because -this prevents the number of characters it matches from being adjusted. + else + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); -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 + /* 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 + { + 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) 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] | 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 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 work space 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; + 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) + { + 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 */ + + else if (op == OP_COND) + { + if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Atomic groups */ + + else if (op == OP_ONCE || op == OP_ONCE_NC) + { + 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 || op == OP_ONCE_NC) + { + 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 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; +} + + + +/************************************************* +* 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_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 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) +{ +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: + 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 + 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) + { + 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). + +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 += 1;; 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_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; + } -Returns: TRUE or FALSE -*/ + /* The extra data item length for each meta is in a table. */ -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; + meta = (meta >> 16) & 0x7fff; + if (meta >= sizeof(meta_extra_lengths)) return NULL; + pptr += meta_extra_lengths[meta]; + } +/* Control never reaches here */ +return pptr; +} - /* 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 */ +/************************************************* +* Find length of a parsed group * +*************************************************/ - 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; - } +/* 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. Caching is used to improve processing speed when +the same capturing group occurs many times. - /* Positive forward assertions and conditions */ +Arguments: + pptrptr pointer to pointer in the parsed pattern + 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 - else if (op == OP_ASSERT || op == OP_COND) - { - if (!is_anchored(scode, bracket_map, cb, atomcount)) return FALSE; - } +Returns: the group length or a negative number +*/ - /* Atomic groups */ +static int +get_grouplength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + int group, parsed_recurse_check *recurses, compile_block *cb) +{ +int branchlength; +int grouplength = -1; - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_anchored(scode, bracket_map, cb, atomcount + 1)) - return FALSE; - } +/* The cache can be used only if there is no possibility of there being two +groups with the same number. */ - /* .* 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. */ +if (group > 0) + { + uint32_t groupinfo = cb->groupinfo[group]; + if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0) + { + if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; + if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) + return groupinfo & GI_FIXED_LENGTH_MASK; + } + } - 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; - } +/* Scan the group */ - /* Check for explicit anchoring */ +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 */ + } - else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; +if (group > 0) + cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength); +return grouplength; - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; +ISNOTFIXED: +if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH; +return -1; } /************************************************* -* Check for starting with ^ or .* * +* Find length of a parsed branch * *************************************************/ -/* 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. +/* 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: - 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 + 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 or FALSE +Returns: the length, or a negative value on error */ -static BOOL -is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, - int atomcount) +static int +get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) { -do { - PCRE2_SPTR scode = first_significant_code( - code + PRIV(OP_lengths)[*code], FALSE); - register int op = *scode; +int branchlength = 0; +int grouplength; +uint32_t lastitemlength = 0; +uint32_t *pptr = *pptrptr; +PCRE2_SIZE offset; +parsed_recurse_check this_recurse; - /* 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. */ +/* 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 (op == OP_COND) - { - scode += 1 + LINK_SIZE; +if ((*lcptr)++ > 2000) + { + *errcodeptr = ERR35; /* Lookbehind is too complicated */ + return -1; + } - if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; - else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); +/* Scan the branch, accumulating the length. */ - 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; +for (;; pptr++) + { + parsed_recurse_check *r; + uint32_t *gptr, *gptrend; + uint32_t escape; + uint32_t group = 0; + uint32_t itemlength = 0; - 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; - } + if (*pptr < META_END) + { + itemlength = 1; + } - /* Non-capturing brackets */ + 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_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += pptr[1] + 1; + break; - if (op == OP_BRA || op == OP_BRAPOS || - op == OP_SBRA || op == OP_SBRAPOS) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } + case META_CIRCUMFLEX: + case META_COMMIT: + case META_DOLLAR: + case META_PRUNE: + case META_SKIP: + case META_THEN: + break; - /* Capturing brackets */ + case META_OPTIONS: + pptr += 1; + break; - 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; - } + case META_BIGVALUE: + itemlength = 1; + pptr += 1; + break; - /* Positive forward assertions */ + case META_CLASS: + case META_CLASS_NOT: + itemlength = 1; + pptr = parsed_skip(pptr, PSKIP_CLASS); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + break; - else if (op == OP_ASSERT) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } + case META_CLASS_EMPTY_NOT: + case META_DOT: + itemlength = 1; + break; - /* Atomic brackets */ + case META_CALLOUT_NUMBER: + pptr += 3; + break; - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_startline(scode, bracket_map, cb, atomcount + 1)) return FALSE; - } + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; - /* .* 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. */ + /* 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. */ - 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; - } + 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; - /* 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. */ + /* Lookaheads can be ignored. */ - else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + pptr = parsed_skip(pptr, PSKIP_KET); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + break; - /* Move on to the next alternative */ + /* Lookbehinds can be ignored, but must themselves be checked. */ - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; -} + 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; -/************************************************* -* Check for asserted fixed first code unit * -*************************************************/ + 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); -/* 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. + 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; + } + } -Arguments: - code points to start of compiled pattern - flags points to the first code unit flags - inassert TRUE if in an assertion + if (group == 0) + { + *errcodeptr = ERR15; /* Non-existent subpattern */ + cb->erroroffset = offset; + return -1; + } -Returns: the fixed first code unit, or 0 with REQ_NONE in flags -*/ + /* 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. */ -static uint32_t -find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, BOOL inassert) -{ -register uint32_t c = 0; -int cflags = REQ_NONE; + 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 */ -*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; + /* 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. */ - switch(op) - { - default: - return 0; + 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; + } - 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; + /* 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; + } + + gptrend = parsed_skip(gptr, 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; + gptr++; + grouplength = get_grouplength(&gptr, 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 OP_EXACT: - scode += IMM2_SIZE; - /* Fall through */ + case META_COND_VERSION: + pptr += 4; + goto CHECK_GROUP; - 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 META_CAPTURE: + group = META_DATA(*pptr); + /* Fall through */ - case OP_EXACTI: - scode += IMM2_SIZE; - /* Fall through */ + case META_ATOMIC: + case META_NOCAPTURE: + pptr++; + CHECK_GROUP: + grouplength = get_grouplength(&pptr, errcodeptr, lcptr, group, recurses, cb); + if (grouplength < 0) return -1; + itemlength = grouplength; + break; - 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; - } + /* Exact repetition is OK; variable repetition is not. A repetition of zero + must subtract the length that has already been added. */ - code += GET(code, 1); - } -while (*code == OP_ALT); + 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 */ -*flags = cflags; -return c; + /* 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; } /************************************************* -* Add an entry to the name/number table * +* Set lengths in a lookbehind * *************************************************/ -/* 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. +/* 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: - cb the compile data block - name the name to add - length the length of the name - groupno the group number + 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: nothing +Returns: TRUE if all is well + FALSE otherwise, with error code and offset set */ -static void -add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, - unsigned int groupno) +static BOOL +set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) { -int i; -PCRE2_UCHAR *slot = cb->name_table; +PCRE2_SIZE offset; +int branchlength; +uint32_t *bptr = *pptrptr; -for (i = 0; i < cb->names_found; i++) +READPLUSOFFSET(offset, bptr); /* Offset for error messages */ +*pptrptr += SIZEOFFSET; + +do { - int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); - if (crc == 0 && slot[IMM2_SIZE+length] != 0) - crc = -1; /* Current name is a substring */ + *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); - /* 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. */ +return TRUE; +} - if (crc < 0) + + +/************************************************* +* 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)) { - memmove(slot + cb->name_entry_size, slot, - CU2BYTES((cb->names_found - i) * cb->name_entry_size)); + default: + return ERR70; /* Unrecognized meta code */ + + case META_ESCAPE: + if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p) + pptr += 1; break; - } - /* Continue the loop for a later or duplicate name */ + 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; - slot += cb->name_entry_size; - } + case META_RECURSE: + pptr += SIZEOFFSET; + break; -PUT2(slot, 0, groupno); -memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); -cb->names_found++; + 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; -/* 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. */ + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; -memset(slot + IMM2_SIZE + length, 0, - CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); + 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_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; } @@ -8316,43 +8763,50 @@ 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 */ +BOOL utf; /* Set TRUE for UTF mode */ +BOOL has_lookbehind; /* 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_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 */ +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. */ -PCRE2_UCHAR *copied_pattern = NULL; -PCRE2_UCHAR stack_copied_pattern[COPIED_PATTERN_SIZE]; +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 group scan, and 32-bit -aligned for the group information cache. */ +It needs to be 16-bit aligned for the preliminary parsing scan. */ -uint32_t c32workspace[C32_WORK_SIZE]; -PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c32workspace; +uint32_t c16workspace[C16_WORK_SIZE]; +PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace; /* -------------- Check arguments and set up the pattern ----------------- */ @@ -8385,43 +8839,21 @@ 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. */ +PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */ -if (patlen == PCRE2_ZERO_TERMINATED) - { +if ((zero_terminated = (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) { - 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; + *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); @@ -8432,16 +8864,16 @@ cb.cbits = tables + cbits_offset; /* tables */ cb.ctypes = tables + ctypes_offset; cb.assert_depth = 0; -cb.bracount = cb.final_bracount = 0; +cb.bracount = 0; cb.cx = ccontext; cb.dupnames = FALSE; cb.end_pattern = pattern + patlen; -cb.nestptr[0] = cb.nestptr[1] = NULL; +cb.erroroffset = 0; cb.external_flags = 0; cb.external_options = options; -cb.groupinfo = c32workspace; +cb.groupinfo = stack_groupinfo; cb.had_recurse = FALSE; -cb.iscondassert = FALSE; +cb.lastcapture = 0; cb.max_lookbehind = 0; cb.name_entry_size = 0; cb.name_table = NULL; @@ -8450,6 +8882,7 @@ 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; @@ -8463,23 +8896,43 @@ 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 --------------- */ /* Check for global one-time option settings at the start of the pattern, and -remember the offset to the actual regex. */ +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; -while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && +while (patlen - skipatstart >= 2 && + 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) + if (patlen - skipatstart - 2 >= p->length && + PRIV(strncmp_c8)(ptr+skipatstart+2, (char *)(p->name), p->length) == 0) { uint32_t c, pp; @@ -8512,7 +8965,7 @@ while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && { errorcode = ERR60; ptr += pp; - goto HAD_ERROR; + goto HAD_EARLY_ERROR; } while (IS_DIGIT(ptr[pp])) { @@ -8523,7 +8976,7 @@ while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && { errorcode = ERR60; ptr += pp; - goto HAD_ERROR; + goto HAD_EARLY_ERROR; } if (p->type == PSO_LIMM) limit_match = c; else limit_recursion = c; @@ -8546,7 +8999,7 @@ ptr += skipatstart; if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) { errorcode = ERR32; - goto HAD_ERROR; + goto HAD_EARLY_ERROR; } #endif @@ -8559,11 +9012,11 @@ if (utf) if ((options & PCRE2_NEVER_UTF) != 0) { errorcode = ERR74; - goto HAD_ERROR; + goto HAD_EARLY_ERROR; } if ((options & PCRE2_NO_UTF_CHECK) == 0 && (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) - goto HAD_UTF_ERROR; + goto HAD_ERROR; /* Offset was set by valid_utf() */ } /* Check UCP lockout. */ @@ -8572,7 +9025,7 @@ if ((cb.external_options & (PCRE2_UCP|PCRE2_NEVER_UCP)) == (PCRE2_UCP|PCRE2_NEVER_UCP)) { errorcode = ERR75; - goto HAD_ERROR; + goto HAD_EARLY_ERROR; } /* Process the BSR setting. */ @@ -8611,23 +9064,102 @@ switch(newline) default: errorcode = ERR56; - goto HAD_ERROR; + 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). 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 ((options & PCRE2_AUTO_CALLOUT) != 0) + parsed_size_needed = (parsed_size_needed + 1) * 5; -/* 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. */ +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; -errorcode = scan_for_captures(&ptr, cb.external_options, &cb); -if (errorcode != 0) goto HAD_ERROR; +/* Do the parsing scan. */ -/* For obscure debugging this code can be enabled. */ +errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb); +if (errorcode != 0) goto HAD_CB_ERROR; -#if 0 +/* 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 { - int i; named_group *ng = cb.named_groups; - fprintf(stderr, "+++Captures: %d\n", cb.final_bracount); + 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); @@ -8635,12 +9167,6 @@ if (errorcode != 0) goto HAD_ERROR; } #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 @@ -8649,24 +9175,26 @@ 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. */ +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, &ptr, &errorcode, FALSE, - FALSE, 0, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, - &cb, &length); +(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 (errorcode != 0) goto HAD_ERROR; if (length > MAX_PATTERN_SIZE) { errorcode = ERR20; - goto HAD_ERROR; + goto HAD_CB_ERROR; } /* Compute the size of, and then get and initialize, the data block for storing @@ -8681,7 +9209,7 @@ re = (pcre2_real_code *) if (re == NULL) { errorcode = ERR21; - goto HAD_ERROR; + goto HAD_CB_ERROR; } re->memctl = ccontext->memctl; @@ -8712,44 +9240,19 @@ 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. */ +from the pre-compile phase, as is the name_entry_size field. */ cb.parens_depth = 0; cb.assert_depth = 0; -cb.bracount = 0; -cb.max_lookbehind = 0; +cb.lastcapture = 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 @@ -8757,23 +9260,21 @@ 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); + 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. */ -ptr = pattern + skipatstart; +pptr = cb.parsed_pattern; 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); - +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; @@ -8809,7 +9310,7 @@ if (errorcode == 0 && cb.had_recurse) { PCRE2_UCHAR *rcode; PCRE2_SPTR rgroup; - int ccount = 0; + unsigned int ccount = 0; int start = RSCAN_CACHE_SIZE; recurse_cache rc[RSCAN_CACHE_SIZE]; @@ -8817,16 +9318,16 @@ if (errorcode == 0 && cb.had_recurse) rcode != NULL; rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf)) { - int i, p, recno; + int p, groupnumber; - recno = (int)GET(rcode, 1); - if (recno == 0) rgroup = codestart; else + 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 (recno == rc[p].recno) + if (groupnumber == rc[p].groupnumber) { rgroup = rc[p].group; break; @@ -8836,19 +9337,19 @@ if (errorcode == 0 && cb.had_recurse) 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 (groupnumber > rc[p].groupnumber) search_from = rc[p].group; } if (rgroup == NULL) { - rgroup = PRIV(find_bracket)(search_from, utf, recno); + rgroup = PRIV(find_bracket)(search_from, utf, groupnumber); if (rgroup == NULL) { errorcode = ERR53; break; } if (--start < 0) start = RSCAN_CACHE_SIZE - 1; - rc[start].recno = recno; + rc[start].groupnumber = groupnumber; rc[start].group = rgroup; if (ccount < RSCAN_CACHE_SIZE) ccount++; } @@ -8861,93 +9362,27 @@ if (errorcode == 0 && cb.had_recurse) /* In rare debugging situations we sometimes need to look at the compiled code at this stage. */ -#ifdef CALL_PRINTINT +#ifdef DEBUG_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. */ +/* 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 && cb.check_lookbehind) +if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) { - 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; + PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; + if (PRIV(auto_possessify)(temp, utf, &cb) != 0) errorcode = ERR80; } -/* Failed to compile, or error while post-processing. Earlier errors get here -via the dreaded goto. */ +/* Failed to compile, or error while post-processing. */ -if (errorcode != 0) - { - HAD_ERROR: - *erroroffset = (int)(ptr - pattern); - HAD_UTF_ERROR: - *errorptr = errorcode; - pcre2_code_free(re); - re = NULL; - goto EXIT; - } +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 @@ -8956,7 +9391,7 @@ 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)) + is_anchored(codestart, 0, &cb, 0, FALSE)) re->overall_options |= PCRE2_ANCHORED; /* If the pattern is still not anchored and we do not have a first code unit, @@ -9005,7 +9440,8 @@ if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0) 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; + else if (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 @@ -9034,27 +9470,6 @@ if (reqcuflags >= 0 && } } -/* 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. */ @@ -9063,25 +9478,46 @@ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && PRIV(study)(re) != 0) { errorcode = ERR31; - goto HAD_ERROR; + goto HAD_CB_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. */ +/* 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: -if (copied_pattern != stack_copied_pattern) - ccontext->memctl.free(copied_pattern, ccontext->memctl.memory_data); +#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 != c32workspace) +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 */ -#pragma warning(pop) \ No newline at end of file +#pragma warning(pop) diff --git a/ProcessHacker/pcre/pcre2_dfa_match.c b/ProcessHacker/pcre/pcre2_dfa_match.c index c8ad26beef45..d0f756910bfc 100644 --- a/ProcessHacker/pcre/pcre2_dfa_match.c +++ b/ProcessHacker/pcre/pcre2_dfa_match.c @@ -72,6 +72,7 @@ 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 @@ -371,7 +372,7 @@ internal_dfa_match( uint32_t offsetcount, int *workspace, int wscount, - int rlevel) + uint32_t rlevel) { stateblock *active_states, *new_states, *temp_states; stateblock *next_active_state, *next_new_state; @@ -400,7 +401,7 @@ BOOL utf = FALSE; BOOL reset_could_continue = FALSE; -rlevel++; +if (rlevel++ > mb->match_limit_recursion) return PCRE2_ERROR_RECURSIONLIMIT; offsetcount &= (uint32_t)(-2); /* Round down */ wscount -= 2; @@ -2591,7 +2592,7 @@ for (;;) sizeof(local_workspace)/sizeof(int), /* size of same */ rlevel); /* function recursion level */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + 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); } } @@ -2710,7 +2711,7 @@ for (;;) sizeof(local_workspace)/sizeof(int), /* size of same */ rlevel); /* function recursion level */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + 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); } @@ -3115,7 +3116,7 @@ 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) { const pcre2_real_code *re = (const pcre2_real_code *)code; @@ -3216,6 +3217,7 @@ if (mcontext == NULL) { mb->callout = NULL; mb->memctl = re->memctl; + mb->match_limit_recursion = PRIV(default_match_context).recursion_limit; } else { @@ -3228,7 +3230,10 @@ else mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; + mb->match_limit_recursion = mcontext->recursion_limit; } +if (mb->match_limit_recursion > re->limit_recursion) + mb->match_limit_recursion = re->limit_recursion; mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; @@ -3470,7 +3475,7 @@ for (;;) { while (start_match < end_subject) { - register uint32_t c = UCHAR21TEST(start_match); + uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif @@ -3510,7 +3515,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 +3526,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; } } } diff --git a/ProcessHacker/pcre/pcre2_error.c b/ProcessHacker/pcre/pcre2_error.c index dcb03e1fbb3c..9ef2b9cd0041 100644 --- a/ProcessHacker/pcre/pcre2_error.c +++ b/ProcessHacker/pcre/pcre2_error.c @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -91,13 +92,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,7 +106,7 @@ 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" "number after (?C is greater than 255\0" @@ -132,13 +133,13 @@ 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" + "(?R (recursive pattern call) must be followed by a closing parenthesis\0" "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" /* 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,9 +152,9 @@ 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 */ @@ -161,7 +162,7 @@ static const unsigned char compile_error_texts[] = "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" + "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,9 @@ 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" ; /* Match-time and UTF error texts are in the same format. */ @@ -275,10 +279,10 @@ Returns: length of message if all is well */ 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; diff --git a/ProcessHacker/pcre/pcre2_find_bracket.c b/ProcessHacker/pcre/pcre2_find_bracket.c index 30d407aba5e9..1564cf96f5ff 100644 --- a/ProcessHacker/pcre/pcre2_find_bracket.c +++ b/ProcessHacker/pcre/pcre2_find_bracket.c @@ -45,7 +45,9 @@ 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 +73,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; diff --git a/ProcessHacker/pcre/pcre2_internal.h b/ProcessHacker/pcre/pcre2_internal.h index 56908708aa13..6a8774ce8c1f 100644 --- a/ProcessHacker/pcre/pcre2_internal.h +++ b/ProcessHacker/pcre/pcre2_internal.h @@ -142,20 +142,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 @@ -1298,23 +1284,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 +1359,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 */ diff --git a/ProcessHacker/pcre/pcre2_intmodedep.h b/ProcessHacker/pcre/pcre2_intmodedep.h index 596d62cfdcff..ebff7e30661c 100644 --- a/ProcessHacker/pcre/pcre2_intmodedep.h +++ b/ProcessHacker/pcre/pcre2_intmodedep.h @@ -140,7 +140,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,11 +200,11 @@ 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 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 @@ -648,18 +648,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,9 +699,10 @@ 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 */ open_capitem *open_caps; /* Chain of open capture items */ @@ -703,13 +710,17 @@ typedef struct compile_block { 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 */ @@ -718,9 +729,7 @@ typedef struct compile_block { 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 @@ -836,6 +845,7 @@ 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 */ + uint32_t match_limit_recursion; /* As it says */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ uint32_t nltype; /* Newline type */ diff --git a/ProcessHacker/pcre/pcre2_jit_compile.c b/ProcessHacker/pcre/pcre2_jit_compile.c index 0d3baa714e4a..5a02249324b3 100644 --- a/ProcessHacker/pcre/pcre2_jit_compile.c +++ b/ProcessHacker/pcre/pcre2_jit_compile.c @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_maketables.c b/ProcessHacker/pcre/pcre2_maketables.c index dbabdb246c30..ddfadfb00d42 100644 --- a/ProcessHacker/pcre/pcre2_maketables.c +++ b/ProcessHacker/pcre/pcre2_maketables.c @@ -45,6 +45,7 @@ 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" diff --git a/ProcessHacker/pcre/pcre2_match.c b/ProcessHacker/pcre/pcre2_match.c index bf770ab90cb0..e05dc35179eb 100644 --- a/ProcessHacker/pcre/pcre2_match.c +++ b/ProcessHacker/pcre/pcre2_match.c @@ -38,11 +38,8 @@ 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 @@ -146,14 +143,14 @@ 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_ref(PCRE2_SIZE offset, PCRE2_SIZE offset_top, PCRE2_SPTR eptr, match_block *mb, BOOL caseless, 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; @@ -300,7 +297,6 @@ enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, 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 @@ -310,8 +306,6 @@ argument of RMATCH isn't actually used in this definition. */ 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;\ @@ -429,7 +423,7 @@ to save the ovector while calling match() to process the pattern recursion. */ op_recurse_ovecsave(). */ static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, +match(PCRE2_SPTR eptr, PCRE2_SPTR ecode, PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth); @@ -472,11 +466,11 @@ static int #if defined(__GNUC__) && !defined(__INTEL_COMPILER) __attribute__ ((noinline)) #endif -op_recurse_ovecsave(REGISTER PCRE2_SPTR eptr, PCRE2_SPTR callpat, +op_recurse_ovecsave(PCRE2_SPTR eptr, PCRE2_SPTR callpat, PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth) { -register int rrc; +int rrc; BOOL cbegroup = *callpat >= OP_SBRA; recursion_info *new_recursive = mb->recursive; PCRE2_SIZE ovecsave[OP_RECURSE_STACK_SAVE_MAX]; @@ -580,20 +574,19 @@ Returns: MATCH_MATCH if matched ) these values are >= 0 */ static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, +match(PCRE2_SPTR eptr, PCRE2_SPTR ecode, PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth) { /* 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 */ +int rrc; /* Returns from recursive calls */ +int i; /* Used for loops not involving calls to RMATCH() */ +uint32_t c; /* Character values not kept over RMATCH() calls */ +BOOL utf; /* Local copy of UTF flag for speed */ BOOL minimize, possessive; /* Quantifier options */ -BOOL caseless; int condcode; /* When recursion is not being used, all "local" variables that have to be @@ -731,6 +724,7 @@ still need to be preserved over recursive calls of match(). These macros define the alternative names that are used. */ #define allow_zero cur_is_word +#define caseless cur_is_word #define cbegroup condition #define code_offset codelink #define condassert condition @@ -1323,7 +1317,7 @@ for (;;) { pcre2_callout_block cb; cb.version = 1; - cb.capture_top = offset_top/2; + cb.capture_top = (uint32_t)offset_top/2; cb.capture_last = mb->capture_last & CAPLMASK; cb.offset_vector = mb->ovector; cb.mark = mb->nomatch_mark; @@ -1507,8 +1501,8 @@ for (;;) if (offset >= offset_top) { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; + PCRE2_SIZE *iptr = mb->ovector + offset_top; + PCRE2_SIZE *iend = mb->ovector + offset; while (iptr < iend) *iptr++ = PCRE2_UNSET; offset_top = offset + 2; } @@ -1750,7 +1744,7 @@ for (;;) pcre2_callout_block cb; cb.version = 1; cb.callout_number = ecode[LINK_SIZE + 1]; - cb.capture_top = offset_top/2; + cb.capture_top = (uint32_t)offset_top/2; cb.capture_last = mb->capture_last & CAPLMASK; cb.offset_vector = mb->ovector; cb.mark = mb->nomatch_mark; @@ -2056,8 +2050,8 @@ for (;;) if (offset > offset_top) { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; + PCRE2_SIZE *iptr = mb->ovector + offset_top; + PCRE2_SIZE *iend = mb->ovector + offset; while (iptr < iend) *iptr++ = PCRE2_UNSET; } @@ -2386,7 +2380,7 @@ for (;;) case OP_ANY: if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && + eptr == mb->end_subject - 1 && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && UCHAR21TEST(eptr) == NLBLOCK->nl[0]) @@ -2853,9 +2847,7 @@ for (;;) continue; } - /* 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. */ for (i = 1; i <= min; i++) { @@ -3766,7 +3758,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t ch, och; + uint32_t ch, och; ecode++; GETCHARINC(ch, ecode); @@ -3788,7 +3780,7 @@ for (;;) else #endif /* SUPPORT_UNICODE */ { - register uint32_t ch = ecode[1]; + uint32_t ch = ecode[1]; c = *eptr++; if (ch == c || (op == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == c)) RRETURN(MATCH_NOMATCH); @@ -3894,7 +3886,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (i = 1; i <= min; i++) { if (eptr >= mb->end_subject) @@ -3929,7 +3921,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (fi = min;; fi++) { RMATCH(eptr, ecode, offset_top, mb, eptrb, RM28); @@ -3974,7 +3966,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (i = min; i < max; i++) { int len = 1; @@ -4035,7 +4027,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (i = 1; i <= min; i++) { if (eptr >= mb->end_subject) @@ -4069,7 +4061,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (fi = min;; fi++) { RMATCH(eptr, ecode, offset_top, mb, eptrb, RM32); @@ -4113,7 +4105,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - register uint32_t d; + uint32_t d; for (i = min; i < max; i++) { int len = 1; @@ -6735,8 +6727,8 @@ in case they inspect these fields. */ if (ocount > 0) { - register PCRE2_SIZE *iptr = mb->ovector + ocount; - register PCRE2_SIZE *iend = iptr - re->top_bracket; + PCRE2_SIZE *iptr = mb->ovector + ocount; + 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; @@ -6892,7 +6884,7 @@ for(;;) { while (start_match < end_subject) { - register uint32_t c = UCHAR21TEST(start_match); + uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 if (c > 255) c = 255; #endif @@ -6936,7 +6928,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. */ @@ -6947,7 +6939,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; } } } @@ -7166,7 +7158,7 @@ if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) too many to fit into the ovector. */ match_data->rc = ((mb->capture_last & OVFLBIT) != 0)? - 0 : mb->end_offset_top/2; + 0 : (int)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 @@ -7180,7 +7172,7 @@ if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) if (mb->end_offset_top/2 <= re->top_bracket) { - register PCRE2_SIZE *iptr, *iend; + 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; @@ -7245,5 +7237,3 @@ 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..6e2e0a40c8d1 100644 --- a/ProcessHacker/pcre/pcre2_match_data.c +++ b/ProcessHacker/pcre/pcre2_match_data.c @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_newline.c b/ProcessHacker/pcre/pcre2_newline.c index 2aab03a294fd..1d22666380bb 100644 --- a/ProcessHacker/pcre/pcre2_newline.c +++ b/ProcessHacker/pcre/pcre2_newline.c @@ -48,6 +48,7 @@ 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..07ab10fda25f 100644 --- a/ProcessHacker/pcre/pcre2_ord2utf.c +++ b/ProcessHacker/pcre/pcre2_ord2utf.c @@ -39,10 +39,12 @@ POSSIBILITY OF SUCH DAMAGE. */ +#define HAVE_CONFIG_H + /* 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 +85,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..fadad4b324a2 100644 --- a/ProcessHacker/pcre/pcre2_pattern_info.c +++ b/ProcessHacker/pcre/pcre2_pattern_info.c @@ -38,7 +38,9 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ + #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_printint.c b/ProcessHacker/pcre/pcre2_printint.c index 2d30926a744e..62074976489b 100644 --- a/ProcessHacker/pcre/pcre2_printint.c +++ b/ProcessHacker/pcre/pcre2_printint.c @@ -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); } } diff --git a/ProcessHacker/pcre/pcre2_serialize.c b/ProcessHacker/pcre/pcre2_serialize.c index 1aa3c54bd901..41d36a910c4a 100644 --- a/ProcessHacker/pcre/pcre2_serialize.c +++ b/ProcessHacker/pcre/pcre2_serialize.c @@ -42,6 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. a sequence of compiled codes. */ #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_string_utils.c b/ProcessHacker/pcre/pcre2_string_utils.c index feaa09894c48..f3666460bd78 100644 --- a/ProcessHacker/pcre/pcre2_string_utils.c +++ b/ProcessHacker/pcre/pcre2_string_utils.c @@ -43,6 +43,7 @@ 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 diff --git a/ProcessHacker/pcre/pcre2_study.c b/ProcessHacker/pcre/pcre2_study.c index 9bd0667e69fc..d6403dccb0bb 100644 --- a/ProcessHacker/pcre/pcre2_study.c +++ b/ProcessHacker/pcre/pcre2_study.c @@ -41,6 +41,7 @@ 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" @@ -50,6 +51,10 @@ collecting data (e.g. minimum matching length). */ #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. */ #define SET_BIT(c) re->start_bitmap[(c)/8] |= (1 << ((c)&7)) @@ -71,6 +76,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 +89,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 +102,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 +114,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 +137,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; @@ -166,7 +179,8 @@ for (;;) 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 +196,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 +471,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 +530,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 +644,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; @@ -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 @@ -1546,12 +1589,20 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0 && 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..c828e307f643 100644 --- a/ProcessHacker/pcre/pcre2_substitute.c +++ b/ProcessHacker/pcre/pcre2_substitute.c @@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ + #define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" @@ -114,7 +115,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 +128,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; @@ -287,6 +290,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. */ @@ -698,7 +707,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 +737,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) { diff --git a/ProcessHacker/pcre/pcre2_tables.c b/ProcessHacker/pcre/pcre2_tables.c index a9e6bc01c521..be0c101bc226 100644 --- a/ProcessHacker/pcre/pcre2_tables.c +++ b/ProcessHacker/pcre/pcre2_tables.c @@ -44,8 +44,8 @@ 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 +#ifndef PCRE2_PCRE2TEST /* We're compiling the library */ #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_ucp.h b/ProcessHacker/pcre/pcre2_ucp.h index 0b7553e5e062..02e5012c29c6 100644 --- a/ProcessHacker/pcre/pcre2_ucp.h +++ b/ProcessHacker/pcre/pcre2_ucp.h @@ -39,8 +39,8 @@ POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _PCRE2_UCP_H -#define _PCRE2_UCP_H +#ifndef PCRE2_UCP_H_IDEMPOTENT_GUARD +#define PCRE2_UCP_H_IDEMPOTENT_GUARD /* This file contains definitions of the property values that are returned by the UCD access macros. New values that are added for new releases of Unicode @@ -263,6 +263,6 @@ enum { ucp_SignWriting }; -#endif +#endif /* PCRE2_UCP_H_IDEMPOTENT_GUARD */ /* End of pcre2_ucp.h */ diff --git a/ProcessHacker/pcre/pcre2_valid_utf.c b/ProcessHacker/pcre/pcre2_valid_utf.c index 86749b30efa0..ccc043a5e2ac 100644 --- a/ProcessHacker/pcre/pcre2_valid_utf.c +++ b/ProcessHacker/pcre/pcre2_valid_utf.c @@ -44,8 +44,8 @@ strings. This file is also #included by the pcre2test program, which uses macros to change 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 +#ifndef PCRE2_PCRE2TEST /* We're compiling the library */ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -94,8 +94,8 @@ Returns: == 0 if the string is a valid UTF string int PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset) { -register PCRE2_SPTR p; -register uint32_t c; +PCRE2_SPTR p; +uint32_t c; /* ----------------- Check a UTF-8 string ----------------- */ @@ -134,7 +134,7 @@ PCRE2_ERROR_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff for (p = string; length > 0; p++) { - register uint32_t ab, d; + uint32_t ab, d; c = *p; length--; diff --git a/ProcessHacker/pcre/pcre2_xclass.c b/ProcessHacker/pcre/pcre2_xclass.c index ccff77056946..43d0150e7554 100644 --- a/ProcessHacker/pcre/pcre2_xclass.c +++ b/ProcessHacker/pcre/pcre2_xclass.c @@ -42,6 +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" diff --git a/ProcessHacker/pcre/pcre2posix.c b/ProcessHacker/pcre/pcre2posix.c index eb5926550119..343b835aa216 100644 --- a/ProcessHacker/pcre/pcre2posix.c +++ b/ProcessHacker/pcre/pcre2posix.c @@ -38,14 +38,15 @@ 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. */ +// dmex: Disable warnings. +#pragma warning(push) +#pragma warning(disable : 4267) #define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -101,7 +102,7 @@ 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 +#define PCRE2_CALL_CONVENTION #endif /* Table to translate PCRE2 compile time error codes into POSIX error codes. @@ -289,8 +290,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 @@ -370,4 +370,4 @@ switch(rc) /* End of pcre2posix.c */ -#pragma warning(pop) \ No newline at end of file +#pragma warning(pop) diff --git a/ProcessHacker/pcre/pcre2posix.h b/ProcessHacker/pcre/pcre2posix.h index 5f2906a4f32c..6505976aa493 100644 --- a/ProcessHacker/pcre/pcre2posix.h +++ b/ProcessHacker/pcre/pcre2posix.h @@ -56,7 +56,7 @@ 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 */ From 9880e279073a2f45eaac864c500d2cf48114fb0e Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 9 Jul 2017 05:19:03 +1000 Subject: [PATCH 265/839] Fix launcher issue with PhGetApplicationFileName --- phlib/util.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 0e68f3bd2136..35e9ef167aec 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2199,6 +2199,11 @@ PPH_STRING PhGetApplicationFileName( VOID ) { + PPH_STRING fileName; + + if (NT_SUCCESS(PhGetProcessImageFileNameWin32(NtCurrentProcess(), &fileName))) + return fileName; + return PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, NULL); } @@ -2210,13 +2215,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, '\\'); + + if (indexOfFileName != -1) + indexOfFileName++; + else + indexOfFileName = 0; + if (indexOfFileName != 0) { // Remove the file name from the path. From 2a279d602b9ea88559e73724c99429fac5e397ff Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 9 Jul 2017 16:28:11 +1000 Subject: [PATCH 266/839] Convert more message dialogs to TaskDialogs --- .gitattributes | 1 + ProcessHacker/ProcessHacker.def | 3 +-- ProcessHacker/actions.c | 13 ++++++------- ProcessHacker/main.c | 9 +++++---- ProcessHacker/mdump.c | 7 ++++--- ProcessHacker/options.c | 15 +++++++++------ ProcessHacker/plugman.c | 9 +++++++-- ProcessHacker/srvprp.c | 8 +++++--- phlib/include/phutil.h | 10 ---------- phlib/util.c | 20 +++++--------------- tools/peview/settings.c | 7 ++++--- 11 files changed, 47 insertions(+), 55 deletions(-) diff --git a/.gitattributes b/.gitattributes index ba1245642f8e..8c535f62e19d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,6 +16,7 @@ *.config eol=crlf *.cmd eol=crlf *.csproj eol=crlf +*.def eol=crlf *.h eol=crlf *.manifest eol=crlf *.resx eol=crlf diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 9e693cb33b12..d2e5565ca064 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -381,10 +381,9 @@ EXPORTS PhShellProperties PhShowConfirmMessage PhShowContinueStatus - PhShowMessage2 PhShowFileDialog PhShowMessage - PhShowMessage_V + PhShowMessage2 PhShowStatus PhUpdateHash diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index b822fd9f75a8..1f60c510791f 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1634,7 +1634,7 @@ BOOLEAN PhUiDetachFromDebuggerProcess( if (status == STATUS_PORT_NOT_SET) { - PhShowInformation(hWnd, L"The process is not being debugged."); + PhShowInformation2(hWnd, L"The process is not being debugged.", L""); return FALSE; } @@ -2262,13 +2262,12 @@ BOOLEAN PhUiCloseConnections( } else { - if (PhShowMessage( + if (PhShowMessage2( 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 + 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; } diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 0923076ecf9a..dabb1af18e6f 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -667,10 +667,11 @@ VOID PhpInitializeSettings( // 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" + if (PhShowMessage2( + PhMainWndHandle, + 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) { diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index 8214941f406a..b31dc20ae7b3 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -278,10 +278,11 @@ NTSTATUS PhpProcessMiniDumpThreadStart( } else { - if (PhShowMessage( + if (PhShowMessage2( context->WindowHandle, - MB_YESNO | MB_ICONWARNING, - L"The process is 32-bit, but the 32-bit version of Process Hacker could not be located. " + 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) { diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 21944675fa3e..9e0232f4b06f 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -207,10 +207,11 @@ VOID PhShowOptionsDialog( if (RestartRequired) { - if (PhShowMessage( + if (PhShowMessage2( PhMainWndHandle, - MB_ICONQUESTION | MB_YESNO, - L"One or more options you have changed requires a restart of Process Hacker. " + 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) { @@ -330,10 +331,12 @@ LRESULT CALLBACK PhpOptionsWndProc( { case IDC_RESET: { - if (PhShowMessage( + if (PhShowMessage2( hwnd, - MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2, - L"Do you want to reset all settings and restart Process Hacker?" + 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); diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index a7c4314126a5..65d84a0767ed 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -380,8 +380,13 @@ INT_PTR CALLBACK PhpPluginsDlgProc( break; case IDC_CLEANUP: { - if (PhShowMessage(hwndDlg, MB_ICONQUESTION | MB_YESNO, - L"Do you want to clean up unused plugin settings?") == IDYES) + 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(); } diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index e9cc3f1ac26c..0aab0620ecae 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -537,10 +537,12 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( goto Cleanup; ErrorCase: - if (PhShowMessage( + if (PhShowMessage2( hwndDlg, - MB_ICONERROR | MB_RETRYCANCEL, - L"Unable to change service configuration: %s", + TDCBF_RETRY_BUTTON | TDCBF_CANCEL_BUTTON, + TD_ERROR_ICON, + L"Unable to change service configuration.", + L"%s", PH_AUTO_T(PH_STRING, PhGetWin32Message(GetLastError()))->Buffer ) == IDRETRY) { diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 34a020c2ba4e..d5dbdb468868 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -222,16 +222,6 @@ PhShowMessage( ... ); -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__) diff --git a/phlib/util.c b/phlib/util.c index 35e9ef167aec..e33e0e449da9 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -374,25 +374,15 @@ INT PhShowMessage( _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); + va_start(argptr, Format); + message = PhFormatString_V(Format, argptr); + va_end(argptr); if (!message) return -1; diff --git a/tools/peview/settings.c b/tools/peview/settings.c index 750ce1ace2ea..224075b64973 100644 --- a/tools/peview/settings.c +++ b/tools/peview/settings.c @@ -94,10 +94,11 @@ VOID PeInitializeSettings( // change anything. if (status == STATUS_FILE_CORRUPT_ERROR) { - if (PhShowMessage( + if (PhShowMessage2( NULL, - MB_ICONWARNING | MB_YESNO, - L"PE View's settings file is corrupt. Do you want to reset it?\n" + 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) { From 6ac84c04f37a1a40b07c86ed4a2e852ed2c5861e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 10 Jul 2017 15:02:02 +1000 Subject: [PATCH 267/839] Add missing error check --- ProcessHacker/main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index dabb1af18e6f..216a7f81a09a 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -558,7 +558,6 @@ VOID PhInitializeKph( { 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; @@ -571,14 +570,16 @@ VOID PhInitializeKph( kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); - parameters.SecurityLevel = KphSecurityPrivilegeCheck; + parameters.SecurityLevel = KphSecuritySignatureCheck; parameters.CreateDynamicConfiguration = TRUE; - KphConnect2Ex(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); - if (signature = PhpReadSignature(processhackerSigFileName->Buffer, &signatureSize)) + if (NT_SUCCESS(KphConnect2Ex(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters))) { - KphVerifyClient(signature, signatureSize); - PhFree(signature); + if (signature = PhpReadSignature(processhackerSigFileName->Buffer, &signatureSize)) + { + KphVerifyClient(signature, signatureSize); + PhFree(signature); + } } PhDereferenceObject(kprocesshackerFileName); From 968c8ca640e6a2678698c5e4b0a09fff2af6bf86 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 10 Jul 2017 16:24:37 +1000 Subject: [PATCH 268/839] treeview: Use system colors by default (feature request) https://wj32.org/processhacker/forums/viewtopic.php?f=14&t=2694 --- phlib/treenew.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/phlib/treenew.c b/phlib/treenew.c index bd96c52cfcc6..c571a64b7b21 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -450,7 +450,7 @@ BOOLEAN PhTnpOnCreate( } if (!(Context->VScrollHandle = CreateWindow( - L"SCROLLBAR", + WC_SCROLLBAR, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_VERT, 0, @@ -467,7 +467,7 @@ BOOLEAN PhTnpOnCreate( } if (!(Context->HScrollHandle = CreateWindow( - L"SCROLLBAR", + WC_SCROLLBAR, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_HORZ, 0, @@ -484,7 +484,7 @@ BOOLEAN PhTnpOnCreate( } if (!(Context->FillerBoxHandle = CreateWindow( - L"STATIC", + WC_STATIC, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, @@ -5183,8 +5183,8 @@ VOID PhTnpPrepareRowForDraw( getNodeColor.Flags = 0; getNodeColor.Node = Node; - getNodeColor.BackColor = RGB(0xff, 0xff, 0xff); - getNodeColor.ForeColor = RGB(0x00, 0x00, 0x00); + getNodeColor.BackColor = GetSysColor(COLOR_WINDOW); // RGB(0xff, 0xff, 0xff) + getNodeColor.ForeColor = GetSysColor(COLOR_WINDOWTEXT); // RGB(0x00, 0x00, 0x00) if (Context->Callback( Context->Handle, From ca73c09e6032883aa03c63afdfd25123a705606e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 10 Jul 2017 16:37:42 +1000 Subject: [PATCH 269/839] treeview: Improve previous commit --- phlib/include/treenewp.h | 2 ++ phlib/treenew.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index 8904a3a1a7cb..997bbf8980ca 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -132,6 +132,8 @@ typedef struct _PH_TREENEW_CONTEXT TEXTMETRIC TextMetrics; HTHEME ThemeData; + COLORREF DefaultBackColor; + COLORREF DefaultForeColor; LONG SystemBorderX; LONG SystemBorderY; LONG SystemEdgeX; diff --git a/phlib/treenew.c b/phlib/treenew.c index c571a64b7b21..639d57cf4a17 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -340,6 +340,8 @@ VOID PhTnpCreateTreeNewContext( 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; } @@ -5183,8 +5185,8 @@ VOID PhTnpPrepareRowForDraw( getNodeColor.Flags = 0; getNodeColor.Node = Node; - getNodeColor.BackColor = GetSysColor(COLOR_WINDOW); // RGB(0xff, 0xff, 0xff) - getNodeColor.ForeColor = GetSysColor(COLOR_WINDOWTEXT); // RGB(0x00, 0x00, 0x00) + getNodeColor.BackColor = Context->DefaultBackColor; + getNodeColor.ForeColor = Context->DefaultForeColor; if (Context->Callback( Context->Handle, From e140ae4a8fee01e5607cb6d99692a788e9af3d2d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 10 Jul 2017 16:58:32 +1000 Subject: [PATCH 270/839] WindowExplorer: Fix regression --- plugins/WindowExplorer/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index 293fddfb2e29..e0fbcffdc876 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -34,7 +34,6 @@ PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; - VOID NTAPI LoadCallback( _In_opt_ PVOID Parameter, _In_opt_ PVOID Context @@ -231,7 +230,7 @@ LOGICAL DllMain( isClient = FALSE; - if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhLibImageBase")) + if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhInstanceHandle")) { isClient = TRUE; } From 3476d5c8c3fd433e92e42947bf840b867c00ed4e Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 11:35:14 +1000 Subject: [PATCH 271/839] Fix splitter GDI leak --- ProcessHacker/include/splitter.h | 4 +++ ProcessHacker/settings.c | 2 +- ProcessHacker/splitter.c | 46 ++++++++++++++++++++------------ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h index 9be37f0023cd..75be46d7593b 100644 --- a/ProcessHacker/include/splitter.h +++ b/ProcessHacker/include/splitter.h @@ -20,6 +20,10 @@ typedef struct _PH_HSPLITTER_CONTEXT HWND ParentWindow; HWND TopWindow; HWND BottomWindow; + + HBRUSH FocusBrush; + HBRUSH HotBrush; + HBRUSH NormalBrush; } PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index bda87b5abf52..2948a89275c0 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -147,7 +147,7 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"ThreadStackListViewColumns", L""); PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,380"); PhpAddStringSetting(L"TokenGroupsListViewColumns", L""); - PhpAddIntegerSetting(L"TokenSplitterPosition", L"250"); + PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 0516fc2cd81d..4de4d68eb34f 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -91,16 +91,16 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM GetClientRect(hwnd, &clientRect); if (context->HasFocus) - FillRect(hdc, &clientRect, CreateSolidBrush(RGB(0x0, 0x0, 0x0))); + FillRect(hdc, &clientRect, context->FocusBrush); else if (context->Hot) - FillRect(hdc, &clientRect, CreateSolidBrush(RGB(0x44, 0x44, 0x44))); + FillRect(hdc, &clientRect, context->HotBrush); else - FillRect(hdc, &clientRect, GetSysColorBrush(COLOR_WINDOW)); + FillRect(hdc, &clientRect, context->NormalBrush); EndPaint(hwnd, &paintStruct); } } - return 0; + return 1; case WM_ERASEBKGND: return 1; case WM_LBUTTONDOWN: @@ -145,6 +145,12 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM return 0; case WM_MOUSEMOVE: { + INT width; + INT height; + INT NewPos; + HDWP deferHandle; + POINT cursorPos; + if (!context->Hot) { TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) }; @@ -158,19 +164,17 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM if (!context->HasFocus) break; - int Width = GetClientWindowWidth(context->ParentWindow); - int NewPos; - HDWP deferHandle; - POINT cursorPos; - GetCursorPos(&cursorPos); ScreenToClient(context->ParentWindow, &cursorPos); NewPos = cursorPos.y; + width = GetClientWindowWidth(context->ParentWindow); + height = GetClientWindowHeight(context->ParentWindow); if (NewPos < 200) break; - if (NewPos > GetClientWindowHeight(context->ParentWindow) - 80) + if (NewPos > height - 80) break; + context->SplitterOffset = NewPos; deferHandle = BeginDeferWindowPos(3); @@ -180,7 +184,7 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM NULL, SPLITTER_PADDING, 90, - Width - SPLITTER_PADDING * 2, + width - SPLITTER_PADDING * 2, cursorPos.y - 90, SWP_NOZORDER | SWP_NOACTIVATE ); @@ -190,7 +194,7 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM NULL, 0, cursorPos.y, - Width, + width, SPLITTER_PADDING, SWP_NOZORDER | SWP_NOACTIVATE ); @@ -200,8 +204,8 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM NULL, SPLITTER_PADDING, cursorPos.y + SPLITTER_PADDING, - Width - SPLITTER_PADDING * 2, - GetClientWindowHeight(context->ParentWindow) - (cursorPos.y + SPLITTER_PADDING) - 65, + width - SPLITTER_PADDING * 2, + height - (cursorPos.y + SPLITTER_PADDING) - 65, SWP_NOZORDER | SWP_NOACTIVATE ); @@ -229,6 +233,9 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( context->TopWindow = TopWindow; context->BottomWindow = BottomWindow; context->SplitterOffset = PhGetIntegerSetting(L"TokenSplitterPosition"); + context->FocusBrush = CreateSolidBrush(RGB(0x0, 0x0, 0x0)); + context->HotBrush = CreateSolidBrush(RGB(0x44, 0x44, 0x44)); + context->NormalBrush = GetSysColorBrush(COLOR_WINDOW); if (PhBeginInitOnce(&initOnce)) { @@ -252,14 +259,14 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( } context->Window = CreateWindowEx( - WS_EX_CONTROLPARENT | WS_EX_TRANSPARENT, + WS_EX_TRANSPARENT, L"PhHSplitter", NULL, WS_CHILD | WS_VISIBLE, 5, 5, - 465, - 10, + 5, + 5, ParentWindow, NULL, PhInstanceHandle, @@ -277,6 +284,11 @@ VOID PhDeleteHSplitter( ) { PhSetIntegerSetting(L"TokenSplitterPosition", Context->SplitterOffset); + + if (Context->FocusBrush) + DeleteObject(Context->FocusBrush); + if (Context->HotBrush) + DeleteObject(Context->HotBrush); } VOID PhHSplitterHandleWmSize( From 780d49e3109ee3568c7b889a2b0a1b61b785bbb3 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 13:31:54 +1000 Subject: [PATCH 272/839] NetworkTools: Fix typo --- plugins/NetworkTools/tracert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index dd98669b9af2..9e4bb57c0279 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -703,7 +703,7 @@ NTSTATUS TracertDialogThreadStart( if (result == -1) break; - if (!IsDialogMessage(context->WindowHandle, &message)) + if (!IsDialogMessage(windowHandle, &message)) { TranslateMessage(&message); DispatchMessage(&message); From 51d31463bf6629dc8f1d8374c83ebad0a26a1d32 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 16:49:04 +1000 Subject: [PATCH 273/839] Fix issue with process starttime and timezone changes --- phlib/include/phutil.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index d5dbdb468868..2cf2f00573e6 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -164,12 +164,11 @@ PhLargeIntegerToLocalSystemTime( ) { FILETIME fileTime; - FILETIME newFileTime; fileTime.dwLowDateTime = LargeInteger->LowPart; fileTime.dwHighDateTime = LargeInteger->HighPart; - FileTimeToLocalFileTime(&fileTime, &newFileTime); - FileTimeToSystemTime(&newFileTime, SystemTime); + FileTimeToSystemTime(&fileTime, SystemTime); + SystemTimeToTzSpecificLocalTime(NULL, SystemTime, SystemTime); } PHLIBAPI From 89d9e3ba2289e7bfc9065b2349b04e33c16fe296 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 16:49:46 +1000 Subject: [PATCH 274/839] Fix typo --- ProcessHacker/syssccpu.c | 2 +- plugins/ToolStatus/graph.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/syssccpu.c b/ProcessHacker/syssccpu.c index c2a6cc7d9a69..a08ab77f489a 100644 --- a/ProcessHacker/syssccpu.c +++ b/ProcessHacker/syssccpu.c @@ -768,7 +768,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; diff --git a/plugins/ToolStatus/graph.c b/plugins/ToolStatus/graph.c index c8effcd4396e..7550714f44fb 100644 --- a/plugins/ToolStatus/graph.c +++ b/plugins/ToolStatus/graph.c @@ -285,7 +285,7 @@ static 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; From 1c567b83e815af81bc06ff0f6c817c17af8b3d31 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 17:18:20 +1000 Subject: [PATCH 275/839] Revert "Fix issue with process starttime and timezone changes" This reverts commit 51d31463bf6629dc8f1d8374c83ebad0a26a1d32. --- phlib/include/phutil.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 2cf2f00573e6..d5dbdb468868 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -164,11 +164,12 @@ PhLargeIntegerToLocalSystemTime( ) { FILETIME fileTime; + FILETIME newFileTime; fileTime.dwLowDateTime = LargeInteger->LowPart; fileTime.dwHighDateTime = LargeInteger->HighPart; - FileTimeToSystemTime(&fileTime, SystemTime); - SystemTimeToTzSpecificLocalTime(NULL, SystemTime, SystemTime); + FileTimeToLocalFileTime(&fileTime, &newFileTime); + FileTimeToSystemTime(&newFileTime, SystemTime); } PHLIBAPI From f0c02aaabcf3c0690311dc8a6a1b8bdb6d5f9e57 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 11 Jul 2017 19:18:43 +1000 Subject: [PATCH 276/839] Fix virtual device path issues --- phlib/util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phlib/util.c b/phlib/util.c index e33e0e449da9..2fd650b27028 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2192,7 +2192,10 @@ PPH_STRING PhGetApplicationFileName( PPH_STRING fileName; if (NT_SUCCESS(PhGetProcessImageFileNameWin32(NtCurrentProcess(), &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); return fileName; + } return PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, NULL); } From 7b5570a25048e3618d1f1ffa30f3a86f56cb8303 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 12 Jul 2017 01:55:49 +1000 Subject: [PATCH 277/839] Add workaround for unknown services on RS2 --- ProcessHacker/srvprv.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index fc2d8b6d1ba9..70dc41e6b684 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -129,6 +129,15 @@ PPH_SERVICE_ITEM PhCreateServiceItem( if (Information) { + if (WindowsVersion >= WINDOWS_10_RS2) + { + // https://github.com/processhacker2/processhacker/issues/120 + if (Information->ServiceStatusProcess.dwServiceType == SERVICE_WIN32) + Information->ServiceStatusProcess.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + if (Information->ServiceStatusProcess.dwServiceType == (SERVICE_WIN32 | SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE)) + Information->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; + } + serviceItem->Name = PhCreateString(Information->lpServiceName); serviceItem->Key = serviceItem->Name->sr; serviceItem->DisplayName = PhCreateString(Information->lpDisplayName); @@ -645,6 +654,15 @@ VOID PhServiceProviderUpdate( } else { + if (WindowsVersion >= WINDOWS_10_RS2) + { + // https://github.com/processhacker2/processhacker/issues/120 + if (serviceEntry->ServiceStatusProcess.dwServiceType == SERVICE_WIN32) + serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + if (serviceEntry->ServiceStatusProcess.dwServiceType == (SERVICE_WIN32 | SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE)) + serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; + } + if ( serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || From e5802a1235f3bc7cbdb6f46523a9739ab0df9c47 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 12 Jul 2017 19:13:44 +1000 Subject: [PATCH 278/839] Enable advanced ACL dialogs by default --- ProcessHacker/settings.c | 1 + phlib/secedit.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 2948a89275c0..19c10e064859 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -47,6 +47,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableStage2", L"1"); PhpAddIntegerSetting(L"EnableWarnings", L"1"); PhpAddIntegerSetting(L"EnableWindowText", L"1"); + PhpAddIntegerSetting(L"EnableSecurityAdvancedDialog", L"1"); PhpAddStringSetting(L"EnvironmentListViewColumns", L""); PhpAddIntegerSetting(L"FindObjRegex", L"0"); PhpAddStringSetting(L"FindObjListViewColumns", L""); diff --git a/phlib/secedit.c b/phlib/secedit.c index f1c09a04cb80..9208498591f5 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -25,7 +25,7 @@ #include #include - +#include #include static ISecurityInformationVtbl PhSecurityInformation_VTable = @@ -118,7 +118,10 @@ VOID PhEditSecurity( FALSE ); - EditSecurity(hWnd, info); + if (PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) + EditSecurityAdvanced(NULL, info, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); + else + EditSecurity(hWnd, info); PhSecurityInformation_Release(info); } From 5e6a57048df71465bb3657d3bb05f2b902b31e0a Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 12 Jul 2017 19:46:45 +1000 Subject: [PATCH 279/839] Fix advanced ACL dialog parent --- phlib/secedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/secedit.c b/phlib/secedit.c index 9208498591f5..99bdeef28d85 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -119,7 +119,7 @@ VOID PhEditSecurity( ); if (PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) - EditSecurityAdvanced(NULL, info, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); + EditSecurityAdvanced(hWnd, info, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); else EditSecurity(hWnd, info); From dca4b534e75aea99c78c70a5a631f29aacc79be8 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 12 Jul 2017 22:09:41 +1000 Subject: [PATCH 280/839] disable advanced security dialog on windows 7 --- phlib/secedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/secedit.c b/phlib/secedit.c index 99bdeef28d85..65057fae144d 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -118,7 +118,7 @@ VOID PhEditSecurity( FALSE ); - if (PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) + if (WindowsVersion >= WINDOWS_8_1 && PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) EditSecurityAdvanced(hWnd, info, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); else EditSecurity(hWnd, info); From d7342929f1426e597b95e0c20a9b9651d406f410 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 13 Jul 2017 20:25:05 +1000 Subject: [PATCH 281/839] Remove debug typedef --- phnt/include/ntrtl.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 74bef7a7d37e..0cfd67a428d1 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -1376,22 +1376,12 @@ VOID NTAPI RtlInitEmptyUnicodeString( _Out_ PUNICODE_STRING DestinationString, - _In_opt_ PWCHAR Buffer, - _In_opt_ USHORT MaximumLength + _In_ PWCHAR Buffer, + _In_ USHORT MaximumLength ) { - if (Buffer) - { - DestinationString->Buffer = Buffer; - DestinationString->MaximumLength = MaximumLength; - } - else - { - PTEB currentTeb = NtCurrentTeb(); - DestinationString->Buffer = currentTeb->StaticUnicodeBuffer; - DestinationString->MaximumLength = sizeof(currentTeb->StaticUnicodeBuffer); - } - + DestinationString->Buffer = Buffer; + DestinationString->MaximumLength = MaximumLength; DestinationString->Length = 0; } From 634e9a7d6d006021189a143fca24ac7a95fcecf8 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Jul 2017 06:45:12 +1000 Subject: [PATCH 282/839] Improve PhUndecorateName, Fix multi-thread issue (PR #139) --- phlib/include/symprv.h | 30 ++++++++-------- phlib/symprv.c | 80 ++++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/phlib/include/symprv.h b/phlib/include/symprv.h index 7c747fb9b913..f0a83df00d5d 100644 --- a/phlib/include/symprv.h +++ b/phlib/include/symprv.h @@ -169,7 +169,7 @@ ULONG64 __stdcall PhGetModuleBase64( _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr + _In_ ULONG64 dwAddr ); PHLIBAPI @@ -177,7 +177,7 @@ PVOID __stdcall PhFunctionTableAccess64( _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase + _In_ ULONG64 AddrBase ); #ifndef _DBGHELP_ @@ -189,23 +189,23 @@ typedef struct _tagADDRESS64 *LPADDRESS64; typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( _In_ HANDLE hProcess, - _In_ DWORD64 qwBaseAddress, + _In_ ULONG64 qwBaseAddress, _Out_writes_bytes_(nSize) PVOID lpBuffer, - _In_ DWORD nSize, - _Out_ LPDWORD lpNumberOfBytesRead + _In_ ULONG nSize, + _Out_ PULONG lpNumberOfBytesRead ); typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( _In_ HANDLE ahProcess, - _In_ DWORD64 AddrBase + _In_ ULONG64 AddrBase ); -typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)( +typedef ULONG64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)( _In_ HANDLE hProcess, - _In_ DWORD64 Address + _In_ ULONG64 Address ); -typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( +typedef ULONG64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( _In_ HANDLE hProcess, _In_ HANDLE hThread, _In_ LPADDRESS64 lpaddr @@ -300,17 +300,17 @@ PHLIBAPI PPH_STRING NTAPI PhUndecorateName( - _In_ HANDLE ProcessHandle, - _In_ PCSTR DecoratedName -); + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PSTR DecoratedName + ); PHLIBAPI PPH_STRING NTAPI PhUndecorateNameW( - _In_ HANDLE ProcessHandle, - _In_ PWSTR DecoratedName -); + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR DecoratedName + ); #ifdef __cplusplus } diff --git a/phlib/symprv.c b/phlib/symprv.c index 2065b1887c1a..3be8511f4924 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -1310,7 +1310,7 @@ NTSTATUS PhAccessOutOfProcessFunctionEntry( ULONG64 __stdcall PhGetModuleBase64( _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr + _In_ ULONG64 dwAddr ) { ULONG64 base; @@ -1347,7 +1347,7 @@ ULONG64 __stdcall PhGetModuleBase64( PVOID __stdcall PhFunctionTableAccess64( _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase + _In_ ULONG64 AddrBase ) { #ifdef _WIN64 @@ -1763,62 +1763,64 @@ NTSTATUS PhWalkThreadStack( return status; } - PPH_STRING PhUndecorateName( - _In_ HANDLE ProcessHandle, - _In_ PCSTR DecoratedName -) + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PSTR DecoratedName + ) { - PPH_STRING UndecoratedStr = NULL; - PSTR UndecoratedName = NULL; - DWORD CandidateSize = 512; // there is no way to know the resulting length of an undecorated name - // if there is not enough place, the function does not fail. Instead it - // return a truncated name. + PPH_STRING undecoratedStr = NULL; + PSTR undecoratedName = NULL; + + PhpRegisterSymbolProvider(SymbolProvider); - if ((!SymInitialize_I) || (!UnDecorateSymbolName_I)) + if (!UnDecorateSymbolName_I) return NULL; - - - SymInitialize_I(ProcessHandle, NULL, TRUE); - - UndecoratedName = PhAllocate(CandidateSize*sizeof(CHAR)); - - DWORD Length = UnDecorateSymbolName_I(DecoratedName, UndecoratedName, CandidateSize, UNDNAME_COMPLETE); - if (Length > 0) + + // lucasg: there is no way to know the resulting length of an undecorated name + // if there is not enough place, the function does not fail. Instead it + // return a truncated name. + + undecoratedName = PhAllocate(PAGE_SIZE * sizeof(CHAR)); + + PH_LOCK_SYMBOLS(); + if (UnDecorateSymbolName_I(DecoratedName, undecoratedName, PAGE_SIZE, UNDNAME_COMPLETE) != 0) { - UndecoratedStr = PhZeroExtendToUtf16(UndecoratedName); + undecoratedStr = PhZeroExtendToUtf16(undecoratedName); } - - PhFree(UndecoratedName); - return UndecoratedStr; + PH_UNLOCK_SYMBOLS(); + + PhFree(undecoratedName); + + return undecoratedStr; } PPH_STRING PhUndecorateNameW( - _In_ HANDLE ProcessHandle, + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, _In_ PWSTR DecoratedName -) + ) { + PPH_STRING undecoratedStr = NULL; + PWSTR undecoratedName = NULL; - PPH_STRING UndecoratedStr = NULL; - PWSTR UndecoratedName = NULL; - DWORD CandidateSize = 512; // there is no way to know the resulting length of an undecorated name - // if there is not enough place, the function does not fail. Instead it - // return a truncated name. + PhpRegisterSymbolProvider(SymbolProvider); - if ((!SymInitialize_I) || (!UnDecorateSymbolNameW_I)) + if (!UnDecorateSymbolNameW_I) return NULL; + // lucasg: there is no way to know the resulting length of an undecorated name + // if there is not enough place, the function does not fail. Instead it + // return a truncated name. - SymInitialize_I(ProcessHandle, NULL, TRUE); - - UndecoratedName = PhAllocate(CandidateSize *sizeof(WCHAR)); + undecoratedName = PhAllocate(PAGE_SIZE * sizeof(WCHAR)); - if (UnDecorateSymbolNameW_I(DecoratedName, UndecoratedName, CandidateSize, UNDNAME_COMPLETE)) + PH_LOCK_SYMBOLS(); + if (UnDecorateSymbolNameW_I(DecoratedName, undecoratedName, PAGE_SIZE, UNDNAME_COMPLETE) != 0) { - UndecoratedStr = PhCreateString(UndecoratedName); + undecoratedStr = PhCreateString(undecoratedName); } + PH_UNLOCK_SYMBOLS(); - PhFree(UndecoratedName); - return UndecoratedStr; + PhFree(undecoratedName); + return undecoratedStr; } \ No newline at end of file From 4c012700faa010bd7e46dad889a32a342a27adc1 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Jul 2017 09:08:37 +1000 Subject: [PATCH 283/839] Revert "Temporarily disable KPH by default until dynamic loading support has been merged" This reverts commit 4cca64a04c3571b9a4f81ae21d4d01ebcf6faf5c. --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 19c10e064859..55528bca1a76 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -40,7 +40,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ElevationLevel", L"1"); // PromptElevateAction PhpAddIntegerSetting(L"EnableCycleCpuUsage", L"1"); PhpAddIntegerSetting(L"EnableInstantTooltips", L"0"); - PhpAddIntegerSetting(L"EnableKph", L"0"); + PhpAddIntegerSetting(L"EnableKph", L"1"); PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"1"); PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); From a4b40e38d61e6fbdadbe7d94bcb99ff6e7f5e8ee Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Jul 2017 12:33:50 +1000 Subject: [PATCH 284/839] Remove remaining legacy DWORD types --- ProcessHacker/actions.c | 2 +- ProcessHacker/appsup.c | 2 +- ProcessHacker/dbgcon.c | 4 +- ProcessHacker/include/notificop.h | 4 +- ProcessHacker/netprv.c | 2 +- ProcessHacker/notifico.c | 4 +- ProcessHacker/phsvc/clapi.c | 10 ++-- ProcessHacker/phsvc/svcapi.c | 10 ++-- ProcessHacker/runas.c | 14 +++--- phlib/include/apiimport.h | 2 +- phlib/include/guisup.h | 2 +- phlib/include/phbasesup.h | 2 +- phlib/include/symprvp.h | 76 +++++++++++++++---------------- phlib/include/verifyp.h | 46 +++++++++---------- 14 files changed, 90 insertions(+), 90 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 1f60c510791f..d039f5ed237b 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -48,7 +48,7 @@ #include #include -typedef DWORD (WINAPI *_SetTcpEntry)( +typedef ULONG (WINAPI *_SetTcpEntry)( _In_ PMIB_TCPROW pTcpRow ); diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 4928ef98b55a..8517f5c049ee 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1461,7 +1461,7 @@ BOOLEAN PhCreateProcessIgnoreIfeoDebugger( { BOOLEAN result; BOOL (NTAPI *debugSetProcessKillOnExit)(BOOL); - BOOL (NTAPI *debugActiveProcessStop)(DWORD); + BOOL (NTAPI *debugActiveProcessStop)(ULONG); BOOLEAN originalValue; STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index 2107da471b6b..c441891d4979 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -45,7 +45,7 @@ typedef struct _STRING_TABLE_ENTRY } STRING_TABLE_ENTRY, *PSTRING_TABLE_ENTRY; BOOL ConsoleHandlerRoutine( - _In_ DWORD dwCtrlType + _In_ ULONG dwCtrlType ); VOID PhpPrintHashtableStatistics( @@ -122,7 +122,7 @@ VOID PhCloseDebugConsole( } static BOOL ConsoleHandlerRoutine( - _In_ DWORD dwCtrlType + _In_ ULONG dwCtrlType ) { switch (dwCtrlType) diff --git a/ProcessHacker/include/notificop.h b/ProcessHacker/include/notificop.h index 5c512ec22400..00a993da9040 100644 --- a/ProcessHacker/include/notificop.h +++ b/ProcessHacker/include/notificop.h @@ -90,7 +90,7 @@ VOID PhNfpIconClickActivateTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ); VOID PhNfpDisableHover( @@ -101,7 +101,7 @@ VOID PhNfpIconRestoreHoverTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ); #endif diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index 48f5b77c72f9..07968e356c33 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -782,7 +782,7 @@ BOOLEAN PhGetNetworkConnections( ) { PVOID table; - DWORD tableSize; + ULONG tableSize; PMIB_TCPTABLE_OWNER_MODULE tcp4Table; PMIB_UDPTABLE_OWNER_MODULE udp4Table; PMIB_TCP6TABLE_OWNER_MODULE tcp6Table; diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 726d858b7797..15f244a30e4c 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -1313,7 +1313,7 @@ VOID PhNfpIconClickActivateTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ) { PhPinMiniInformation(MiniInfoActivePinType, 1, 0, @@ -1334,7 +1334,7 @@ VOID PhNfpIconRestoreHoverTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ) { IconDisableHover = FALSE; diff --git a/ProcessHacker/phsvc/clapi.c b/ProcessHacker/phsvc/clapi.c index 5a5cfb5b867a..8863149b2333 100644 --- a/ProcessHacker/phsvc/clapi.c +++ b/ProcessHacker/phsvc/clapi.c @@ -895,11 +895,11 @@ NTSTATUS PhSvcCallSetTcpEntry( PHSVC_API_MSG m; struct { - DWORD dwState; - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwRemoteAddr; - DWORD dwRemotePort; + ULONG dwState; + ULONG dwLocalAddr; + ULONG dwLocalPort; + ULONG dwRemoteAddr; + ULONG dwRemotePort; } *tcpRow = TcpRow; if (!PhSvcClPortHandle) diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index 20fef6efb6a2..89eee95d20cb 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -1113,11 +1113,11 @@ NTSTATUS PhSvcApiSetTcpEntry( ULONG (__stdcall *localSetTcpEntry)(PVOID TcpRow); struct { - DWORD dwState; - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwRemoteAddr; - DWORD dwRemotePort; + ULONG dwState; + ULONG dwLocalAddr; + ULONG dwLocalPort; + ULONG dwRemoteAddr; + ULONG dwRemotePort; } tcpRow; ULONG result; diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index c7131fbb28ff..e84d047429df 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -1015,11 +1015,11 @@ static VOID SetRunAsServiceStatus( SetServiceStatus(RunAsServiceStatusHandle, &status); } -static DWORD WINAPI RunAsServiceHandlerEx( - _In_ DWORD dwControl, - _In_ DWORD dwEventType, - _In_ LPVOID lpEventData, - _In_ LPVOID lpContext +static ULONG WINAPI RunAsServiceHandlerEx( + _In_ ULONG dwControl, + _In_ ULONG dwEventType, + _In_ PVOID lpEventData, + _In_ PVOID lpContext ) { switch (dwControl) @@ -1035,8 +1035,8 @@ static DWORD WINAPI RunAsServiceHandlerEx( } static VOID WINAPI RunAsServiceMain( - _In_ DWORD dwArgc, - _In_ LPTSTR *lpszArgv + _In_ ULONG dwArgc, + _In_ PWSTR *lpszArgv ) { PPH_STRING portName; diff --git a/phlib/include/apiimport.h b/phlib/include/apiimport.h index 80769e38bc04..973798657c33 100644 --- a/phlib/include/apiimport.h +++ b/phlib/include/apiimport.h @@ -61,7 +61,7 @@ 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 + _In_ ULONG dwFlags ); typedef HRESULT (WINAPI *_SHParseDisplayName)( diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index 1a5c9beabd5b..60ac7efe1267 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -52,7 +52,7 @@ typedef BOOL (WINAPI *_RunFileDlg)( typedef HRESULT (WINAPI *_SHAutoComplete)( _In_ HWND hwndEdit, - _In_ DWORD dwFlags + _In_ ULONG dwFlags ); extern _IsImmersiveProcess IsImmersiveProcess_I; diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index b2c6a1838710..cfaad93639d0 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -112,7 +112,7 @@ PhGetModuleProcAddress( _In_ PSTR ProcName ) { - HMODULE module; + PVOID module; module = PhGetDllHandle(ModuleName); diff --git a/phlib/include/symprvp.h b/phlib/include/symprvp.h index a760e142594e..9d1f309d08f6 100644 --- a/phlib/include/symprvp.h +++ b/phlib/include/symprvp.h @@ -29,15 +29,15 @@ typedef BOOL (WINAPI *_SymEnumSymbolsW)( typedef BOOL (WINAPI *_SymFromAddr)( _In_ HANDLE hProcess, - _In_ DWORD64 Address, - _Out_opt_ PDWORD64 Displacement, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement, _Inout_ PSYMBOL_INFO Symbol ); typedef BOOL (WINAPI *_SymFromAddrW)( _In_ HANDLE hProcess, - _In_ DWORD64 Address, - _Out_opt_ PDWORD64 Displacement, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement, _Inout_ PSYMBOL_INFOW Symbol ); @@ -55,54 +55,54 @@ typedef BOOL (WINAPI *_SymFromNameW)( typedef BOOL (WINAPI *_SymGetLineFromAddr64)( _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr, - _Out_ PDWORD pdwDisplacement, + _In_ ULONG64 dwAddr, + _Out_ PULONG pdwDisplacement, _Out_ PIMAGEHLP_LINE64 Line ); typedef BOOL (WINAPI *_SymGetLineFromAddrW64)( _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr, - _Out_ PDWORD pdwDisplacement, + _In_ ULONG64 dwAddr, + _Out_ PULONG pdwDisplacement, _Out_ PIMAGEHLP_LINEW64 Line ); -typedef DWORD64 (WINAPI *_SymLoadModule64)( +typedef ULONG64 (WINAPI *_SymLoadModule64)( _In_ HANDLE hProcess, _In_opt_ HANDLE hFile, _In_opt_ PCSTR ImageName, _In_opt_ PCSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_ DWORD SizeOfDll + _In_ ULONG64 BaseOfDll, + _In_ ULONG SizeOfDll ); -typedef DWORD64 (WINAPI *_SymLoadModuleExW)( +typedef ULONG64 (WINAPI *_SymLoadModuleExW)( _In_ HANDLE hProcess, _In_ HANDLE hFile, _In_ PCWSTR ImageName, _In_ PCWSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_ DWORD DllSize, + _In_ ULONG64 BaseOfDll, + _In_ ULONG DllSize, _In_ PMODLOAD_DATA Data, - _In_ DWORD Flags + _In_ ULONG Flags ); -typedef DWORD (WINAPI *_SymGetOptions)(); +typedef ULONG (WINAPI *_SymGetOptions)(); -typedef DWORD (WINAPI *_SymSetOptions)( - _In_ DWORD SymOptions +typedef ULONG (WINAPI *_SymSetOptions)( + _In_ ULONG SymOptions ); typedef BOOL (WINAPI *_SymGetSearchPath)( _In_ HANDLE hProcess, _Out_ PSTR SearchPath, - _In_ DWORD SearchPathLength + _In_ ULONG SearchPathLength ); typedef BOOL (WINAPI *_SymGetSearchPathW)( _In_ HANDLE hProcess, _Out_ PWSTR SearchPath, - _In_ DWORD SearchPathLength + _In_ ULONG SearchPathLength ); typedef BOOL (WINAPI *_SymSetSearchPath)( @@ -117,17 +117,17 @@ typedef BOOL (WINAPI *_SymSetSearchPathW)( typedef BOOL (WINAPI *_SymUnloadModule64)( _In_ HANDLE hProcess, - _In_ DWORD64 BaseOfDll + _In_ ULONG64 BaseOfDll ); typedef PVOID (WINAPI *_SymFunctionTableAccess64)( _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase + _In_ ULONG64 AddrBase ); -typedef DWORD64 (WINAPI *_SymGetModuleBase64)( +typedef ULONG64 (WINAPI *_SymGetModuleBase64)( _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr + _In_ ULONG64 dwAddr ); typedef BOOL (WINAPI *_SymRegisterCallbackW64)( @@ -137,7 +137,7 @@ typedef BOOL (WINAPI *_SymRegisterCallbackW64)( ); typedef BOOL (WINAPI *_StackWalk64)( - _In_ DWORD MachineType, + _In_ ULONG MachineType, _In_ HANDLE hProcess, _In_ HANDLE hThread, _Inout_ LPSTACKFRAME64 StackFrame, @@ -150,7 +150,7 @@ typedef BOOL (WINAPI *_StackWalk64)( typedef BOOL (WINAPI *_MiniDumpWriteDump)( _In_ HANDLE hProcess, - _In_ DWORD ProcessId, + _In_ ULONG ProcessId, _In_ HANDLE hFile, _In_ MINIDUMP_TYPE DumpType, _In_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, @@ -167,18 +167,18 @@ typedef BOOL (CALLBACK *_SymbolServerSetOptions)( _In_ ULONG64 data ); -typedef DWORD(WINAPI *_UnDecorateSymbolName)( - _In_ PCSTR DecoratedName, - _Out_ PSTR UnDecoratedName, - _In_ DWORD UndecoratedLength, - _In_ DWORD Flags - ); +typedef ULONG (WINAPI *_UnDecorateSymbolName)( + _In_ PCSTR DecoratedName, + _Out_ PSTR UnDecoratedName, + _In_ ULONG UndecoratedLength, + _In_ ULONG Flags + ); -typedef DWORD(WINAPI *_UnDecorateSymbolNameW)( - _In_ PCWSTR DecoratedName, - _Out_ PWSTR UnDecoratedName, - _In_ DWORD UndecoratedLength, - _In_ DWORD Flags - ); +typedef ULONG (WINAPI *_UnDecorateSymbolNameW)( + _In_ PCWSTR DecoratedName, + _Out_ PWSTR UnDecoratedName, + _In_ ULONG UndecoratedLength, + _In_ ULONG Flags + ); #endif \ No newline at end of file diff --git a/phlib/include/verifyp.h b/phlib/include/verifyp.h index 8a9dbdca2d40..cc308d6758cb 100644 --- a/phlib/include/verifyp.h +++ b/phlib/include/verifyp.h @@ -5,44 +5,44 @@ typedef struct _CATALOG_INFO { - DWORD cbStruct; + ULONG cbStruct; WCHAR wszCatalogFile[MAX_PATH]; } CATALOG_INFO, *PCATALOG_INFO; typedef struct tagCRYPTUI_VIEWSIGNERINFO_STRUCT { - DWORD dwSize; + ULONG dwSize; HWND hwndParent; - DWORD dwFlags; + ULONG dwFlags; LPCTSTR szTitle; CMSG_SIGNER_INFO *pSignerInfo; HCRYPTMSG hMsg; LPCSTR pszOID; - DWORD_PTR dwReserved; - DWORD cStores; + ULONG_PTR dwReserved; + ULONG cStores; HCERTSTORE *rghStores; - DWORD cPropSheetPages; + ULONG cPropSheetPages; LPCPROPSHEETPAGE rgPropSheetPages; } CRYPTUI_VIEWSIGNERINFO_STRUCT, *PCRYPTUI_VIEWSIGNERINFO_STRUCT; typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle)( HANDLE hFile, - DWORD *pcbHash, + ULONG *pcbHash, BYTE *pbHash, - DWORD dwFlags + ULONG dwFlags ); typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle2)( HCATADMIN hCatAdmin, HANDLE hFile, - DWORD *pcbHash, + ULONG *pcbHash, BYTE *pbHash, - DWORD dwFlags + ULONG dwFlags ); typedef BOOL (WINAPI *_CryptCATAdminAcquireContext)( HANDLE *phCatAdmin, GUID *pgSubsystem, - DWORD dwFlags + ULONG dwFlags ); typedef BOOL (WINAPI *_CryptCATAdminAcquireContext2)( @@ -50,32 +50,32 @@ typedef BOOL (WINAPI *_CryptCATAdminAcquireContext2)( const GUID *pgSubsystem, PCWSTR pwszHashAlgorithm, PCCERT_STRONG_SIGN_PARA pStrongHashPolicy, - DWORD dwFlags + ULONG dwFlags ); typedef HANDLE (WINAPI *_CryptCATAdminEnumCatalogFromHash)( HANDLE hCatAdmin, BYTE *pbHash, - DWORD cbHash, - DWORD dwFlags, + ULONG cbHash, + ULONG dwFlags, HANDLE *phPrevCatInfo ); typedef BOOL (WINAPI *_CryptCATCatalogInfoFromContext)( HANDLE hCatInfo, CATALOG_INFO *psCatInfo, - DWORD dwFlags + ULONG dwFlags ); typedef BOOL (WINAPI *_CryptCATAdminReleaseCatalogContext)( HANDLE hCatAdmin, HANDLE hCatInfo, - DWORD dwFlags + ULONG dwFlags ); typedef BOOL (WINAPI *_CryptCATAdminReleaseContext)( HANDLE hCatAdmin, - DWORD dwFlags + ULONG dwFlags ); typedef PCRYPT_PROVIDER_DATA (WINAPI *_WTHelperProvDataFromStateData)( @@ -84,9 +84,9 @@ typedef PCRYPT_PROVIDER_DATA (WINAPI *_WTHelperProvDataFromStateData)( typedef PCRYPT_PROVIDER_SGNR (WINAPI *_WTHelperGetProvSignerFromChain)( CRYPT_PROVIDER_DATA *pProvData, - DWORD idxSigner, + ULONG idxSigner, BOOL fCounterSigner, - DWORD idxCounterSigner + ULONG idxCounterSigner ); typedef LONG (WINAPI *_WinVerifyTrust)( @@ -95,12 +95,12 @@ typedef LONG (WINAPI *_WinVerifyTrust)( LPVOID pWVTData ); -typedef DWORD (WINAPI *_CertNameToStr)( - DWORD dwCertEncodingType, +typedef ULONG (WINAPI *_CertNameToStr)( + ULONG dwCertEncodingType, PCERT_NAME_BLOB pName, - DWORD dwStrType, + ULONG dwStrType, LPTSTR psz, - DWORD csz + ULONG csz ); typedef PCCERT_CONTEXT (WINAPI *_CertDuplicateCertificateContext)( From f74e0359de7615b7f039667a97615267c03dab2a Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 26 Jul 2017 13:01:20 +1000 Subject: [PATCH 285/839] Add missing pointer helper macros --- phlib/include/phsup.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 2be65f1ddb46..f669fb940271 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -16,6 +16,10 @@ #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 From b6093deda4506159957c3519de49004a74f9c3fb Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Jul 2017 12:25:03 +1000 Subject: [PATCH 286/839] Add base address column to String search window --- ProcessHacker/include/memsrch.h | 2 ++ ProcessHacker/memrslt.c | 20 +++++++++++++++++--- ProcessHacker/memsrch.c | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/include/memsrch.h b/ProcessHacker/include/memsrch.h index 5804a8f1f9d9..110d3d901f54 100644 --- a/ProcessHacker/include/memsrch.h +++ b/ProcessHacker/include/memsrch.h @@ -5,6 +5,7 @@ typedef struct _PH_MEMORY_RESULT { LONG RefCount; PVOID Address; + PVOID BaseAddress; SIZE_T Length; PH_STRINGREF Display; } PH_MEMORY_RESULT, *PPH_MEMORY_RESULT; @@ -42,6 +43,7 @@ VOID PhFreeForMemorySearch( PVOID PhCreateMemoryResult( _In_ PVOID Address, + _In_ PVOID BaseAddress, _In_ SIZE_T Length ); diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index cb3c8b38e367..f65c39074769 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -299,8 +299,9 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( 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"); + 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); @@ -531,6 +532,19 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( } 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]; @@ -543,7 +557,7 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( ); } break; - case 2: + case 3: wcsncpy_s( dispInfo->item.pszText, dispInfo->item.cchTextMax, diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index fb22ff01d98a..f558f0aa4fb8 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -124,6 +124,7 @@ VOID PhFreeForMemorySearch( PVOID PhCreateMemoryResult( _In_ PVOID Address, + _In_ PVOID BaseAddress, _In_ SIZE_T Length ) { @@ -136,6 +137,7 @@ PVOID PhCreateMemoryResult( result->RefCount = 1; result->Address = Address; + result->BaseAddress = BaseAddress; result->Length = Length; result->Display.Length = 0; result->Display.Buffer = NULL; @@ -429,6 +431,7 @@ VOID PhSearchMemoryString( if (!(isWide && !detectUnicode) && (result = PhCreateMemoryResult( PTR_ADD_OFFSET(baseAddress, i - bias - lengthInBytes), + baseAddress, lengthInBytes ))) { From 41d92a722d8b30c6cea6f60da8d1ec79b7d68b54 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Jul 2017 15:53:14 +1000 Subject: [PATCH 287/839] Add entrypoint column to process module tab (feature request) https://wj32.org/processhacker/forums/viewtopic.php?f=14&t=1803 --- ProcessHacker/include/modlist.h | 3 ++- ProcessHacker/include/modprv.h | 2 ++ ProcessHacker/modlist.c | 4 ++++ ProcessHacker/modprv.c | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/include/modlist.h b/ProcessHacker/include/modlist.h index 099cf56fd4e3..1e9edb4150f1 100644 --- a/ProcessHacker/include/modlist.h +++ b/ProcessHacker/include/modlist.h @@ -26,8 +26,9 @@ #define PHMOTLC_LOADREASON 15 #define PHMOTLC_FILEMODIFIEDTIME 16 #define PHMOTLC_FILESIZE 17 +#define PHMOTLC_ENTRYPOINT 18 -#define PHMOTLC_MAXIMUM 18 +#define PHMOTLC_MAXIMUM 19 // begin_phapppub typedef struct _PH_MODULE_NODE diff --git a/ProcessHacker/include/modprv.h b/ProcessHacker/include/modprv.h index b08bc9c6697f..05e36ef3b664 100644 --- a/ProcessHacker/include/modprv.h +++ b/ProcessHacker/include/modprv.h @@ -8,6 +8,7 @@ extern PPH_OBJECT_TYPE PhModuleItemType; typedef struct _PH_MODULE_ITEM { PVOID BaseAddress; + PVOID EntryPoint; ULONG Size; ULONG Flags; ULONG Type; @@ -18,6 +19,7 @@ typedef struct _PH_MODULE_ITEM PH_IMAGE_VERSION_INFO VersionInfo; WCHAR BaseAddressString[PH_PTR_STR_LEN_1]; + WCHAR EntryPointAddressString[PH_PTR_STR_LEN_1]; BOOLEAN IsFirst; BOOLEAN JustProcessed; diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index af8adadb31c9..e1c0c967b387 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -114,6 +114,7 @@ VOID PhInitializeModuleList( 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); + PhAddTreeNewColumnEx(hwnd, PHMOTLC_ENTRYPOINT, FALSE, L"Entry point", 70, PH_ALIGN_LEFT, -1, 0, TRUE); TreeNew_SetRedraw(hwnd, TRUE); @@ -857,6 +858,9 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( getCellText->Text = node->FileSizeText->sr; } break; + case PHMOTLC_ENTRYPOINT: + PhInitializeStringRef(&getCellText->Text, moduleItem->EntryPointAddressString); + break; default: return FALSE; } diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index de4854d285c3..b3e15418a019 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -459,6 +459,8 @@ VOID PhModuleProviderUpdate( moduleItem->BaseAddress = module->BaseAddress; PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); + moduleItem->EntryPoint = module->EntryPoint; + PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->EntryPoint); moduleItem->Size = module->Size; moduleItem->Flags = module->Flags; moduleItem->Type = module->Type; From 05f85025295eb9e9e45389f5dba36473450b1ae3 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Jul 2017 18:01:19 +1000 Subject: [PATCH 288/839] Add type filter to the handle search window --- ProcessHacker/ProcessHacker.rc | 13 ++-- ProcessHacker/findobj.c | 130 ++++++++++++++++++++++++++------- ProcessHacker/resource.h | 5 +- 3 files changed, 112 insertions(+), 36 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index bcbe1bc418c3..63339037d300 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -724,11 +724,12 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS 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,224,14,ES_AUTOHSCROLL - PUSHBUTTON "Find",IDOK,300,7,50,14 - CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,25,353,206 - CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,261,9,36,10 + LTEXT "Filter:",IDC_STATIC,7,7,19,8 + EDITTEXT IDC_FILTER,29,4,134,13,ES_AUTOHSCROLL + PUSHBUTTON "Find",IDOK,301,3,50,14 + CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,21,353,210 + CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,6,34,10 + COMBOBOX IDC_FILTERTYPE,165,4,93,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END IDD_OBJTOKEN DIALOGEX 0, 0, 260, 260 @@ -1965,7 +1966,7 @@ BEGIN BEGIN LEFTMARGIN, 2 RIGHTMARGIN, 355 - TOPMARGIN, 7 + TOPMARGIN, 4 BOTTOMMARGIN, 231 END diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 57a673c25e03..e6adb82a4673 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -3,6 +3,7 @@ * object search * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -82,6 +83,7 @@ static RECT MinimumSize; static HANDLE SearchThreadHandle = NULL; static BOOLEAN SearchStop; static PPH_STRING SearchString; +static PPH_STRING SearchTypeString; static pcre2_code *SearchRegexCompiledExpression; static pcre2_match_data *SearchRegexMatchData; static PPH_LIST SearchResults = NULL; @@ -202,6 +204,58 @@ INT NTAPI PhpObjectHandleCompareFunction( return uintptrcmp((ULONG_PTR)item1->Handle, (ULONG_PTR)item2->Handle); } +static int __cdecl PhpStringObjectTypeCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PWSTR entry1 = *(PWSTR *)elem1; + PWSTR entry2 = *(PWSTR *)elem2; + + return PhCompareStringZ(entry2, entry1, FALSE); +} + +static 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, PhDuplicateStringZ(objectType->TypeName.Buffer)); + objectType = PH_NEXT_OBJECT_TYPE(objectType); + } + + PhFree(objectTypes); + } + + // Sort the object types. + qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PWSTR), PhpStringObjectTypeCompare); + + // HACK: Add the object types in reverse order. + for (ULONG i = objectTypeList->Count - 1; i != 0; i--) + { + ComboBox_AddString(FilterTypeCombo, objectTypeList->Items[i]); + PhFree(objectTypeList->Items[i]); + } + + PhDereferenceObject(objectTypeList); +} + static INT_PTR CALLBACK PhpFindObjectsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -223,15 +277,14 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), NULL); + PhpPopulateObjectTypes(GetDlgItem(hwndDlg, IDC_FILTERTYPE)); + 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); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, 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; @@ -261,13 +314,6 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( 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); @@ -276,6 +322,10 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( break; case WM_CLOSE: { + PhSetIntegerSetting(L"FindObjRegex", Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED); + PhSaveWindowPlacementToSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); + PhSaveListViewColumnsToSetting(L"FindObjListViewColumns", PhFindObjectsListViewHandle); + ShowWindow(hwndDlg, SW_HIDE); // IMPORTANT // Set the result to 0 so the default dialog message @@ -309,6 +359,7 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( ULONG i; PhMoveReference(&SearchString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTER))); + PhMoveReference(&SearchTypeString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTERTYPE))); if (SearchRegexCompiledExpression) { @@ -749,6 +800,21 @@ static BOOLEAN MatchSearchString( } } +static BOOLEAN MatchTypeString( + _In_ PPH_STRINGREF Input + ) +{ + if (SearchRegexCompiledExpression && SearchRegexMatchData) + return TRUE; + else + { + if (PhEqualString2(SearchTypeString, L"Everything", FALSE)) + return TRUE; + + return PhFindStringInStringRef(Input, &SearchTypeString->sr, TRUE) != -1; + } +} + typedef struct _SEARCH_HANDLE_CONTEXT { BOOLEAN NeedToFree; @@ -775,11 +841,15 @@ static NTSTATUS NTAPI SearchHandleFunction( ))) { PPH_STRING upperBestObjectName; + PPH_STRING upperTypeName; upperBestObjectName = PhDuplicateString(bestObjectName); _wcsupr(upperBestObjectName->Buffer); - if (MatchSearchString(&upperBestObjectName->sr) || + upperTypeName = PhDuplicateString(typeName); + _wcsupr(upperTypeName->Buffer); + + if ((MatchSearchString(&upperBestObjectName->sr) && MatchTypeString(&upperTypeName->sr)) || (UseSearchPointer && context->HandleInfo->Object == (PVOID)SearchPointer)) { PPHP_OBJECT_SEARCH_RESULT searchResult; @@ -996,22 +1066,26 @@ static NTSTATUS PhpFindObjectsThreadStart( PhFree(handles); } - if (NT_SUCCESS(PhEnumProcesses(&processes))) + if (PhEqualString2(SearchTypeString, L"File", TRUE) || + PhEqualString2(SearchTypeString, L"Everything", FALSE)) { - process = PH_FIRST_PROCESS(processes); - - do + if (NT_SUCCESS(PhEnumProcesses(&processes))) { - PhEnumGenericModules( - process->UniqueProcessId, - NULL, - PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, - EnumModulesCallback, - (PVOID)process->UniqueProcessId + 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)); + } while (process = PH_NEXT_PROCESS(process)); - PhFree(processes); + PhFree(processes); + } } Exit: diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index ad488eaad530..6e5d76cb8a14 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -533,6 +533,7 @@ #define IDC_HANDLESEARCH 1385 #define IDC_SEARCH 1387 #define IDC_FILTEROPTIONS 1389 +#define IDC_FILTERTYPE 1390 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -738,9 +739,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 231 +#define _APS_NEXT_RESOURCE_VALUE 232 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1390 +#define _APS_NEXT_CONTROL_VALUE 1391 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif From ae30c233b17667a51d45d03dd5856898ad6b1ac9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Jul 2017 19:58:02 +1000 Subject: [PATCH 289/839] Fix handle search type filter bug in previous commit --- ProcessHacker/findobj.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index e6adb82a4673..d77d68f9d58b 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -212,7 +212,7 @@ static int __cdecl PhpStringObjectTypeCompare( PWSTR entry1 = *(PWSTR *)elem1; PWSTR entry2 = *(PWSTR *)elem2; - return PhCompareStringZ(entry2, entry1, FALSE); + return PhCompareStringZ(entry1, entry2, TRUE); } static VOID PhpPopulateObjectTypes( @@ -246,8 +246,8 @@ static VOID PhpPopulateObjectTypes( // Sort the object types. qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PWSTR), PhpStringObjectTypeCompare); - // HACK: Add the object types in reverse order. - for (ULONG i = objectTypeList->Count - 1; i != 0; i--) + // Add the types to the object filter combobox. + for (ULONG i = 0; i < objectTypeList->Count; i++) { ComboBox_AddString(FilterTypeCombo, objectTypeList->Items[i]); PhFree(objectTypeList->Items[i]); From 90c8745aff88c63b44a25524c50f2b1bc6ab4ff8 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 27 Jul 2017 20:25:50 +1000 Subject: [PATCH 290/839] Fix single instance regression --- phlib/include/phutil.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index d5dbdb468868..270041ab4c2c 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1062,14 +1062,46 @@ PhGetNamespaceHandle( 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)((PCHAR)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, - NULL + securityDescriptor ); NtCreateDirectoryObject( @@ -1078,6 +1110,8 @@ PhGetNamespaceHandle( &objectAttributes ); + PhFree(securityDescriptor); + PhEndInitOnce(&initOnce); } From ee9f6680aff1343024533c804acb0fa3bf53b3b9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 00:38:06 +1000 Subject: [PATCH 291/839] peview: Fix crash (missing files from commit 634e9a7d6d006021189a143fca24ac7a95fcecf8) --- tools/peview/expprp.c | 4 ++-- tools/peview/impprp.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/peview/expprp.c b/tools/peview/expprp.c index 0688891d8c11..9e50ec4e491e 100644 --- a/tools/peview/expprp.c +++ b/tools/peview/expprp.c @@ -77,7 +77,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( PPH_STRING forwardName = NULL; if (exportFunction.ForwardedName[0] == '?') - forwardName = PhUndecorateName(PvSymbolProvider->ProcessHandle, exportFunction.ForwardedName); + forwardName = PhUndecorateName(PvSymbolProvider, exportFunction.ForwardedName); else forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); @@ -98,7 +98,7 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( PPH_STRING exportName = NULL; if (exportEntry.Name[0] == '?') - exportName = PhUndecorateName(PvSymbolProvider->ProcessHandle, exportEntry.Name); + exportName = PhUndecorateName(PvSymbolProvider, exportEntry.Name); else exportName = PhZeroExtendToUtf16(exportEntry.Name); diff --git a/tools/peview/impprp.c b/tools/peview/impprp.c index 9a1dd38b2256..4decdd8721f6 100644 --- a/tools/peview/impprp.c +++ b/tools/peview/impprp.c @@ -63,7 +63,7 @@ VOID PvpProcessImports( PPH_STRING importName = NULL; if (importEntry.Name[0] == '?') - importName = PhUndecorateName(PvSymbolProvider->ProcessHandle, importEntry.Name); + importName = PhUndecorateName(PvSymbolProvider, importEntry.Name); else importName = PhZeroExtendToUtf16(importEntry.Name); From 421d1bc8fca9380678df2794df11ad6227a67558 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 08:37:52 +1000 Subject: [PATCH 292/839] Fix handle search filter strings --- ProcessHacker/findobj.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index d77d68f9d58b..d8247c987479 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -209,10 +209,10 @@ static int __cdecl PhpStringObjectTypeCompare( _In_ const void *elem2 ) { - PWSTR entry1 = *(PWSTR *)elem1; - PWSTR entry2 = *(PWSTR *)elem2; + PPH_STRING entry1 = *(PPH_STRING *)elem1; + PPH_STRING entry2 = *(PPH_STRING *)elem2; - return PhCompareStringZ(entry1, entry2, TRUE); + return PhCompareString(entry1, entry2, TRUE); } static VOID PhpPopulateObjectTypes( @@ -236,7 +236,7 @@ static VOID PhpPopulateObjectTypes( for (ULONG i = 0; i < objectTypes->NumberOfTypes; i++) { - PhAddItemList(objectTypeList, PhDuplicateStringZ(objectType->TypeName.Buffer)); + PhAddItemList(objectTypeList, PhCreateStringFromUnicodeString(&objectType->TypeName)); objectType = PH_NEXT_OBJECT_TYPE(objectType); } @@ -244,13 +244,13 @@ static VOID PhpPopulateObjectTypes( } // Sort the object types. - qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PWSTR), PhpStringObjectTypeCompare); + qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PVOID), PhpStringObjectTypeCompare); // Add the types to the object filter combobox. for (ULONG i = 0; i < objectTypeList->Count; i++) { - ComboBox_AddString(FilterTypeCombo, objectTypeList->Items[i]); - PhFree(objectTypeList->Items[i]); + ComboBox_AddString(FilterTypeCombo, PhGetString(objectTypeList->Items[i])); + PhDereferenceObject(objectTypeList->Items[i]); } PhDereferenceObject(objectTypeList); From 4ca9317f9fc5df5b7d1eac6449d45f3b28dcc2a8 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 12:37:49 +1000 Subject: [PATCH 293/839] Plugins: Fix TreeNewContextMenu bug --- plugins/DotNetTools/asmpage.c | 4 ++-- plugins/ExtendedTools/disktab.c | 4 ++-- plugins/ExtraPlugins/wndtree.c | 4 ++-- plugins/NetworkTools/tracert.c | 6 +++--- plugins/NetworkTools/tracetree.c | 4 ++-- plugins/WindowExplorer/wndtree.c | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index f21322d1026f..fe12ba0a462d 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -560,9 +560,9 @@ BOOLEAN NTAPI DotNetAsmTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - DotNetAsmShowContextMenu(context, mouseEvent->Location); + DotNetAsmShowContextMenu(context, contextMenuEvent->Location); } return TRUE; } diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index 2ab3ad8c3cd5..20e368f64c87 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -669,9 +669,9 @@ BOOLEAN NTAPI EtpDiskTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - EtShowDiskContextMenu(mouseEvent->Location); + EtShowDiskContextMenu(contextMenuEvent->Location); } return TRUE; case TreeNewDestroying: diff --git a/plugins/ExtraPlugins/wndtree.c b/plugins/ExtraPlugins/wndtree.c index 0bdeced96e21..ce624e5b847f 100644 --- a/plugins/ExtraPlugins/wndtree.c +++ b/plugins/ExtraPlugins/wndtree.c @@ -273,9 +273,9 @@ BOOLEAN NTAPI PluginsTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y)); + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(contextMenuEvent->Location.x, contextMenuEvent->Location.y)); } return TRUE; case TreeNewHeaderRightClick: diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 9e4bb57c0279..f5b2c5376733 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -614,7 +614,7 @@ INT_PTR CALLBACK TracertDlgProc( PPH_EMENU menu; PTRACERT_ROOT_NODE selectedNode; PPH_EMENU_ITEM selectedItem; - PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)lParam; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; if (selectedNode = GetSelectedTracertNode(context)) { @@ -628,8 +628,8 @@ INT_PTR CALLBACK TracertDlgProc( hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, - mouseEvent->Location.x, - mouseEvent->Location.y + contextMenuEvent->Location.x, + contextMenuEvent->Location.y ); if (selectedItem && selectedItem->Id != -1) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 5b1a945edcc8..fb4d6f55a818 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -352,13 +352,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; diff --git a/plugins/WindowExplorer/wndtree.c b/plugins/WindowExplorer/wndtree.c index a18b545b90e5..c1d9d02476da 100644 --- a/plugins/WindowExplorer/wndtree.c +++ b/plugins/WindowExplorer/wndtree.c @@ -479,9 +479,9 @@ BOOLEAN NTAPI WepWindowTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y)); + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(contextMenuEvent->Location.x, contextMenuEvent->Location.y)); } return TRUE; } From 0c3cd4514cda30bb3a055814755ad9630fab59a4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 12:40:52 +1000 Subject: [PATCH 294/839] Fix treeview clipboard issue caused by PhResolveDevicePrefix and extra-null terminated strings --- phlib/native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phlib/native.c b/phlib/native.c index 0a6acd15f6b1..7be1079480cb 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5028,6 +5028,8 @@ PPH_STRING PhResolveDevicePrefix( PhReleaseQueuedLockShared(&PhDeviceMupPrefixesLock); } + PhTrimToNullTerminatorString(newName); + return newName; } From a11b40ec3eeb621082f2af42cd9423f94bb630fb Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 12:52:42 +1000 Subject: [PATCH 295/839] Export the PhInsertCopyCellEMenuItem and PhHandleCopyCellEMenuItem functions --- ProcessHacker/include/appsup.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 1843bae473ad..16d1762462da 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -393,7 +393,6 @@ NTAPI PhApplyTreeNewFilters( _In_ PPH_TN_FILTER_SUPPORT Support ); -// end_phapppub typedef struct _PH_COPY_CELL_CONTEXT { @@ -402,16 +401,23 @@ 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 ); +// end_phapppub BOOLEAN PhShellOpenKey2( _In_ HWND hWnd, From a27cfbab9f6f88c6f9256115518e1110cfc27129 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 13:17:08 +1000 Subject: [PATCH 296/839] Fix crash from commit 0c3cd4514cda30bb3a055814755ad9630fab59a4 --- phlib/native.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phlib/native.c b/phlib/native.c index 7be1079480cb..2817a8e7fb01 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5028,7 +5028,8 @@ PPH_STRING PhResolveDevicePrefix( PhReleaseQueuedLockShared(&PhDeviceMupPrefixesLock); } - PhTrimToNullTerminatorString(newName); + if (newName) + PhTrimToNullTerminatorString(newName); return newName; } From a96187247225b391babab78ea2c2278760e39513 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 14:56:32 +1000 Subject: [PATCH 297/839] Fix modules tab not showing signatures for mapped images when they're actually signed (reported by Zorkov Igor) https://wj32.org/processhacker/forums/viewtopic.php?t=2428 --- ProcessHacker/modprv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index b3e15418a019..9f5f1f623ba9 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -535,8 +535,6 @@ VOID PhModuleProviderUpdate( 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. From b5d3ac21ec6d64ccf6b4809b33c9a59788e869d4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 15:20:22 +1000 Subject: [PATCH 298/839] Plugins: Add missing copy menus to the Disk, .NET, Tracert and Windows tabs, Fix remaining contextmenu issues --- plugins/DotNetTools/asmpage.c | 42 +++++++++++++++++----------- plugins/ExtendedTools/disktab.c | 17 +++++++---- plugins/ExtendedTools/disktabp.h | 3 +- plugins/ExtraPlugins/dialog.c | 8 ++---- plugins/ExtraPlugins/wndtree.c | 2 +- plugins/NetworkTools/nettools.h | 1 + plugins/NetworkTools/tracert.c | 21 +++++++++++++- plugins/WindowExplorer/wnddlg.c | 48 +++++++++++++++++++++++++------- plugins/WindowExplorer/wndtree.c | 2 +- 9 files changed, 103 insertions(+), 41 deletions(-) diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index fe12ba0a462d..df80c57ed190 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -393,7 +393,7 @@ PDNA_NODE DotNetAsmGetSelectedEntry( VOID DotNetAsmShowContextMenu( _In_ PASMPAGE_CONTEXT Context, - _In_ POINT Location + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent ) { PDNA_NODE node; @@ -405,6 +405,7 @@ VOID DotNetAsmShowContextMenu( menu = PhCreateEMenu(); PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_ASSEMBLY_MENU), 0); + PhInsertCopyCellEMenuItem(menu, ID_CLR_COPY, Context->TnHandle, ContextMenuEvent->Column); if (PhIsNullOrEmptyString(node->PathText) || !RtlDoesFileExists_U(node->PathText->Buffer)) { @@ -416,31 +417,38 @@ VOID DotNetAsmShowContextMenu( Context->WindowHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, - Location.x, - Location.y + ContextMenuEvent->Location.x, + ContextMenuEvent->Location.y ); if (selectedItem && selectedItem->Id != -1) { - switch (selectedItem->Id) + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + + if (!handled) { - case ID_CLR_OPENFILELOCATION: + switch (selectedItem->Id) { - if (!PhIsNullOrEmptyString(node->PathText) && RtlDoesFileExists_U(node->PathText->Buffer)) + case ID_CLR_OPENFILELOCATION: { - PhShellExploreFile(Context->WindowHandle, node->PathText->Buffer); + if (!PhIsNullOrEmptyString(node->PathText) && RtlDoesFileExists_U(node->PathText->Buffer)) + { + PhShellExploreFile(Context->WindowHandle, node->PathText->Buffer); + } } - } - break; - case ID_CLR_COPY: - { - PPH_STRING text; + break; + case ID_CLR_COPY: + { + PPH_STRING text; - text = PhGetTreeNewText(Context->TnHandle, 0); - PhSetClipboardString(Context->TnHandle, &text->sr); - PhDereferenceObject(text); + text = PhGetTreeNewText(Context->TnHandle, 0); + PhSetClipboardString(Context->TnHandle, &text->sr); + PhDereferenceObject(text); + } + break; } - break; } } @@ -562,7 +570,7 @@ BOOLEAN NTAPI DotNetAsmTreeNewCallback( { PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - DotNetAsmShowContextMenu(context, contextMenuEvent->Location); + DotNetAsmShowContextMenu(context, contextMenuEvent); } return TRUE; } diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index 20e368f64c87..f669e3cc93ae 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -671,7 +671,7 @@ BOOLEAN NTAPI EtpDiskTreeNewCallback( { PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - EtShowDiskContextMenu(contextMenuEvent->Location); + EtShowDiskContextMenu(hwnd, contextMenuEvent); } return TRUE; case TreeNewDestroying: @@ -916,7 +916,8 @@ VOID EtpInitializeDiskMenu( } VOID EtShowDiskContextMenu( - _In_ POINT Location + _In_ HWND TreeWindowHandle, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent ) { PET_DISK_ITEM *diskItems; @@ -931,6 +932,7 @@ VOID EtShowDiskContextMenu( 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); @@ -940,13 +942,18 @@ VOID EtShowDiskContextMenu( PhMainWndHandle, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, - Location.x, - Location.y + ContextMenuEvent->Location.x, + ContextMenuEvent->Location.y ); if (item) { - EtHandleDiskCommand(item->Id); + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(item); + + if (!handled) + EtHandleDiskCommand(item->Id); } PhDestroyEMenu(menu); diff --git a/plugins/ExtendedTools/disktabp.h b/plugins/ExtendedTools/disktabp.h index 794572c90e7c..0a90f64716ef 100644 --- a/plugins/ExtendedTools/disktabp.h +++ b/plugins/ExtendedTools/disktabp.h @@ -107,7 +107,8 @@ VOID EtpInitializeDiskMenu( ); VOID EtShowDiskContextMenu( - _In_ POINT Location + _In_ HWND TreeWindowHandle, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent ); VOID NTAPI EtpDiskItemAddedHandler( diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index 829a96ed8c49..50ca89609f1e 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -371,12 +371,10 @@ INT_PTR CALLBACK CloudPluginsDlgProc( break; case ID_WCTSHOWCONTEXTMENU: { - POINT cursorPos; PPH_EMENU menu; PPH_EMENU_ITEM selectedItem; PPLUGIN_NODE selectedNode; - - GetCursorPos(&cursorPos); + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; if (!(selectedNode = WeGetSelectedWindowNode(context))) break; @@ -448,8 +446,8 @@ INT_PTR CALLBACK CloudPluginsDlgProc( hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, - cursorPos.x, - cursorPos.y + contextMenuEvent->Location.x, + contextMenuEvent->Location.y ); if (selectedItem && selectedItem->Id != -1) diff --git a/plugins/ExtraPlugins/wndtree.c b/plugins/ExtraPlugins/wndtree.c index ce624e5b847f..5c9858fd1b49 100644 --- a/plugins/ExtraPlugins/wndtree.c +++ b/plugins/ExtraPlugins/wndtree.c @@ -275,7 +275,7 @@ BOOLEAN NTAPI PluginsTreeNewCallback( { PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(contextMenuEvent->Location.x, contextMenuEvent->Location.y)); + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, (LPARAM)contextMenuEvent); } return TRUE; case TreeNewHeaderRightClick: diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index d2ae5088edf9..106eb2849428 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -95,6 +95,7 @@ typedef enum _PH_NETWORK_ACTION MAINMENU_ACTION_TRACERT, MAINMENU_ACTION_WHOIS, MAINMENU_ACTION_GEOIP_UPDATE, + MENU_ACTION_COPY, } PH_NETWORK_ACTION; #define NTM_RECEIVEDTRACE (WM_APP + NETWORK_ACTION_TRACEROUTE) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index f5b2c5376733..62f39daef461 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -518,6 +518,15 @@ VOID ShowMenu( } } break; + case MENU_ACTION_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(Context->TreeNewHandle, 0); + PhSetClipboardString(Context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; } } @@ -622,6 +631,9 @@ INT_PTR CALLBACK TracertDlgProc( 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); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MENU_ACTION_COPY, L"Copy", NULL, NULL), -1); + PhInsertCopyCellEMenuItem(menu, MENU_ACTION_COPY, context->TreeNewHandle, contextMenuEvent->Column); selectedItem = PhShowEMenu( menu, @@ -634,7 +646,14 @@ INT_PTR CALLBACK TracertDlgProc( if (selectedItem && selectedItem->Id != -1) { - ShowMenu(context, selectedItem->Id); + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + + if (!handled) + { + ShowMenu(context, selectedItem->Id); + } } PhDestroyEMenu(menu); diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index ab97179b6f24..9aa47dd23c1f 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -432,13 +432,11 @@ INT_PTR CALLBACK WepWindowsDlgProc( break; case ID_SHOWCONTEXTMENU: { - POINT point; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; PWE_WINDOW_NODE *windows; ULONG numberOfWindows; PPH_EMENU menu; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); + PPH_EMENU_ITEM selectedItem; WeGetSelectedWindowNodes( &context->TreeContext, @@ -450,6 +448,7 @@ INT_PTR CALLBACK WepWindowsDlgProc( { 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) @@ -529,7 +528,22 @@ INT_PTR CALLBACK WepWindowsDlgProc( 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); + 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 != -1) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + PhDestroyEMenu(menu); } } @@ -854,13 +868,11 @@ INT_PTR CALLBACK WepWindowsPageProc( break; case ID_SHOWCONTEXTMENU: { - POINT point; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; PWE_WINDOW_NODE *windows; ULONG numberOfWindows; PPH_EMENU menu; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); + PPH_EMENU selectedItem; WeGetSelectedWindowNodes( &context->TreeContext, @@ -872,6 +884,7 @@ INT_PTR CALLBACK WepWindowsPageProc( { 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) @@ -951,7 +964,22 @@ INT_PTR CALLBACK WepWindowsPageProc( 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); + 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 != -1) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + PhDestroyEMenu(menu); } } diff --git a/plugins/WindowExplorer/wndtree.c b/plugins/WindowExplorer/wndtree.c index c1d9d02476da..8b7c3e56bd09 100644 --- a/plugins/WindowExplorer/wndtree.c +++ b/plugins/WindowExplorer/wndtree.c @@ -481,7 +481,7 @@ BOOLEAN NTAPI WepWindowTreeNewCallback( { PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(contextMenuEvent->Location.x, contextMenuEvent->Location.y)); + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenuEvent); } return TRUE; } From e3a3f037d49e1ff63ee295d1ee8dfb4d5811ff6e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 15:21:05 +1000 Subject: [PATCH 299/839] Improve KPH error checking --- phlib/kph.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/phlib/kph.c b/phlib/kph.c index 0acd7d0baa27..85e996a0c533 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -25,8 +25,8 @@ #include HANDLE PhKphHandle = NULL; -BOOLEAN PhKphVerified; -KPH_KEY PhKphL1Key; +BOOLEAN PhKphVerified = FALSE; +KPH_KEY PhKphL1Key = 0; NTSTATUS KphConnect( _In_opt_ PWSTR DeviceName @@ -196,13 +196,19 @@ NTSTATUS KphConnect2Ex( if (StartService(serviceHandle, 0, NULL)) started = TRUE; + else + status = PhGetLastWin32ErrorAsNtStatus(); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); } CloseServiceHandle(scmHandle); } } - if (started) + if (NT_SUCCESS(status) && started) { // Try to open the device again. status = KphConnect(fullDeviceName); From a2a36215170b59911d9429b9160de60089443939 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 16:15:25 +1000 Subject: [PATCH 300/839] Fix previous commit --- phlib/kph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/kph.c b/phlib/kph.c index 85e996a0c533..e27f622ca57b 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -208,7 +208,7 @@ NTSTATUS KphConnect2Ex( } } - if (NT_SUCCESS(status) && started) + if (started) { // Try to open the device again. status = KphConnect(fullDeviceName); From 2182ff8b6bdfedc15be409d0d62bdb45d95ecd6c Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 19:35:37 +1000 Subject: [PATCH 301/839] Update readme.md --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++---------- README.txt | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 README.txt diff --git a/README.md b/README.md index 38037e452c14..051fbf9c326c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,58 @@ -Process Hacker is a powerful free and open source process viewer. +##Process Hacker -## Getting started +A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. -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. +[![Build status](https://ci.appveyor.com/api/projects/status/5einmgmy3mnsfjdn?svg=true)](https://ci.appveyor.com/project/pbatard/rufus) +[![Licence](https://img.shields.io/badge/license-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) + +![Logo](https://raw.githubusercontent.com/processhacker2/processhacker/master/ProcessHacker/resources/ProcessHacker.png) + +* [Official Website](http://processhacker.sourceforge.net/) +* [FAQ](http://processhacker.sourceforge.net/faq.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 +73,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 +89,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 @@ -59,3 +99,4 @@ for all processes when it is not running as administrator: 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). + \ No newline at end of file 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). From 98810f309786f5f3b0f6e07639393560e9ebfd76 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 19:37:07 +1000 Subject: [PATCH 302/839] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 051fbf9c326c..178bf550becd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -##Process Hacker +## Process Hacker A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. @@ -99,4 +99,4 @@ to show details for all processes when it is not running as administrator: 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). - \ No newline at end of file + From 0758700b4265c1845417f26b86a64cd569edd7f4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 19:42:04 +1000 Subject: [PATCH 303/839] BuildTools: Update readme location, Improve website changelog format --- tools/CustomBuildTool/Source Files/Build.cs | 6 +++--- .../bin/Release/CustomBuildTool.exe | Bin 160256 -> 160256 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index f915c51c8408..a75528a64197 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -299,7 +299,7 @@ public static bool CopyTextFiles() { try { - Win32.CopyIfNewer("README.md", "bin\\README.txt"); + Win32.CopyIfNewer("README.txt", "bin\\README.txt"); Win32.CopyIfNewer("CHANGELOG.txt", "bin\\CHANGELOG.txt"); Win32.CopyIfNewer("COPYRIGHT.txt", "bin\\COPYRIGHT.txt"); Win32.CopyIfNewer("LICENSE.txt", "bin\\LICENSE.txt"); @@ -872,8 +872,8 @@ public static void WebServiceUpdateConfig() if (string.IsNullOrEmpty(BuildSetupSig)) return; - string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\""); - string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); + string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\""); + string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\" --abbrev-commit"); string buildPostString = Json.Serialize(new BuildUpdateRequest { Updated = TimeStart.ToString("o"), diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index ac40ae99e063999800bb18b2a9740a8b0b41da61..7ec0fe7844142e97d27ca7fde66e3e1a96e1c9df 100644 GIT binary patch delta 6361 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 delta 6386 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 From 6f259a37973b3747b448e4e1438bc89828248d7f Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 19:44:12 +1000 Subject: [PATCH 304/839] Add support for searching handle values --- ProcessHacker/findobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index d8247c987479..602f39befab9 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -850,7 +850,7 @@ static NTSTATUS NTAPI SearchHandleFunction( _wcsupr(upperTypeName->Buffer); if ((MatchSearchString(&upperBestObjectName->sr) && MatchTypeString(&upperTypeName->sr)) || - (UseSearchPointer && context->HandleInfo->Object == (PVOID)SearchPointer)) + (UseSearchPointer && (context->HandleInfo->Object == (PVOID)SearchPointer || context->HandleInfo->HandleValue == SearchPointer))) { PPHP_OBJECT_SEARCH_RESULT searchResult; From 8da8e0f9ad88dec6c0c810f8e6d742951c85fe04 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 20:10:42 +1000 Subject: [PATCH 305/839] Add contextmenu to memory search results window --- ProcessHacker/memrslt.c | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index f65c39074769..9f9bc09b2138 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -621,6 +621,116 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( } } 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), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), -1); + 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); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)lvHandle, TRUE); + } + break; + } + } + + PhDestroyEMenu(menu); + } + } + break; } } break; From e980e6d5eb599a4b24e1f2ba726cfe4b54bcf9c4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 20:23:07 +1000 Subject: [PATCH 306/839] Update copyright --- ProcessHacker/memrslt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index 9f9bc09b2138..ba3b0750c547 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -3,6 +3,7 @@ * memory search results * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * From 0a0a1b16f5811091ed47e2202a1ea58fcf181e24 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 28 Jul 2017 21:18:42 +1000 Subject: [PATCH 307/839] Add paramater columns to the thread stack window --- ProcessHacker/thrdstk.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 050eaf9267ff..0ca0d75d0ef3 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -3,6 +3,7 @@ * thread stack viewer * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -180,8 +181,16 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( PhDereferenceObject(title); lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 30, L" "); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 30, L"#"); PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 300, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 50, L"Stack"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 50, L"Frame"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 50, L"Parameter #1"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 50, L"Parameter #2"); + PhAddListViewColumn(lvHandle, 6, 6, 6, LVCFMT_LEFT, 50, L"Parameter #3"); + PhAddListViewColumn(lvHandle, 7, 7, 7, LVCFMT_LEFT, 50, L"Parameter #4"); + PhAddListViewColumn(lvHandle, 8, 8, 8, LVCFMT_LEFT, 50, L"Address"); + PhAddListViewColumn(lvHandle, 9, 9, 9, LVCFMT_LEFT, 50, L"Return Address"); PhSetListViewStyle(lvHandle, FALSE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhLoadListViewColumnsFromSetting(L"ThreadStackListViewColumns", lvHandle); @@ -461,10 +470,39 @@ static NTSTATUS PhpRefreshThreadStack( PTHREAD_STACK_ITEM item = ThreadStackContext->List->Items[i]; INT lvItemIndex; WCHAR integerString[PH_INT32_STR_LEN_1]; + WCHAR addressString[PH_PTR_STR_LEN_1]; PhPrintUInt32(integerString, item->Index); lvItemIndex = PhAddListViewItem(ThreadStackContext->ListViewHandle, MAXINT, integerString, item); PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 1, PhGetStringOrDefault(item->Symbol, L"???")); + + PhPrintPointer(addressString, item->StackFrame.StackAddress); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 2, addressString); + + PhPrintPointer(addressString, item->StackFrame.FrameAddress); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 3, addressString); + + // There are no params for kernel-mode stack traces. + if ((ULONG_PTR)item->StackFrame.PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) + { + PhPrintPointer(addressString, item->StackFrame.Params[0]); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 4, addressString); + + PhPrintPointer(addressString, item->StackFrame.Params[1]); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 5, addressString); + + PhPrintPointer(addressString, item->StackFrame.Params[2]); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 6, addressString); + + PhPrintPointer(addressString, item->StackFrame.Params[3]); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 7, addressString); + } + + PhPrintPointer(addressString, item->StackFrame.PcAddress); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 8, addressString); + + PhPrintPointer(addressString, item->StackFrame.ReturnAddress); + PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 9, addressString); } SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, TRUE, 0); From 9d3b37aaf39f2ad3b8224552f201e1432d9a50f1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 00:46:51 +1000 Subject: [PATCH 308/839] NetworkTools: Fix string usage --- plugins/NetworkTools/tracetree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index fb4d6f55a818..12aa70e1265c 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -223,7 +223,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 { From 1bccd249695593fee3fde4383caa1b8238bea74a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 02:02:12 +1000 Subject: [PATCH 309/839] Fix missing ImageSubsystem for WSL processes, Remove extra handle for querying the window title --- ProcessHacker/proctree.c | 71 +++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index e59e448ffab8..a6cd292c6846 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1031,40 +1031,47 @@ static VOID PhpUpdateProcessNodeImage( PVOID imageBaseAddress; PH_REMOTE_MAPPED_IMAGE mappedImage; - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) + if (ProcessNode->ProcessItem->IsSubsystemProcess) { - if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo))) + ProcessNode->ImageSubsystem = IMAGE_SUBSYSTEM_POSIX_CUI; + } + else + { + if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) { - if (NT_SUCCESS(NtReadVirtualMemory( - processHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageBaseAddress)), - &imageBaseAddress, - sizeof(PVOID), - NULL - ))) + if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) { - if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage))) + if (NT_SUCCESS(NtReadVirtualMemory( + processHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageBaseAddress)), + &imageBaseAddress, + sizeof(PVOID), + NULL + ))) { - ProcessNode->ImageTimeDateStamp = mappedImage.NtHeaders->FileHeader.TimeDateStamp; - ProcessNode->ImageCharacteristics = mappedImage.NtHeaders->FileHeader.Characteristics; - - if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage))) { - ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->Subsystem; - ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; + 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); } - 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); + NtClose(processHandle); + } } ProcessNode->ValidMask |= PHPN_IMAGE; @@ -1077,20 +1084,13 @@ static VOID PhpUpdateProcessNodeAppId( { 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 (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessNode->ProcessId))) - goto Done; - } - - if (NT_SUCCESS(PhGetProcessWindowTitle( - processHandle, + if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(PhGetProcessWindowTitle( + ProcessNode->ProcessItem->QueryHandle, &windowFlags, &windowTitle ))) @@ -1101,9 +1101,6 @@ static VOID PhpUpdateProcessNodeAppId( PhDereferenceObject(windowTitle); } - NtClose(processHandle); - -Done: ProcessNode->ValidMask |= PHPN_APPID; } } From c68d1f3e2a7b23ba1c6cfcf668c768fc6de5ec19 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 02:19:50 +1000 Subject: [PATCH 310/839] Fix module tab verification status text --- ProcessHacker/modlist.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index e1c0c967b387..1e77f5273cd8 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -662,7 +662,7 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( switch (getCellText->Id) { case PHMOTLC_NAME: - getCellText->Text = moduleItem->Name->sr; + getCellText->Text = PhGetStringRef(moduleItem->Name); break; case PHMOTLC_BASEADDRESS: PhInitializeStringRefLongHint(&getCellText->Text, moduleItem->BaseAddressString); @@ -733,16 +733,8 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( } 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); - } + PhInitializeStringRef(&getCellText->Text, + moduleItem->VerifyResult == VrTrusted ? L"Trusted" : L"Not trusted"); break; case PHMOTLC_VERIFIEDSIGNER: getCellText->Text = PhGetStringRef(moduleItem->VerifySignerName); From ce2a8fdf3f51e263e116d7fe23e212fa32e076df Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 04:21:38 +1000 Subject: [PATCH 311/839] Fix Windows 7 issue with PhCreatePipe --- phlib/native.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 2817a8e7fb01..03c4812f065e 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6208,7 +6208,7 @@ NTSTATUS PhCreatePipe( ) { NTSTATUS status; - PACL pipeAcl; + PACL pipeAcl = NULL; HANDLE pipeDirectoryHandle; HANDLE pipeReadHandle; HANDLE pipeWriteHandle; @@ -6253,7 +6253,6 @@ NTSTATUS PhCreatePipe( RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); - RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); oa.SecurityDescriptor = &securityDescriptor; } @@ -6277,6 +6276,9 @@ NTSTATUS PhCreatePipe( if (!NT_SUCCESS(status)) { + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + NtClose(pipeDirectoryHandle); return status; } @@ -6305,6 +6307,9 @@ NTSTATUS PhCreatePipe( *PipeWriteHandle = pipeWriteHandle; } + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + NtClose(pipeDirectoryHandle); return status; } From 3bb7b67292f7b4384434414a3d87497bdc3825e1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 05:29:25 +1000 Subject: [PATCH 312/839] Fix analyzing thread wait on win7 and win8 --- ProcessHacker/anawait.c | 2 +- phnt/include/ntpsapi.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index 7f1f75b4bf1b..6e8d9f0f957b 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -222,7 +222,7 @@ VOID PhpAnalyzeWaitPassive( ))) { NtClose(threadHandle); - PhShowInformation(hWnd, L"Unable to determine whether the thread is waiting."); + PhShowStatus(hWnd, L"Unable to determine whether the thread is waiting.", status, 0); return; } diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 1168592532ab..1be1843f6e1f 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -756,8 +756,8 @@ typedef struct _THREAD_LAST_SYSCALL_INFORMATION { PVOID FirstArgument; USHORT SystemCallNumber; - USHORT Reserved; // since REDSTONE2 - ULONG64 WaitTime; + //USHORT Reserved; // since REDSTONE2 + //ULONG64 WaitTime; } THREAD_LAST_SYSCALL_INFORMATION, *PTHREAD_LAST_SYSCALL_INFORMATION; // private From 43ec569448f56e7d0b44b937d0e2802c9bcbc5da Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 05:37:14 +1000 Subject: [PATCH 313/839] Rewrite thread stack window: Add treelist control, Add column customization, Add right-click menu, Add column sorting --- ProcessHacker/ProcessHacker.rc | 18 +- ProcessHacker/resource.h | 5 +- ProcessHacker/settings.c | 4 +- ProcessHacker/thrdstk.c | 935 +++++++++++++++++++++++++-------- 4 files changed, 722 insertions(+), 240 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 63339037d300..2261c520d25c 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -618,15 +618,15 @@ BEGIN PUSHBUTTON "Delete",IDC_DELETE,203,239,50,14 END -IDD_THRDSTACK DIALOGEX 0, 0, 261, 228 +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 - 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 + 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 @@ -1920,10 +1920,10 @@ BEGIN IDD_THRDSTACK, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 254 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 + LEFTMARGIN, 2 + RIGHTMARGIN, 271 + TOPMARGIN, 2 + BOTTOMMARGIN, 226 END IDD_ABOUT, DIALOG diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 6e5d76cb8a14..00012e75a9d9 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -534,6 +534,7 @@ #define IDC_SEARCH 1387 #define IDC_FILTEROPTIONS 1389 #define IDC_FILTERTYPE 1390 +#define IDC_TREELIST 1391 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -739,9 +740,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 232 +#define _APS_NEXT_RESOURCE_VALUE 233 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1391 +#define _APS_NEXT_CONTROL_VALUE 1392 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 55528bca1a76..9ad41d1257c8 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -145,8 +145,8 @@ VOID PhAddDefaultSettings( 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"); + PhpAddStringSetting(L"ThreadStackTreeListColumns", L""); + PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,400"); PhpAddStringSetting(L"TokenGroupsListViewColumns", L""); PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 0ca0d75d0ef3..6406cbdad01a 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -23,23 +23,28 @@ #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) -typedef struct _THREAD_STACK_CONTEXT +static RECT MinimumSize = { -1, -1, -1, -1 }; + +typedef struct _PH_THREAD_STACK_CONTEXT { HANDLE ProcessId; HANDLE ThreadId; HANDLE ThreadHandle; - HWND ListViewHandle; PPH_THREAD_PROVIDER ThreadProvider; PPH_SYMBOL_PROVIDER SymbolProvider; BOOLEAN CustomWalk; @@ -51,7 +56,17 @@ typedef struct _THREAD_STACK_CONTEXT NTSTATUS WalkStatus; PPH_STRING StatusMessage; PH_QUEUED_LOCK StatusLock; -} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT; + + PH_LAYOUT_MANAGER LayoutManager; + + HWND WindowHandle; + HWND TreeNewHandle; + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + PPH_LIST NodeRootList; +} PH_THREAD_STACK_CONTEXT, *PPH_THREAD_STACK_CONTEXT; typedef struct _THREAD_STACK_ITEM { @@ -60,6 +75,43 @@ typedef struct _THREAD_STACK_ITEM 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, @@ -73,7 +125,7 @@ VOID PhpFreeThreadStackItem( NTSTATUS PhpRefreshThreadStack( _In_ HWND hwnd, - _In_ PTHREAD_STACK_CONTEXT ThreadStackContext + _In_ PPH_THREAD_STACK_CONTEXT ThreadStackContext ); INT_PTR CALLBACK PhpThreadStackProgressDlgProc( @@ -83,7 +135,526 @@ INT_PTR CALLBACK PhpThreadStackProgressDlgProc( _In_ LPARAM lParam ); -static RECT MinimumSize = { -1, -1, -1, -1 }; +#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 + ) +{ + PhClearReference(&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)) != -1) + { + 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; + + 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 %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 = 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, PH_STACK_TREE_COLUMN_INDEX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_SYMBOL, TRUE, L"Name", 250, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_SYMBOL, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_STACKADDRESS, FALSE, L"Stack address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_STACKADDRESS, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_FRAMEADDRESS, FALSE, L"Frame address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_FRAMEADDRESS, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER1, FALSE, L"Stack parameter #1", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER2, FALSE, L"Stack parameter #2", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER2, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER3, FALSE, L"Stack parameter #3", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER3, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER4, FALSE, L"Stack parameter #4", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER4, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_CONTROLADDRESS, TRUE, L"Control address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_CONTROLADDRESS, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_RETURNADDRESS, FALSE, L"Return address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_RETURNADDRESS, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + + 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 PhShowThreadStackDialog( _In_ HWND ParentWindowHandle, @@ -93,7 +664,7 @@ VOID PhShowThreadStackDialog( ) { NTSTATUS status; - THREAD_STACK_CONTEXT threadStackContext; + PH_THREAD_STACK_CONTEXT threadStackContext; HANDLE threadHandle = NULL; // If the user is trying to view a system thread stack @@ -104,7 +675,7 @@ VOID PhShowThreadStackDialog( return; } - memset(&threadStackContext, 0, sizeof(THREAD_STACK_CONTEXT)); + memset(&threadStackContext, 0, sizeof(PH_THREAD_STACK_CONTEXT)); threadStackContext.ProcessId = ProcessId; threadStackContext.ThreadId = ThreadId; threadStackContext.ThreadProvider = ThreadProvider; @@ -160,55 +731,42 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( _In_ LPARAM lParam ) { + PPH_THREAD_STACK_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PPH_THREAD_STACK_CONTEXT)lParam; + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPH_THREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + } + + if (!context) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { NTSTATUS status; - PTHREAD_STACK_CONTEXT threadStackContext; - PPH_STRING title; - HWND lvHandle; - PPH_LAYOUT_MANAGER layoutManager; + + 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))); - 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"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 50, L"Stack"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 50, L"Frame"); - PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 50, L"Parameter #1"); - PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 50, L"Parameter #2"); - PhAddListViewColumn(lvHandle, 6, 6, 6, LVCFMT_LEFT, 50, L"Parameter #3"); - PhAddListViewColumn(lvHandle, 7, 7, 7, LVCFMT_LEFT, 50, L"Parameter #4"); - PhAddListViewColumn(lvHandle, 8, 8, 8, LVCFMT_LEFT, 50, L"Address"); - PhAddListViewColumn(lvHandle, 9, 9, 9, LVCFMT_LEFT, 50, L"Return Address"); - 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); + SetWindowText(hwndDlg, PhaFormatString(L"Stack - thread %u", 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) { @@ -231,18 +789,18 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( 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.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); - threadStackContext->CustomWalk = control.u.Initializing.CustomWalk; + context->CustomWalk = control.u.Initializing.CustomWalk; } - status = PhpRefreshThreadStack(hwndDlg, threadStackContext); + status = PhpRefreshThreadStack(hwndDlg, context); if (status == STATUS_ABANDONED) EndDialog(hwndDlg, IDCANCEL); @@ -252,33 +810,26 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( break; case WM_DESTROY: { - PPH_LAYOUT_MANAGER layoutManager; - PTHREAD_STACK_CONTEXT threadStackContext; - ULONG i; + context->StopWalk = TRUE; - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); + DeleteThreadStackTree(context); - threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + PhDeleteLayoutManager(&context->LayoutManager); if (PhPluginsEnabled) { PH_PLUGIN_THREAD_STACK_CONTROL control; - control.Type = PluginThreadStackUninitializing; - control.UniqueKey = threadStackContext; + control.UniqueKey = context; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); } - for (i = 0; i < threadStackContext->List->Count; i++) - PhpFreeThreadStackItem(threadStackContext->List->Items[i]); + for (ULONG i = 0; i < context->List->Count; i++) + PhpFreeThreadStackItem(context->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: @@ -295,10 +846,7 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( { NTSTATUS status; - if (!NT_SUCCESS(status = PhpRefreshThreadStack( - hwndDlg, - (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()) - ))) + if (!NT_SUCCESS(status = PhpRefreshThreadStack(hwndDlg, context))) { PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); } @@ -306,103 +854,43 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( break; case IDC_COPY: { - HWND lvHandle; + PPH_STRING text; - 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); + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); } break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case LVN_GETINFOTIP: + case WM_PH_SHOWSTACKMENU: { - LPNMLVGETINFOTIP getInfoTip = (LPNMLVGETINFOTIP)header; - HWND lvHandle; - PTHREAD_STACK_CONTEXT threadStackContext; + PPH_EMENU menu; + PPH_STACK_TREE_ROOT_NODE selectedNode; + PPH_EMENU_ITEM selectedItem; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (header->hwndFrom == lvHandle) + if (selectedNode = GetSelectedThreadStackNode(context)) { - PTHREAD_STACK_ITEM stackItem; - PPH_THREAD_STACK_FRAME stackFrame; - - if (PhGetListViewItemParam(lvHandle, getInfoTip->iItem, &stackItem)) + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), -1); + PhInsertCopyCellEMenuItem(menu, IDC_COPY, context->TreeNewHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != -1) { - 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); + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); } + + PhDestroyEMenu(menu); } } break; @@ -411,10 +899,7 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( break; case WM_SIZE: { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); + PhLayoutManagerLayout(&context->LayoutManager); } break; case WM_SIZING: @@ -437,89 +922,76 @@ static VOID PhpFreeThreadStackItem( static NTSTATUS PhpRefreshThreadStack( _In_ HWND hwnd, - _In_ PTHREAD_STACK_CONTEXT ThreadStackContext + _In_ PPH_THREAD_STACK_CONTEXT Context ) { ULONG i; - ThreadStackContext->StopWalk = FALSE; - PhMoveReference(&ThreadStackContext->StatusMessage, PhCreateString(L"Loading stack...")); + Context->StopWalk = FALSE; + PhMoveReference(&Context->StatusMessage, PhCreateString(L"Loading stack...")); DialogBoxParam( PhInstanceHandle, MAKEINTRESOURCE(IDD_PROGRESS), hwnd, PhpThreadStackProgressDlgProc, - (LPARAM)ThreadStackContext + (LPARAM)Context ); - if (!ThreadStackContext->StopWalk && NT_SUCCESS(ThreadStackContext->WalkStatus)) + if (!Context->StopWalk && NT_SUCCESS(Context->WalkStatus)) { - for (i = 0; i < ThreadStackContext->List->Count; i++) - PhpFreeThreadStackItem(ThreadStackContext->List->Items[i]); + for (i = 0; i < Context->List->Count; i++) + PhpFreeThreadStackItem(Context->List->Items[i]); - PhDereferenceObject(ThreadStackContext->List); - ThreadStackContext->List = ThreadStackContext->NewList; - ThreadStackContext->NewList = PhCreateList(10); + PhDereferenceObject(Context->List); + Context->List = Context->NewList; + Context->NewList = PhCreateList(10); - SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, FALSE, 0); - ListView_DeleteAllItems(ThreadStackContext->ListViewHandle); + ClearThreadStackTree(Context); - for (i = 0; i < ThreadStackContext->List->Count; i++) + for (i = 0; i < Context->List->Count; i++) { - PTHREAD_STACK_ITEM item = ThreadStackContext->List->Items[i]; - INT lvItemIndex; - WCHAR integerString[PH_INT32_STR_LEN_1]; - WCHAR addressString[PH_PTR_STR_LEN_1]; + PTHREAD_STACK_ITEM item = Context->List->Items[i]; + PPH_STACK_TREE_ROOT_NODE stackNode; - PhPrintUInt32(integerString, item->Index); - lvItemIndex = PhAddListViewItem(ThreadStackContext->ListViewHandle, MAXINT, integerString, item); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 1, PhGetStringOrDefault(item->Symbol, L"???")); + stackNode = AddThreadStackNode(Context, item->Index); + stackNode->StackFrame = item->StackFrame; - PhPrintPointer(addressString, item->StackFrame.StackAddress); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 2, addressString); + if (!PhIsNullOrEmptyString(item->Symbol)) + stackNode->SymbolString = PhReferenceObject(item->Symbol); - PhPrintPointer(addressString, item->StackFrame.FrameAddress); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 3, addressString); + PhPrintPointer(stackNode->StackAddressString, item->StackFrame.StackAddress); + PhPrintPointer(stackNode->FrameAddressString, item->StackFrame.FrameAddress); // There are no params for kernel-mode stack traces. if ((ULONG_PTR)item->StackFrame.PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) { - PhPrintPointer(addressString, item->StackFrame.Params[0]); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 4, addressString); - - PhPrintPointer(addressString, item->StackFrame.Params[1]); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 5, addressString); - - PhPrintPointer(addressString, item->StackFrame.Params[2]); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 6, addressString); - - PhPrintPointer(addressString, item->StackFrame.Params[3]); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 7, addressString); + 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]); } - PhPrintPointer(addressString, item->StackFrame.PcAddress); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 8, addressString); + PhPrintPointer(stackNode->PcAddressString, item->StackFrame.PcAddress); + PhPrintPointer(stackNode->ReturnAddressString, item->StackFrame.ReturnAddress); - PhPrintPointer(addressString, item->StackFrame.ReturnAddress); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 9, addressString); + UpdateThreadStackNode(Context, stackNode); } - SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, TRUE, 0); - InvalidateRect(ThreadStackContext->ListViewHandle, NULL, FALSE); + TreeNew_NodesStructured(Context->TreeNewHandle); } else { - for (i = 0; i < ThreadStackContext->NewList->Count; i++) - PhpFreeThreadStackItem(ThreadStackContext->NewList->Items[i]); + for (i = 0; i < Context->NewList->Count; i++) + PhpFreeThreadStackItem(Context->NewList->Items[i]); - PhClearList(ThreadStackContext->NewList); + PhClearList(Context->NewList); } - if (ThreadStackContext->StopWalk) + if (Context->StopWalk) return STATUS_ABANDONED; - return ThreadStackContext->WalkStatus; + return Context->WalkStatus; } static BOOLEAN NTAPI PhpWalkThreadStackCallback( @@ -527,7 +999,7 @@ static BOOLEAN NTAPI PhpWalkThreadStackCallback( _In_opt_ PVOID Context ) { - PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)Context; + PPH_THREAD_STACK_CONTEXT threadStackContext = (PPH_THREAD_STACK_CONTEXT)Context; PPH_STRING symbol; PTHREAD_STACK_ITEM item; @@ -585,7 +1057,7 @@ static NTSTATUS PhpRefreshThreadStackThreadStart( { PH_AUTO_POOL autoPool; NTSTATUS status; - PTHREAD_STACK_CONTEXT threadStackContext = Parameter; + PPH_THREAD_STACK_CONTEXT threadStackContext = Parameter; CLIENT_ID clientId; BOOLEAN defaultWalk; @@ -664,24 +1136,36 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( _In_ LPARAM lParam ) { + PPH_THREAD_STACK_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PPH_THREAD_STACK_CONTEXT)lParam; + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPH_THREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + } + + if (!context) + return FALSE; + 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)) + context->ProgressWindowHandle = hwndDlg; + + if (threadHandle = PhCreateThread(0, PhpRefreshThreadStackThreadStart, context)) { NtClose(threadHandle); } else { - threadStackContext->WalkStatus = STATUS_UNSUCCESSFUL; + context->WalkStatus = STATUS_UNSUCCESSFUL; EndDialog(hwndDlg, IDOK); break; } @@ -704,10 +1188,8 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( { case IDCANCEL: { - PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); - threadStackContext->StopWalk = TRUE; + context->StopWalk = TRUE; } break; } @@ -720,13 +1202,12 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( 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; + PhAcquireQueuedLockExclusive(&context->StatusLock); + message = context->StatusMessage; PhReferenceObject(message); - PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock); + PhReleaseQueuedLockExclusive(&context->StatusLock); SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, message->Buffer); PhDereferenceObject(message); @@ -734,5 +1215,5 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( break; } - return 0; + return FALSE; } From 4c0c64c9ae8be3efc3faeff051fa382178eb72b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 05:56:10 +1000 Subject: [PATCH 314/839] Fix thread stack right-click menu --- ProcessHacker/thrdstk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 6406cbdad01a..a86ac5b96b91 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -877,7 +877,7 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( selectedItem = PhShowEMenu( menu, hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, contextMenuEvent->Location.x, contextMenuEvent->Location.y From 752940005eaf82d19766a173a37a4e31910e5948 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 16:56:57 +1000 Subject: [PATCH 315/839] Fix thread stack memory leak --- ProcessHacker/thrdstk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index a86ac5b96b91..fb0142ba6a31 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -257,6 +257,8 @@ VOID DestroyThreadStackNode( _In_ PPH_STACK_TREE_ROOT_NODE Node ) { + PhClearReference(&Node->TooltipText); + PhClearReference(&Node->IndexString); PhClearReference(&Node->SymbolString); PhDereferenceObject(Node); From 0512933f407e95ef28ebe3360eed84de9f36f697 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 18:31:41 +1000 Subject: [PATCH 316/839] OnlineChecks: Fix service right-click crash --- plugins/OnlineChecks/main.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index a7ec2e294b99..dc1ee8297a9d 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -346,15 +346,24 @@ VOID NTAPI ServiceMenuInitializingCallback( else serviceItem = NULL; - QueryServiceFileName( - &serviceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ); + if (serviceItem) + { + QueryServiceFileName( + &serviceItem->Name->sr, + &serviceFileName, + &serviceBinaryPath + ); + } + + if (serviceBinaryPath) + PhDereferenceObject(serviceBinaryPath); - if (serviceBinaryPath) PhDereferenceObject(serviceBinaryPath); + if (serviceFileName) + { + // TODO: memory leak or possible use-after-free bug? + PhAutoDereferenceObject(serviceFileName); + } - // TODO: Possible serviceFileName string memory leak. sendToMenu = CreateSendToMenu(menuInfo->Menu, serviceFileName ? serviceFileName : NULL); if (!serviceItem || !serviceFileName) From a948f9e98f28c1d6d68a89d572b6b0738cd3aebe Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 18:37:07 +1000 Subject: [PATCH 317/839] Revert "NetworkTools: Fix string usage" This reverts commit 9d3b37aaf39f2ad3b8224552f201e1432d9a50f1. --- plugins/NetworkTools/tracetree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 12aa70e1265c..fb4d6f55a818 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -223,7 +223,7 @@ VOID UpdateTracertNodePingText( PhFormatString(L"%s ms", PhaFormatUInt64(Node->PingList[Index], TRUE)->Buffer) ); - CellText->Text = PhGetStringRef(Node->PingString[Index]); + CellText->Text = Node->PingString[Index]->sr; } else { From 8c0f431c44ff7bd8a38543c19503860b2c5ba1b4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 18:37:53 +1000 Subject: [PATCH 318/839] Fix default thread stack columns --- ProcessHacker/thrdstk.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index fb0142ba6a31..9674e6531cce 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -627,16 +627,16 @@ VOID InitializeThreadStackTree( TreeNew_SetCallback(Context->TreeNewHandle, ThreadStackTreeNewCallback, Context); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_INDEX, TRUE, L"#", 30, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_INDEX, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_SYMBOL, TRUE, L"Name", 250, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_SYMBOL, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_STACKADDRESS, FALSE, L"Stack address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_STACKADDRESS, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_FRAMEADDRESS, FALSE, L"Frame address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_FRAMEADDRESS, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER1, FALSE, L"Stack parameter #1", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER1, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER2, FALSE, L"Stack parameter #2", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER2, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER3, FALSE, L"Stack parameter #3", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER3, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER4, FALSE, L"Stack parameter #4", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_PARAMETER4, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_CONTROLADDRESS, TRUE, L"Control address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_CONTROLADDRESS, 0); - PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_RETURNADDRESS, FALSE, L"Return address", 100, PH_ALIGN_LEFT, PH_STACK_TREE_COLUMN_RETURNADDRESS, 0); + 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, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_FRAMEADDRESS, FALSE, L"Frame address", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER1, FALSE, L"Stack parameter #1", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER2, FALSE, L"Stack parameter #2", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER3, FALSE, L"Stack parameter #3", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER4, FALSE, L"Stack parameter #4", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_CONTROLADDRESS, FALSE, L"Control address", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_RETURNADDRESS, FALSE, L"Return address", 100, PH_ALIGN_LEFT, -1, 0); TreeNew_SetTriState(Context->TreeNewHandle, TRUE); From 62fc4d9e337e0ac13cfd66d28d03c7bb35df6cb3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 19:23:16 +1000 Subject: [PATCH 319/839] NetworkTools: Fix tracert crash --- plugins/NetworkTools/nettools.h | 3 ++- plugins/NetworkTools/tracert.c | 32 ++++++++++++++++++++++++++------ plugins/NetworkTools/tracert.h | 3 +-- plugins/NetworkTools/tracetree.c | 2 +- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 106eb2849428..8e46673641d2 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -102,7 +102,8 @@ typedef enum _PH_NETWORK_ACTION #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 WM_TRACERT_HOSTNAME (WM_APP + NETWORK_ACTION_TRACEROUTE + 1003) +#define WM_TRACERT_COUNTRY (WM_APP + NETWORK_ACTION_TRACEROUTE + 1004) #define UPDATE_MENUITEM 1005 #define PH_UPDATEISERRORED (WM_APP + 501) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 62f39daef461..57bd18c03b4a 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -72,7 +72,7 @@ NTSTATUS TracertHostnameLookupCallback( NI_NAMEREQD )) { - PhMoveReference(&resolve->Node->HostnameString, PhCreateString(resolve->SocketAddressHostname)); + SendMessage(resolve->WindowHandle, WM_TRACERT_HOSTNAME, resolve->Index, (LPARAM)PhCreateString(resolve->SocketAddressHostname)); } else { @@ -100,7 +100,7 @@ NTSTATUS TracertHostnameLookupCallback( NI_NAMEREQD )) { - PhMoveReference(&resolve->Node->HostnameString, PhCreateString(resolve->SocketAddressHostname)); + SendMessage(resolve->WindowHandle, WM_TRACERT_HOSTNAME, resolve->Index, (LPARAM)PhCreateString(resolve->SocketAddressHostname)); } else { @@ -141,7 +141,7 @@ VOID TracertQueueHostLookup( if (NT_SUCCESS(RtlIpv4AddressToStringEx(&sockAddrIn, 0, addressString, &addressStringLength))) { - Node->IpAddressString = PhCreateString(addressString); + PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); } resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); @@ -149,7 +149,7 @@ VOID TracertQueueHostLookup( resolve->Type = PH_IPV4_NETWORK_TYPE; resolve->WindowHandle = Context->WindowHandle; - resolve->Node = Node; + resolve->Index = Node->TTL; ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_family = AF_INET; ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_addr = sockAddrIn; @@ -176,7 +176,7 @@ VOID TracertQueueHostLookup( if (NT_SUCCESS(RtlIpv6AddressToStringEx(&sockAddrIn6, 0, 0, addressString, &addressStringLength))) { - Node->IpAddressString = PhCreateString(addressString); + PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); } resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); @@ -184,7 +184,7 @@ VOID TracertQueueHostLookup( resolve->Type = PH_IPV6_NETWORK_TYPE; resolve->WindowHandle = Context->WindowHandle; - resolve->Node = Node; + resolve->Index = Node->TTL; ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_family = AF_INET6; ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_addr = sockAddrIn6; @@ -689,6 +689,26 @@ INT_PTR CALLBACK TracertDlgProc( TreeNew_NodesStructured(context->TreeNewHandle); } break; + case WM_TRACERT_HOSTNAME: + { + ULONG index = (ULONG)wParam; + PPH_STRING hostName = (PPH_STRING)lParam; + PTRACERT_ROOT_NODE traceNode; + + traceNode = FindTracertNode(context, index); + + if (traceNode) + { + PhMoveReference(&traceNode->HostnameString, hostName); + + UpdateTracertNode(context, traceNode); + } + else + { + PhDereferenceObject(hostName); + } + } + break; } return FALSE; diff --git a/plugins/NetworkTools/tracert.h b/plugins/NetworkTools/tracert.h index 27bcad79bd48..817a5f3f0c78 100644 --- a/plugins/NetworkTools/tracert.h +++ b/plugins/NetworkTools/tracert.h @@ -71,8 +71,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; diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index fb4d6f55a818..12aa70e1265c 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -223,7 +223,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 { From 584528246e08eb0ed347f658bf0a17dff8ce011b Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 19:49:46 +1000 Subject: [PATCH 320/839] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 178bf550becd..c677a1505a6c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. -[![Build status](https://ci.appveyor.com/api/projects/status/5einmgmy3mnsfjdn?svg=true)](https://ci.appveyor.com/project/pbatard/rufus) +[![Build status](https://ci.appveyor.com/api/projects/status/5einmgmy3mnsfjdn?svg=true)](https://ci.appveyor.com/project/processhacker/processhacker2) [![Licence](https://img.shields.io/badge/license-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) ![Logo](https://raw.githubusercontent.com/processhacker2/processhacker/master/ProcessHacker/resources/ProcessHacker.png) From b3e28b17c954b7cc1254dc504bceb4e21e4d1dec Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 30 Jul 2017 19:55:36 +1000 Subject: [PATCH 321/839] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c677a1505a6c..8b33bb795fa4 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A free, powerful, multi-purpose tool that helps you monitor system resources, de * [Official Website](http://processhacker.sourceforge.net/) * [FAQ](http://processhacker.sourceforge.net/faq.php) +* [Nightly Builds](https://wj32.org/processhacker/nightly.php) ## System requirements From 83bb1b650319ba66f9c9a0488f604e8d894a51b9 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 10:30:03 +1000 Subject: [PATCH 322/839] Enable advanced security dialog for Win8 and Srv2012 by default --- phlib/secedit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/secedit.c b/phlib/secedit.c index 65057fae144d..24bd903c867a 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -118,7 +118,7 @@ VOID PhEditSecurity( FALSE ); - if (WindowsVersion >= WINDOWS_8_1 && PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) + if (WindowsVersion >= WINDOWS_8 && PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) EditSecurityAdvanced(hWnd, info, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); else EditSecurity(hWnd, info); From 72d2f606ba20554a34ac241d2ae03f1b7e38e69a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 11:00:38 +1000 Subject: [PATCH 323/839] ToolStatus: Fix searching waiting/unknown network connections --- plugins/ToolStatus/filter.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/plugins/ToolStatus/filter.c b/plugins/ToolStatus/filter.c index 4a20fa9d329b..cd8a92ccd31c 100644 --- a/plugins/ToolStatus/filter.c +++ b/plugins/ToolStatus/filter.c @@ -473,16 +473,49 @@ BOOLEAN ServiceTreeFilterCallback( 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)) From 9961e1c141dea7b0b0d52cfb77e7192c5e3cdfdf Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 15:03:16 +1000 Subject: [PATCH 324/839] Add OriginalFileName to the PhEnumGenericModules callbacks --- phlib/include/phnative.h | 1 + phlib/native.c | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 7b9d0baa713c..c5a60e72b0c6 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -842,6 +842,7 @@ typedef struct _PH_MODULE_INFO ULONG Flags; PPH_STRING Name; PPH_STRING FileName; + PPH_STRING OriginalFileName; USHORT LoadOrderIndex; // -1 if N/A USHORT LoadCount; // -1 if N/A diff --git a/phlib/native.c b/phlib/native.c index 03c4812f065e..a567ac5dad0e 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5157,6 +5157,7 @@ static BOOLEAN EnumGenericProcessModulesCallback( moduleInfo.Flags = Module->Flags; moduleInfo.Name = PhCreateStringFromUnicodeString(&Module->BaseDllName); moduleInfo.FileName = PhGetFileName(fileName); + moduleInfo.OriginalFileName = fileName; moduleInfo.LoadOrderIndex = (USHORT)(context->LoadOrderIndex++); moduleInfo.LoadCount = Module->ObsoleteLoadCount; @@ -5171,12 +5172,11 @@ static BOOLEAN EnumGenericProcessModulesCallback( moduleInfo.LoadTime.QuadPart = 0; } - PhDereferenceObject(fileName); - cont = context->Callback(&moduleInfo, context->Context); PhDereferenceObject(moduleInfo.Name); PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); return cont; } @@ -5222,13 +5222,12 @@ VOID PhpRtlModulesToGenericModules( 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 = -1; moduleInfo.LoadTime.QuadPart = 0; - PhDereferenceObject(fileName); - if (module->OffsetToFileName == 0) { static PH_STRINGREF driversString = PH_STRINGREF_INIT(L"\\System32\\Drivers\\"); @@ -5247,6 +5246,7 @@ VOID PhpRtlModulesToGenericModules( PhDereferenceObject(moduleInfo.Name); PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); if (!cont) break; @@ -5331,6 +5331,7 @@ BOOLEAN PhpCallbackMappedFileOrImage( moduleInfo.EntryPoint = NULL; moduleInfo.Flags = 0; moduleInfo.FileName = PhGetFileName(FileName); + moduleInfo.OriginalFileName = FileName; moduleInfo.Name = PhGetBaseName(moduleInfo.FileName); moduleInfo.LoadOrderIndex = -1; moduleInfo.LoadCount = -1; @@ -5340,6 +5341,7 @@ BOOLEAN PhpCallbackMappedFileOrImage( cont = Callback(&moduleInfo, Context); PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); PhDereferenceObject(moduleInfo.Name); return cont; @@ -5446,8 +5448,6 @@ VOID PhpEnumGenericMappedFilesAndImages( BaseAddressHashtable ); - PhDereferenceObject(fileName); - if (!cont) break; } From b0f6fb012796b911a63b470d5a219df0978ddcae Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 15:39:47 +1000 Subject: [PATCH 325/839] Rewrite find handle window: Add treelist control, Add column customization, Add right-click menu, Add column sorting --- ProcessHacker/ProcessHacker.rc | 3 +- ProcessHacker/findobj.c | 1921 ++++++++++++++++++++------------ ProcessHacker/settings.c | 2 +- 3 files changed, 1213 insertions(+), 713 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 2261c520d25c..01037698009a 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -360,6 +360,7 @@ BEGIN 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 @@ -727,7 +728,7 @@ BEGIN LTEXT "Filter:",IDC_STATIC,7,7,19,8 EDITTEXT IDC_FILTER,29,4,134,13,ES_AUTOHSCROLL PUSHBUTTON "Find",IDOK,301,3,50,14 - CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,21,353,210 + CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,353,212,WS_EX_CLIENTEDGE CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,6,34,10 COMBOBOX IDC_FILTERTYPE,165,4,93,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 602f39befab9..c3dbb4eb6f3b 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -25,11 +25,13 @@ #include +#include #include #include #include #include +#include #include #include #include @@ -39,8 +41,42 @@ #include "pcre/pcre2.h" -#define WM_PH_SEARCH_UPDATE (WM_APP + 801) +#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; + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + PPH_LIST NodeRootList; + + HANDLE TimerQueueHandle; + 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 { @@ -55,153 +91,511 @@ typedef struct _PHP_OBJECT_SEARCH_RESULT PHP_OBJECT_RESULT_TYPE ResultType; HANDLE Handle; + PVOID Object; PPH_STRING TypeName; - PPH_STRING Name; - PPH_STRING ProcessName; - - WCHAR HandleString[PH_PTR_STR_LEN_1]; + PPH_STRING ObjectName; + PPH_STRING BestObjectName; 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 - ); +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, + TREE_COLUMN_ITEM_MAXIMUM +} PH_HANDLE_OBJECT_TREE_COLUMN_ITEM_NAME; + +typedef struct _PH_HANDLE_OBJECT_TREE_ROOT_NODE +{ + PH_TREENEW_NODE Node; -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 PPH_STRING SearchTypeString; -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; + 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[TREE_COLUMN_ITEM_MAXIMUM]; +} PH_HANDLE_OBJECT_TREE_ROOT_NODE, *PPH_HANDLE_OBJECT_TREE_ROOT_NODE; + +#define SORT_FUNCTION(Column) HandleObjectTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl HandleObjectTreeNewCompare##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->Node.Index, (ULONG_PTR)node2->Node.Index); \ + \ + return PhModifySort(sortResult, ((PPH_HANDLE_SEARCH_CONTEXT)_context)->TreeNewSortOrder); \ +} -VOID PhShowFindObjectsDialog( - VOID +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 + +VOID HandleObjectLoadSettingsTreeList( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context ) { - if (!PhFindObjectsWindowHandle) - { - PhFindObjectsWindowHandle = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_FINDOBJECTS), - NULL, - PhpFindObjectsDlgProc - ); - } + PPH_STRING settings; + + settings = PhGetStringSetting(L"FindObjTreeListColumns"); + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); +} + +VOID HandleObjectSaveSettingsTreeList( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(L"FindObjTreeListColumns", &settings->sr); + PhDereferenceObject(settings); +} - if (!IsWindowVisible(PhFindObjectsWindowHandle)) - ShowWindow(PhFindObjectsWindowHandle, SW_SHOW); +BOOLEAN HandleObjectNodeHashtableEqualFunction( + _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; - SetForegroundWindow(PhFindObjectsWindowHandle); + return node1->HandleObject == node2->HandleObject; } -VOID PhpInitializeFindObjMenu( - _In_ PPH_EMENU Menu, - _In_ PPHP_OBJECT_SEARCH_RESULT *Results, - _In_ ULONG NumberOfResults +ULONG HandleObjectNodeHashtableHashFunction( + _In_ PVOID Entry ) { - BOOLEAN allCanBeClosed = TRUE; - ULONG i; + return PhHashIntPtr((ULONG_PTR)(*(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)Entry)->HandleObject); +} - if (NumberOfResults == 1) - { - PH_HANDLE_ITEM_INFO info; +VOID DestroyHandleObjectNode( + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node + ) +{ + PhClearReference(&Node->ClientIdName); + PhClearReference(&Node->TypeNameString); + PhClearReference(&Node->ObjectNameString); + PhClearReference(&Node->BestObjectName); - 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); - } + PhDereferenceObject(Node); +} + +PPH_HANDLE_OBJECT_TREE_ROOT_NODE AddHandleObjectNode( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PVOID HandleObject, + _In_ HANDLE Handle + ) +{ + 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) * TREE_COLUMN_ITEM_MAXIMUM); + handleObjectNode->Node.TextCache = handleObjectNode->TextCache; + handleObjectNode->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; + + handleObjectNode->HandleObject = HandleObject; + handleObjectNode->Handle = Handle; + + PhAddEntryHashtable(Context->NodeHashtable, &handleObjectNode); + PhAddItemList(Context->NodeList, handleObjectNode); + + //TreeNew_NodesStructured(Context->TreeNewHandle); + + return handleObjectNode; +} + +PPH_HANDLE_OBJECT_TREE_ROOT_NODE FindHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PVOID HandleObject + ) +{ + PH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNode; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNodePtr = &lookupHandleObjectNode; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNode; + + lookupHandleObjectNode.HandleObject = HandleObject; + + handleObjectNode = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupHandleObjectNodePtr + ); + + if (handleObjectNode) + return *handleObjectNode; else + return NULL; +} + +VOID RemoveHandleObjectNode( + _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)) != -1) { - PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - PhEnableEMenuItem(Menu, ID_OBJECT_COPY, TRUE); + PhRemoveItemList(Context->NodeList, index); } - for (i = 0; i < NumberOfResults; i++) + DestroyHandleObjectNode(Node); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID UpdateHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PPH_HANDLE_OBJECT_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 HandleObjectTreeNewCallback( + _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) { - if (Results[i]->ResultType != HandleSearchResult) + case TreeNewGetChildren: { - allCanBeClosed = FALSE; - break; + 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), + }; + 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_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; } - PhEnableEMenuItem(Menu, ID_OBJECT_CLOSE, allCanBeClosed); + return FALSE; } -INT NTAPI PhpObjectProcessCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context +VOID ClearHandleObjectTree( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context ) { - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; - INT result; + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyHandleObjectNode(Context->NodeList->Items[i]); - result = PhCompareStringWithNull(item1->ProcessName, item2->ProcessName, TRUE); + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); - if (result != 0) - return result; - else - return uintptrcmp((ULONG_PTR)item1->ProcessId, (ULONG_PTR)item2->ProcessId); + TreeNew_NodesStructured(Context->TreeNewHandle); } -INT NTAPI PhpObjectTypeCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context +PPH_HANDLE_OBJECT_TREE_ROOT_NODE GetSelectedHandleObjectNode( + _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 GetSelectedHandleObjectNodes( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _Out_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE **HandleObjectNodes, + _Out_ PULONG NumberOfHandleObjectNodes ) { - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; + 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]; - return PhCompareString(item1->TypeName, item2->TypeName, TRUE); + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *HandleObjectNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfHandleObjectNodes = list->Count; + + PhDereferenceObject(list); } -INT NTAPI PhpObjectNameCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context +VOID InitializeHandleObjectTree( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + Context->NodeList = PhCreateList(100); + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_HANDLE_OBJECT_TREE_ROOT_NODE), + HandleObjectNodeHashtableEqualFunction, + HandleObjectNodeHashtableHashFunction, + 100 + ); + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(Context->TreeNewHandle, HandleObjectTreeNewCallback, 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, -1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, -1, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + + HandleObjectLoadSettingsTreeList(Context); +} + +VOID DeleteHandleObjectTree( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context ) { - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; + HandleObjectSaveSettingsTreeList(Context); - return PhCompareString(item1->Name, item2->Name, TRUE); + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + DestroyHandleObjectNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); } -INT NTAPI PhpObjectHandleCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context +VOID PhpInitializeFindObjMenu( + _In_ PPH_EMENU Menu, + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE *Results, + _In_ ULONG NumberOfResults ) { - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; + 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; + } + } - return uintptrcmp((ULONG_PTR)item1->Handle, (ULONG_PTR)item2->Handle); + PhEnableEMenuItem(Menu, ID_OBJECT_CLOSE, allCanBeClosed); } static int __cdecl PhpStringObjectTypeCompare( @@ -215,7 +609,7 @@ static int __cdecl PhpStringObjectTypeCompare( return PhCompareString(entry1, entry2, TRUE); } -static VOID PhpPopulateObjectTypes( +VOID PhpPopulateObjectTypes( _In_ HWND FilterTypeCombo ) { @@ -256,89 +650,581 @@ static VOID PhpPopulateObjectTypes( PhDereferenceObject(objectTypeList); } -static INT_PTR CALLBACK PhpFindObjectsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam +VOID PhpFindObjectAddResultEntries( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context ) { - switch (uMsg) + ULONG i; + + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + + PhAcquireQueuedLockExclusive(&Context->SearchResultsLock); + + for (i = Context->SearchResultsAddIndex; i < Context->SearchResults->Count; i++) { - case WM_INITDIALOG: - { - HWND lvHandle; + 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 = AddHandleObjectNode(Context, searchResult->Object, 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) + PhPrintPointer(objectNode->ObjectString, searchResult->Object); + + PhDereferenceObject(processItem); + } - 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->SearchResultsAddIndex = i; - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - PhFindObjectsListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); + PhReleaseQueuedLockExclusive(&Context->SearchResultsLock); - PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), NULL); + TreeNew_NodesStructured(Context->TreeNewHandle); + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); +} - PhpPopulateObjectTypes(GetDlgItem(hwndDlg, IDC_FILTERTYPE)); +VOID PhpFindObjectClearResultEntries( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + ClearHandleObjectTree(Context); - PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, 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); + Context->SearchResultsAddIndex = 0; - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 150; - MinimumSize.bottom = 100; - MapDialogRect(hwndDlg, &MinimumSize); + for (ULONG i = 0; i < Context->SearchResults->Count; i++) + PhFree(Context->SearchResults->Items[i]); - PhRegisterDialog(hwndDlg); + PhClearList(Context->SearchResults); +} - PhLoadWindowPlacementFromSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); +VOID CALLBACK PhpFindObjectTreeUpdateCallback( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ BOOLEAN TimerOrWaitFired + ) +{ + if (!Context->SearchThreadHandle) + { + RtlUpdateTimer(Context->TimerQueueHandle, Context->UpdateTimerHandle, 1000, INFINITE); + return; + } - 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); + // Update the search results. + PhpFindObjectAddResultEntries(Context); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); - } - 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: - { - PhSetIntegerSetting(L"FindObjRegex", Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED); - PhSaveWindowPlacementToSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); - PhSaveListViewColumnsToSetting(L"FindObjListViewColumns", PhFindObjectsListViewHandle); - - 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: + RtlUpdateTimer(Context->TimerQueueHandle, 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 (Context->SearchRegexCompiledExpression && Context->SearchRegexMatchData) + return TRUE; + else + { + if (PhEqualString2(Context->SearchTypeString, L"Everything", FALSE)) + return TRUE; + + return PhFindStringInStringRef(Input, &Context->SearchTypeString->sr, TRUE) != -1; + } +} + +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 = PhAllocate(sizeof(PHP_OBJECT_SEARCH_RESULT)); + memset(searchResult, 0, 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; + + upperFileName = PhDuplicateString(Module->FileName); + _wcsupr(upperFileName->Buffer); + + if (MatchSearchString(context, &upperFileName->sr) || + (context->UseSearchPointer && Module->BaseAddress == (PVOID)context->SearchPointer)) + { + PPHP_OBJECT_SEARCH_RESULT searchResult; + PWSTR typeName; + + switch (Module->Type) { - if (SearchThreadHandle) + 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(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 = -1; + + BOOLEAN useWorkQueue = FALSE; + PH_WORK_QUEUE workQueue; + processHandleHashtable = PhCreateSimpleHashtable(8); + + if (!KphIsConnected()) + { + useWorkQueue = TRUE; + PhInitializeWorkQueue(&workQueue, 1, 20, 1000); + + if (PhBeginInitOnce(&initOnce)) { - SetCursor(LoadCursor(NULL, IDC_WAIT)); + 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; + + // 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)); + + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPH_HANDLE_SEARCH_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + } + + 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); + + PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), L"Search Handles or DLLs"); + + PhpPopulateObjectTypes(GetDlgItem(hwndDlg, IDC_FILTERTYPE)); + + InitializeHandleObjectTree(context); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, 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); + + PhRegisterDialog(hwndDlg); + + PhCenterWindow(hwndDlg, NULL); + PhLoadWindowPlacementFromSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); + + context->SearchResults = PhCreateList(128); + context->SearchResultsAddIndex = 0; + + if (NT_SUCCESS(RtlCreateTimerQueue(&context->TimerQueueHandle))) + { + RtlCreateTimer( + context->TimerQueueHandle, + &context->UpdateTimerHandle, + PhpFindObjectTreeUpdateCallback, + context, + 0, + 1000, + 0 + ); + } + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_FILTER), TRUE); + Edit_SetSel(GetDlgItem(hwndDlg, IDC_FILTER), 0, -1); + } + break; + case WM_DESTROY: + { + context->SearchStop = TRUE; + + if (context->UpdateTimerHandle) + { + RtlDeleteTimer(context->TimerQueueHandle, context->UpdateTimerHandle, NULL); + context->TimerQueueHandle = NULL; + } + + if (context->TimerQueueHandle) + { + RtlDeleteTimerQueue(context->TimerQueueHandle); + context->TimerQueueHandle = 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); + + PhDeleteLayoutManager(&context->LayoutManager); + + DeleteHandleObjectTree(context); + + if (context->SearchResults) + { + for (ULONG i = 0; i < context->SearchResults->Count; i++) + PhFree(context->SearchResults->Items[i]); + + PhClearList(context->SearchResults); + } + + 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; } @@ -351,26 +1237,24 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( case IDOK: { // Don't continue if the user requested cancellation. - if (SearchStop) + if (context->SearchStop) break; - if (!SearchThreadHandle) + if (!context->SearchThreadHandle) { - ULONG i; - - PhMoveReference(&SearchString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTER))); - PhMoveReference(&SearchTypeString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTERTYPE))); + PhMoveReference(&context->SearchString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTER))); + PhMoveReference(&context->SearchTypeString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTERTYPE))); - if (SearchRegexCompiledExpression) + if (context->SearchRegexCompiledExpression) { - pcre2_code_free(SearchRegexCompiledExpression); - SearchRegexCompiledExpression = NULL; + pcre2_code_free(context->SearchRegexCompiledExpression); + context->SearchRegexCompiledExpression = NULL; } - if (SearchRegexMatchData) + if (context->SearchRegexMatchData) { - pcre2_match_data_free(SearchRegexMatchData); - SearchRegexMatchData = NULL; + pcre2_match_data_free(context->SearchRegexMatchData); + context->SearchRegexMatchData = NULL; } if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED) @@ -378,16 +1262,16 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( int errorCode; PCRE2_SIZE errorOffset; - SearchRegexCompiledExpression = pcre2_compile( - SearchString->Buffer, - SearchString->Length / sizeof(WCHAR), + context->SearchRegexCompiledExpression = pcre2_compile( + context->SearchString->Buffer, + context->SearchString->Length / sizeof(WCHAR), PCRE2_CASELESS | PCRE2_DOTALL, &errorCode, &errorOffset, NULL ); - if (!SearchRegexCompiledExpression) + if (!context->SearchRegexCompiledExpression) { PhShowError(hwndDlg, L"Unable to compile the regular expression: \"%s\" at position %zu.", PhGetStringOrDefault(PH_AUTO(PhPcre2GetErrorMessage(errorCode)), L"Unknown error"), @@ -396,97 +1280,107 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( break; } - SearchRegexMatchData = pcre2_match_data_create_from_pattern(SearchRegexCompiledExpression, NULL); + context->SearchRegexMatchData = pcre2_match_data_create_from_pattern(context->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); - } + PhpFindObjectClearResultEntries(context); // Start the search. - SearchResults = PhCreateList(128); - SearchResultsAddIndex = 0; - - SearchThreadHandle = PhCreateThread(0, PhpFindObjectsThreadStart, NULL); + PhReferenceObject(context); + context->SearchThreadHandle = PhCreateThread(0, PhpFindObjectsThreadStart, context); - if (!SearchThreadHandle) - { - PhClearReference(&SearchResults); + if (!context->SearchThreadHandle) break; - } SetDlgItemText(hwndDlg, IDOK, L"Cancel"); - SetCursor(LoadCursor(NULL, IDC_WAIT)); + SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); } else { - SearchStop = TRUE; + context->SearchStop = TRUE; EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); } } break; case IDCANCEL: { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); + 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; + + GetSelectedHandleObjectNodes(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 != -1) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + + PhDestroyEMenu(menu); + } } break; case ID_OBJECT_CLOSE: { - PPHP_OBJECT_SEARCH_RESULT *results; - ULONG numberOfResults; - ULONG i; - - PhGetSelectedListViewItemParams( - PhFindObjectsListViewHandle, - &results, - &numberOfResults - ); + PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNodes = NULL; + ULONG numberOfHandleObjectNodes = 0; - if (numberOfResults != 0 && PhShowConfirmMessage( + GetSelectedHandleObjectNodes(context, &handleObjectNodes, &numberOfHandleObjectNodes); + + if (numberOfHandleObjectNodes != 0 && PhShowConfirmMessage( hwndDlg, L"close", - numberOfResults == 1 ? L"the selected handle" : L"the selected handles", + numberOfHandleObjectNodes == 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++) + for (ULONG i = 0; i < numberOfHandleObjectNodes; i++) { NTSTATUS status; HANDLE processHandle; - - if (results[i]->ResultType != HandleSearchResult) + + if (handleObjectNodes[i]->ResultType != HandleSearchResult) continue; - + if (NT_SUCCESS(status = PhOpenProcess( &processHandle, PROCESS_DUP_HANDLE, - results[i]->ProcessId + handleObjectNodes[i]->ProcessId ))) { if (NT_SUCCESS(status = NtDuplicateObject( processHandle, - results[i]->Handle, + handleObjectNodes[i]->Handle, NULL, NULL, 0, @@ -494,17 +1388,16 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( DUPLICATE_CLOSE_SOURCE ))) { - PhRemoveListViewItem(PhFindObjectsListViewHandle, - PhFindListViewItemByParam(PhFindObjectsListViewHandle, 0, results[i])); + RemoveHandleObjectNode(context, handleObjectNodes[i]); } - + NtClose(processHandle); } - + if (!NT_SUCCESS(status)) { if (!PhShowContinueStatus(hwndDlg, - PhaFormatString(L"Unable to close \"%s\"", results[i]->Name->Buffer)->Buffer, + PhaFormatString(L"Unable to close \"%s\"", PhGetStringOrDefault(handleObjectNodes[i]->BestObjectName, L"??"))->Buffer, status, 0 )) @@ -512,24 +1405,21 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( } } } - - PhFree(results); } break; case ID_HANDLE_OBJECTPROPERTIES1: case ID_HANDLE_OBJECTPROPERTIES2: { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; - if (result) + if (handleObjectNode = GetSelectedHandleObjectNode(context)) { PH_HANDLE_ITEM_INFO info; - info.ProcessId = result->ProcessId; - info.Handle = result->Handle; - info.TypeName = result->TypeName; - info.BestObjectName = result->Name; + 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); @@ -540,14 +1430,13 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( break; case ID_OBJECT_GOTOOWNINGPROCESS: { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; - if (result) + if (handleObjectNode = GetSelectedHandleObjectNode(context)) { PPH_PROCESS_NODE processNode; - if (processNode = PhFindProcessNode(result->ProcessId)) + if (processNode = PhFindProcessNode(handleObjectNode->ProcessId)) { ProcessHacker_SelectTabPage(PhMainWndHandle, 0); ProcessHacker_SelectProcessNode(PhMainWndHandle, processNode); @@ -558,26 +1447,25 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( break; case ID_OBJECT_PROPERTIES: { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; - if (result) + if (handleObjectNode = GetSelectedHandleObjectNode(context)) { - if (result->ResultType == HandleSearchResult) + if (handleObjectNode->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); - + + handleItem = PhCreateHandleItem(&handleObjectNode->HandleInfo); + + handleItem->BestObjectName = handleItem->ObjectName = handleObjectNode->BestObjectName; + PhReferenceObjectEx(handleObjectNode->BestObjectName, 2); + + handleItem->TypeName = handleObjectNode->TypeNameString; + PhReferenceObject(handleObjectNode->TypeNameString); + PhShowHandleProperties( hwndDlg, - result->ProcessId, + handleObjectNode->ProcessId, handleItem ); PhDereferenceObject(handleItem); @@ -585,185 +1473,49 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( else { // DLL or Mapped File. Just show file properties. - PhShellProperties(hwndDlg, result->Name->Buffer); + PhShellProperties(hwndDlg, handleObjectNode->BestObjectName->Buffer); } } } break; case ID_OBJECT_COPY: { - PhCopyListView(PhFindObjectsListViewHandle); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; + PPH_STRING text; - switch (header->code) - { - case NM_DBLCLK: - { - if (header->hwndFrom == PhFindObjectsListViewHandle) - { - SendMessage(hwndDlg, WM_COMMAND, ID_OBJECT_PROPERTIES, 0); - } + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); } 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_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom); } break; case WM_PH_SEARCH_FINISHED: { - NTSTATUS handleSearchStatus = (NTSTATUS)wParam; - // Add any un-added items. - SendMessage(hwndDlg, WM_PH_SEARCH_UPDATE, 0, 0); + PhpFindObjectAddResultEntries(context); - NtWaitForSingleObject(SearchThreadHandle, FALSE, NULL); - NtClose(SearchThreadHandle); - SearchThreadHandle = NULL; - SearchStop = FALSE; - - ExtendedListView_SortItems(GetDlgItem(hwndDlg, IDC_RESULTS)); + NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL); + NtClose(context->SearchThreadHandle); + context->SearchThreadHandle = NULL; + context->SearchStop = FALSE; SetDlgItemText(hwndDlg, IDOK, L"Find"); EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); SetCursor(LoadCursor(NULL, IDC_ARROW)); - if (handleSearchStatus == STATUS_INSUFFICIENT_RESOURCES) + if ((NTSTATUS)wParam == STATUS_INSUFFICIENT_RESOURCES) { PhShowWarning( hwndDlg, @@ -778,318 +1530,65 @@ static INT_PTR CALLBACK PhpFindObjectsDlgProc( 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; - } -} - -static BOOLEAN MatchTypeString( - _In_ PPH_STRINGREF Input - ) -{ - if (SearchRegexCompiledExpression && SearchRegexMatchData) - return TRUE; - else - { - if (PhEqualString2(SearchTypeString, L"Everything", FALSE)) - return TRUE; - - return PhFindStringInStringRef(Input, &SearchTypeString->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( +NTSTATUS PhpFindObjectsDialogThreadStart( _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; - PPH_STRING upperTypeName; - - upperBestObjectName = PhDuplicateString(bestObjectName); - _wcsupr(upperBestObjectName->Buffer); - - upperTypeName = PhDuplicateString(typeName); - _wcsupr(upperTypeName->Buffer); - - if ((MatchSearchString(&upperBestObjectName->sr) && MatchTypeString(&upperTypeName->sr)) || - (UseSearchPointer && (context->HandleInfo->Object == (PVOID)SearchPointer || context->HandleInfo->HandleValue == SearchPointer))) - { - PPHP_OBJECT_SEARCH_RESULT searchResult; + BOOL result; + MSG message; + PH_AUTO_POOL autoPool; - 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; + PhInitializeAutoPool(&autoPool); - PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhFindObjectsWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_FINDOBJECTS), + NULL, + PhpFindObjectsDlgProc + ); - PhAddItemList(SearchResults, searchResult); + PhSetEvent(&PhFindObjectsInitializedEvent); - // Update the search results in batches of 40. - if (SearchResults->Count % 40 == 0) - PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_UPDATE, 0, 0); + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; - PhReleaseQueuedLockExclusive(&SearchResultsLock); - } - else + if (!IsDialogMessage(PhFindObjectsWindowHandle, &message)) { - PhDereferenceObject(typeName); - PhDereferenceObject(bestObjectName); + TranslateMessage(&message); + DispatchMessage(&message); } - PhDereferenceObject(upperBestObjectName); + PhDrainAutoPool(&autoPool); } - if (context->NeedToFree) - PhFree(context); + PhDeleteAutoPool(&autoPool); + PhResetEvent(&PhFindObjectsInitializedEvent); - 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)) + if (PhFindObjectsThreadHandle) { - 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); + NtClose(PhFindObjectsThreadHandle); + PhFindObjectsThreadHandle = NULL; } - PhDereferenceObject(upperFileName); - - return TRUE; + return STATUS_SUCCESS; } -static NTSTATUS PhpFindObjectsThreadStart( - _In_ PVOID Parameter +VOID PhShowFindObjectsDialog( + VOID ) { - 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))) + if (!PhFindObjectsThreadHandle) { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static ULONG fileObjectTypeIndex = -1; - - 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; - - 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); - } - + if (!(PhFindObjectsThreadHandle = PhCreateThread(0, PhpFindObjectsDialogThreadStart, NULL))) { - PPH_KEY_VALUE_PAIR entry; - - i = 0; - - while (PhEnumHashtable(processHandleHashtable, &entry, &i)) - NtClose((HANDLE)entry->Value); + PhShowStatus(PhMainWndHandle, L"Unable to create the window.", 0, GetLastError()); + return; } - PhDereferenceObject(processHandleHashtable); - PhFree(handles); - } - - if (PhEqualString2(SearchTypeString, L"File", TRUE) || - PhEqualString2(SearchTypeString, L"Everything", FALSE)) - { - 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); - } + PhWaitForEvent(&PhFindObjectsInitializedEvent, NULL); } -Exit: - PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_FINISHED, status, 0); - - return STATUS_SUCCESS; + PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_SHOWDIALOG, 0, 0); } diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 9ad41d1257c8..1df0dbb05dfb 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -50,7 +50,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableSecurityAdvancedDialog", L"1"); PhpAddStringSetting(L"EnvironmentListViewColumns", L""); PhpAddIntegerSetting(L"FindObjRegex", L"0"); - PhpAddStringSetting(L"FindObjListViewColumns", L""); + PhpAddStringSetting(L"FindObjTreeListColumns", L""); PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"350,350"); PhpAddScalableIntegerPairSetting(L"FindObjWindowSize", L"@96|550,420"); PhpAddStringSetting(L"FileBrowseExecutable", L"%SystemRoot%\\explorer.exe /select,\"%s\""); From 453087165937c8776c0a014eaa7cdc39b34f7279 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 15:42:57 +1000 Subject: [PATCH 326/839] Fix resource ordering --- ProcessHacker/ProcessHacker.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 01037698009a..1e94724c23cd 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -728,9 +728,9 @@ BEGIN LTEXT "Filter:",IDC_STATIC,7,7,19,8 EDITTEXT IDC_FILTER,29,4,134,13,ES_AUTOHSCROLL PUSHBUTTON "Find",IDOK,301,3,50,14 - CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,353,212,WS_EX_CLIENTEDGE CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,6,34,10 COMBOBOX IDC_FILTERTYPE,165,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 From 8665d68c04044d1fc507d8e959ff850344213e60 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 16:29:31 +1000 Subject: [PATCH 327/839] Fix find handle window not searching module original name --- ProcessHacker/findobj.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index c3dbb4eb6f3b..f0a0bb8ee4aa 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -860,11 +860,15 @@ static BOOLEAN NTAPI EnumModulesCallback( 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); - if (MatchSearchString(context, &upperFileName->sr) || + 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; @@ -898,6 +902,7 @@ static BOOLEAN NTAPI EnumModulesCallback( PhReleaseQueuedLockExclusive(&context->SearchResultsLock); } + PhDereferenceObject(upperOriginalFileName); PhDereferenceObject(upperFileName); return TRUE; From b3dcb0ad8b092a30d79fbad15cbcdd74307c0ea9 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 18:12:44 +1000 Subject: [PATCH 328/839] Fix find handle crash when sorting ObjectAddress or OriginalName columns --- ProcessHacker/findobj.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index f0a0bb8ee4aa..5cf0ab4a92e6 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -172,6 +172,18 @@ BEGIN_SORT_FUNCTION(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 HandleObjectLoadSettingsTreeList( _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context ) @@ -209,7 +221,7 @@ ULONG HandleObjectNodeHashtableHashFunction( _In_ PVOID Entry ) { - return PhHashIntPtr((ULONG_PTR)(*(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)Entry)->HandleObject); + return PhHashIntPtr((ULONG_PTR)(*(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)Entry)->Handle); } VOID DestroyHandleObjectNode( @@ -226,7 +238,6 @@ VOID DestroyHandleObjectNode( PPH_HANDLE_OBJECT_TREE_ROOT_NODE AddHandleObjectNode( _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context, - _In_ PVOID HandleObject, _In_ HANDLE Handle ) { @@ -241,7 +252,6 @@ PPH_HANDLE_OBJECT_TREE_ROOT_NODE AddHandleObjectNode( handleObjectNode->Node.TextCache = handleObjectNode->TextCache; handleObjectNode->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; - handleObjectNode->HandleObject = HandleObject; handleObjectNode->Handle = Handle; PhAddEntryHashtable(Context->NodeHashtable, &handleObjectNode); @@ -254,14 +264,14 @@ PPH_HANDLE_OBJECT_TREE_ROOT_NODE AddHandleObjectNode( PPH_HANDLE_OBJECT_TREE_ROOT_NODE FindHandleObjectNode( _In_ PPH_HANDLE_SEARCH_CONTEXT Context, - _In_ PVOID HandleObject + _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.HandleObject = HandleObject; + lookupHandleObjectNode.Handle = Handle; handleObjectNode = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)PhFindEntryHashtable( Context->NodeHashtable, @@ -329,6 +339,8 @@ BOOLEAN NTAPI HandleObjectTreeNewCallback( SORT_FUNCTION(Type), SORT_FUNCTION(Name), SORT_FUNCTION(Handle), + SORT_FUNCTION(ObjectAddress), + SORT_FUNCTION(OriginalName) }; int (__cdecl *sortFunction)(void *, const void *, const void *); @@ -672,7 +684,7 @@ VOID PhpFindObjectAddResultEntries( processItem = PhReferenceProcessItem(clientId.UniqueProcess); - objectNode = AddHandleObjectNode(Context, searchResult->Object, searchResult->Handle); + objectNode = AddHandleObjectNode(Context, searchResult->Handle); objectNode->ProcessId = searchResult->ProcessId; objectNode->ResultType = searchResult->ResultType; objectNode->ClientIdName = PhGetClientIdNameEx(&clientId, processItem ? processItem->ProcessName : NULL); @@ -683,7 +695,10 @@ VOID PhpFindObjectAddResultEntries( PhPrintPointer(objectNode->HandleString, searchResult->Handle); if (searchResult->Object) + { + objectNode->HandleObject = searchResult->Object; PhPrintPointer(objectNode->ObjectString, searchResult->Object); + } PhDereferenceObject(processItem); } From a0c8bef525d263cd4f2c1c6c67d18d8ca11d9a5b Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 18:49:48 +1000 Subject: [PATCH 329/839] NetworkTools: Add support for multiple traceroute results for the same hop --- plugins/NetworkTools/tracert.c | 66 ++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 57bd18c03b4a..e26d8cf6647f 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -141,7 +141,23 @@ VOID TracertQueueHostLookup( if (NT_SUCCESS(RtlIpv4AddressToStringEx(&sockAddrIn, 0, addressString, &addressStringLength))) { - PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); + 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)); @@ -176,7 +192,23 @@ VOID TracertQueueHostLookup( if (NT_SUCCESS(RtlIpv6AddressToStringEx(&sockAddrIn6, 0, 0, addressString, &addressStringLength))) { - PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); + 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)); @@ -695,13 +727,33 @@ INT_PTR CALLBACK TracertDlgProc( PPH_STRING hostName = (PPH_STRING)lParam; PTRACERT_ROOT_NODE traceNode; - traceNode = FindTracertNode(context, index); - - if (traceNode) + if (traceNode = FindTracertNode(context, index)) { - PhMoveReference(&traceNode->HostnameString, hostName); + 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 + { + PhDereferenceObject(hostName); + } + } + else + { + PhMoveReference(&traceNode->HostnameString, hostName); - UpdateTracertNode(context, traceNode); + UpdateTracertNode(context, traceNode); + } } else { From 16636d0a1caa7d2a610adcc896276e30d6d89d41 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 19:03:58 +1000 Subject: [PATCH 330/839] NetworkTools: Fix use-after-free bug , Add refresh button to tracert window --- plugins/NetworkTools/NetworkTools.rc | 1 + plugins/NetworkTools/resource.h | 3 +- plugins/NetworkTools/tracert.c | 56 ++++++++++++++++++---------- plugins/NetworkTools/tracert.h | 4 ++ 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/plugins/NetworkTools/NetworkTools.rc b/plugins/NetworkTools/NetworkTools.rc index 928883c44c71..7f639e93aebd 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -135,6 +135,7 @@ 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 diff --git a/plugins/NetworkTools/resource.h b/plugins/NetworkTools/resource.h index 84bd7f5c868c..204de7750856 100644 --- a/plugins/NetworkTools/resource.h +++ b/plugins/NetworkTools/resource.h @@ -269,6 +269,7 @@ #define IDC_STATUS 1023 #define IDC_GEOIP 1028 #define IDC_LIST_TRACERT 1030 +#define IDC_REFRESH 1031 // Next default values for new objects // @@ -276,7 +277,7 @@ #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_CONTROL_VALUE 1032 #define _APS_NEXT_SYMED_VALUE 104 #endif #endif diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index e26d8cf6647f..ce47aaaada97 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -438,9 +438,9 @@ NTSTATUS NetworkTracertThreadStart( IcmpCloseHandle(icmpHandle); } - PhDereferenceObject(context); - PostMessage(context->WindowHandle, NTM_RECEIVEDFINISH, 0, 0); + + PhDereferenceObject(context); return STATUS_SUCCESS; } @@ -630,14 +630,16 @@ INT_PTR CALLBACK TracertDlgProc( 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); - PhReferenceObject(context); + EnableWindow(GetDlgItem(hwndDlg, IDC_REFRESH), FALSE); + PhReferenceObject(context); PhCreateThread2(NetworkTracertThreadStart, (PVOID)context); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); @@ -650,6 +652,26 @@ INT_PTR CALLBACK TracertDlgProc( case IDCANCEL: 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; @@ -700,24 +722,18 @@ INT_PTR CALLBACK TracertDlgProc( break; case NTM_RECEIVEDFINISH: { - PPH_STRING windowText; + 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); - 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; diff --git a/plugins/NetworkTools/tracert.h b/plugins/NetworkTools/tracert.h index 817a5f3f0c78..843ef15ad83f 100644 --- a/plugins/NetworkTools/tracert.h +++ b/plugins/NetworkTools/tracert.h @@ -104,6 +104,10 @@ TracertGetFilterSupportTreeList( VOID ); +VOID ClearTracertTree( + _In_ PNETWORK_TRACERT_CONTEXT Context + ); + PTRACERT_ROOT_NODE GetSelectedTracertNode( _In_ PNETWORK_TRACERT_CONTEXT Context ); From b35e5243e2bbfb3f27e9a256863c8e4c688cf2c6 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 19:05:48 +1000 Subject: [PATCH 331/839] NetworkTools: Fix tracert text capitalization --- plugins/NetworkTools/tracert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index ce47aaaada97..eb5d39c5a0fc 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -725,11 +725,11 @@ INT_PTR CALLBACK TracertDlgProc( EnableWindow(GetDlgItem(hwndDlg, IDC_REFRESH), TRUE); Static_SetText(context->WindowHandle, PhaFormatString( - L"Tracing %s... Complete", + 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.", + L"Tracing route to %s with %lu bytes of data... complete.", context->IpAddressString, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) )->Buffer); From 887b0fa754140bd76f9d0d9ab0b1922376081ca1 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 21:00:22 +1000 Subject: [PATCH 332/839] NetworkTools: Fix tracert refresh button not updating nodes --- plugins/NetworkTools/tracetree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 12aa70e1265c..1cf94b79f2ac 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -472,6 +472,8 @@ VOID ClearTracertTree( PhClearHashtable(Context->NodeHashtable); PhClearList(Context->NodeList); + + TreeNew_NodesStructured(Context->TreeNewHandle); } PTRACERT_ROOT_NODE GetSelectedTracertNode( From 9317b9f3d6b503957182eae0943ffe70c531c0a4 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 31 Jul 2017 21:00:48 +1000 Subject: [PATCH 333/839] Remove unused field from find handles window --- ProcessHacker/findobj.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 5cf0ab4a92e6..1e06baf50a0d 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -60,7 +60,6 @@ typedef struct _PH_HANDLE_SEARCH_CONTEXT PH_SORT_ORDER TreeNewSortOrder; PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; - PPH_LIST NodeRootList; HANDLE TimerQueueHandle; HANDLE UpdateTimerHandle; From 5460d9f1d3c04ddbc102a8e01c9e5a87715a0421 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 1 Aug 2017 12:20:01 +1000 Subject: [PATCH 334/839] Add subprocess column (feature request) --- ProcessHacker/include/proctree.h | 4 +++- ProcessHacker/proctree.c | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index 23c30a4f3b66..4c9ab88a7ab2 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -89,8 +89,9 @@ #define PHPRTLC_TIMESTAMP 76 #define PHPRTLC_FILEMODIFIEDTIME 77 #define PHPRTLC_FILESIZE 78 +#define PHPRTLC_SUBPROCESSCOUNT 79 -#define PHPRTLC_MAXIMUM 79 +#define PHPRTLC_MAXIMUM 80 #define PHPRTLC_IOGROUP_COUNT 9 #define PHPN_WSCOUNTERS 0x1 @@ -213,6 +214,7 @@ typedef struct _PH_PROCESS_NODE PPH_STRING TimeStampText; PPH_STRING FileModifiedTimeText; PPH_STRING FileSizeText; + PPH_STRING SubprocessCountText; // Graph buffers PH_GRAPH_BUFFERS CpuGraphBuffers; diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index a6cd292c6846..a7ce40df47a1 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -207,6 +207,7 @@ VOID PhInitializeProcessTreeList( 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); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_SUBPROCESSCOUNT, FALSE, L"Subprocesses", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); TreeNew_SetRedraw(hwnd, TRUE); @@ -561,6 +562,7 @@ VOID PhpRemoveProcessNode( PhClearReference(&ProcessNode->TimeStampText); PhClearReference(&ProcessNode->FileModifiedTimeText); PhClearReference(&ProcessNode->FileSizeText); + PhClearReference(&ProcessNode->SubprocessCountText); PhDeleteGraphBuffers(&ProcessNode->CpuGraphBuffers); PhDeleteGraphBuffers(&ProcessNode->PrivateGraphBuffers); @@ -2572,6 +2574,12 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getCellText->Text = node->FileSizeText->sr; } break; + case PHPRTLC_SUBPROCESSCOUNT: + { + PhMoveReference(&node->SubprocessCountText, PhFormatUInt64(node->Children->Count, -1)); + getCellText->Text = node->SubprocessCountText->sr; + } + break; default: return FALSE; } From 881f99636b96b99676624eace30f4c0f0ed200f7 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 1 Aug 2017 18:29:54 +1000 Subject: [PATCH 335/839] Fix subprocesses column sorting --- ProcessHacker/proctree.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index a7ce40df47a1..f0c591107a01 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1769,6 +1769,12 @@ BEGIN_SORT_FUNCTION(FileSize) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(Subprocesses) +{ + sortResult = int64cmp(node1->Children->Count, node2->Children->Count); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpProcessTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -1887,7 +1893,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( SORT_FUNCTION(CfGuard), SORT_FUNCTION(TimeStamp), SORT_FUNCTION(FileModifiedTime), - SORT_FUNCTION(FileSize) + SORT_FUNCTION(FileSize), + SORT_FUNCTION(Subprocesses) }; int (__cdecl *sortFunction)(const void *, const void *); From 14f52afb70235a3bf4eff6da9072ed1515b2aaf7 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 1 Aug 2017 18:33:59 +1000 Subject: [PATCH 336/839] Fix typo --- ProcessHacker/proctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index f0c591107a01..c45faf671101 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2583,7 +2583,7 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_SUBPROCESSCOUNT: { - PhMoveReference(&node->SubprocessCountText, PhFormatUInt64(node->Children->Count, -1)); + PhMoveReference(&node->SubprocessCountText, PhFormatUInt64(node->Children->Count, TRUE)); getCellText->Text = node->SubprocessCountText->sr; } break; From 829154d8dc02bf272d943f5de6ac58a4fc04ed52 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 2 Aug 2017 21:52:04 +1000 Subject: [PATCH 337/839] Update ntpsapi with undocumented JobObjectInformation classes --- phnt/include/ntpsapi.h | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 1be1843f6e1f..286184cee8bb 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1561,6 +1561,55 @@ NtCreateThreadEx( #if (PHNT_MODE != PHNT_MODE_KERNEL) +#define JobObjectBasicAccountingInformation 1 +#define JobObjectBasicLimitInformation 2 +#define JobObjectBasicProcessIdList 3 +#define JobObjectBasicUIRestrictions 4 +#define JobObjectSecurityLimitInformation 5 +#define JobObjectEndOfJobTimeInformation 6 +#define JobObjectAssociateCompletionPortInformation 7 +#define JobObjectBasicAndIoAccountingInformation 8 +#define JobObjectExtendedLimitInformation 9 +#define JobObjectJobSetInformation 10 +#define JobObjectGroupInformation 11 +#define JobObjectNotificationLimitInformation 12 +#define JobObjectLimitViolationInformation 13 +#define JobObjectGroupInformationEx 14 +#define JobObjectCpuRateControlInformation 15 +#define JobObjectCompletionFilter 16 +#define JobObjectCompletionCounter 17 +#define JobObjectFreezeInformation 18 +#define JobObjectExtendedAccountingInformation 19 +#define JobObjectWakeInformation 20 +#define JobObjectBackgroundInformation 21 +#define JobObjectSchedulingRankBiasInformation 22 +#define JobObjectTimerVirtualizationInformation 23 +#define JobObjectCycleTimeNotification 24 +#define JobObjectClearEvent 25 +#define JobObjectInterferenceInformation 26 +#define JobObjectClearPeakJobMemoryUsed 27 +#define JobObjectMemoryUsageInformation 28 +#define JobObjectSharedCommit 29 +#define JobObjectContainerId 30 +#define JobObjectIoRateControlInformation 31 +#define JobObjectNetRateControlInformation 32 +#define JobObjectNotificationLimitInformation2 33 +#define JobObjectLimitViolationInformation2 34 +#define JobObjectCreateSilo 35 +#define JobObjectSiloBasicInformation 36 +#define JobObjectSiloRootDirectory 37 +#define JobObjectServerSiloBasicInformation 38 +#define JobObjectServerSiloUserSharedData 39 +#define JobObjectServerSiloInitialize 40 +#define JobObjectServerSiloRunningState 41 +#define JobObjectIoAttribution 42 +#define JobObjectMemoryPartitionInformation 43 +#define JobObjectContainerTelemetryId 44 +#define JobObjectSiloSystemRoot 45 +#define JobObjectEnergyTrackingState 46 +#define JobObjectThreadImpersonationInformation 47 +#define MaxJobObjectInfoClass 48 + NTSYSCALLAPI NTSTATUS NTAPI From 01779e65f0197ee1490722c3db4f4093650172ae Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 2 Aug 2017 23:44:11 +1000 Subject: [PATCH 338/839] Update ntpsapi with undocumented JobObjectInformation structs --- phnt/include/ntpsapi.h | 104 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 286184cee8bb..cc957c2baf71 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1561,6 +1561,7 @@ NtCreateThreadEx( #if (PHNT_MODE != PHNT_MODE_KERNEL) +// JOBOBJECTINFOCLASS #define JobObjectBasicAccountingInformation 1 #define JobObjectBasicLimitInformation 2 #define JobObjectBasicProcessIdList 3 @@ -1610,6 +1611,109 @@ NtCreateThreadEx( #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 From cf57a21a132d8577c6226d0c7fb568fa4fe42df5 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 2 Aug 2017 23:47:33 +1000 Subject: [PATCH 339/839] Add HiddenProcessesMenuEnabled setting, Update hidden process detection window icon --- ProcessHacker/hidnproc.c | 5 ++++- ProcessHacker/mainwnd.c | 9 +++++---- ProcessHacker/settings.c | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index f30b4f664328..f3b342c8c2fc 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -101,7 +101,7 @@ VOID PhShowHiddenProcessesDialog( PhHiddenProcessesWindowHandle = CreateDialog( PhInstanceHandle, MAKEINTRESOURCE(IDD_HIDDENPROCESSES), - PhMainWndHandle, + NULL, PhpHiddenProcessesDlgProc ); } @@ -125,6 +125,9 @@ static INT_PTR CALLBACK PhpHiddenProcessesDlgProc( { 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); diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index f30c396d2eb5..184825f5cf2a 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2761,10 +2761,11 @@ VOID PhMwpInitializeSubMenu( } else if (Index == 2) // Tools { -#ifdef _WIN64 - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_HIDDENPROCESSES)) - PhDestroyEMenuItem(menuItem); -#endif + 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) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 1df0dbb05dfb..94df736bbbee 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -59,6 +59,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ForceNoParent", L"0"); PhpAddStringSetting(L"HandleTreeListColumns", L""); PhpAddStringSetting(L"HandleTreeListSort", L"0,1"); // 0, AscendingSortOrder + PhpAddIntegerSetting(L"HiddenProcessesMenuEnabled", L"0"); PhpAddStringSetting(L"HiddenProcessesListViewColumns", L""); PhpAddIntegerPairSetting(L"HiddenProcessesWindowPosition", L"400,400"); PhpAddScalableIntegerPairSetting(L"HiddenProcessesWindowSize", L"@96|520,400"); From 651adee893bded681e11212c5a808653c2670989 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 3 Aug 2017 00:05:02 +1000 Subject: [PATCH 340/839] Tidy up class names --- ProcessHacker/ProcessHacker.rc | 2 +- ProcessHacker/options.c | 2 +- ProcessHacker/proctree.c | 10 +++++----- ProcessHacker/sysinfo.c | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 1e94724c23cd..6ab929d692fc 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -758,7 +758,7 @@ BEGIN END IDD_HIDDENPROCESSES DIALOGEX 0, 0, 337, 221 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +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 diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 9e0232f4b06f..0c5ea7c7f833 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -279,7 +279,7 @@ static VOID PhpPageInit( MapWindowPoints(NULL, optionsWindow, (POINT *)&rect, 2); resetButton = CreateWindowEx( WS_EX_NOPARENTNOTIFY, - L"BUTTON", + WC_BUTTON, L"Reset", WS_CHILD | WS_VISIBLE | WS_TABSTOP, clientRect.right - rect.right, diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index c45faf671101..22a3d5ad63ec 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1028,17 +1028,17 @@ static VOID PhpUpdateProcessNodeImage( { if (!(ProcessNode->ValidMask & PHPN_IMAGE)) { - HANDLE processHandle; - PROCESS_BASIC_INFORMATION basicInfo; - PVOID imageBaseAddress; - PH_REMOTE_MAPPED_IMAGE mappedImage; - 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, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) { if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 4e8f019f4ff3..55513f538b9a 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -390,7 +390,7 @@ VOID PhSipOnShowWindow( } SeparatorControl = CreateWindow( - L"STATIC", + WC_STATIC, NULL, WS_CHILD | SS_OWNERDRAW, 0, @@ -404,7 +404,7 @@ VOID PhSipOnShowWindow( ); RestoreSummaryControl = CreateWindow( - L"STATIC", + WC_STATIC, NULL, WS_CHILD | WS_TABSTOP | SS_OWNERDRAW | SS_NOTIFY, 0, @@ -1143,7 +1143,7 @@ PPH_SYSINFO_SECTION PhSipCreateSection( section->PanelId = IDDYNAMIC + SectionList->Count * 2 + 2; section->PanelHandle = CreateWindow( - L"STATIC", + WC_STATIC, NULL, WS_CHILD | SS_OWNERDRAW | SS_NOTIFY, 0, From 123be3619130b07f7c98caef8182f36fe7513547 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 3 Aug 2017 17:00:12 +1000 Subject: [PATCH 341/839] Fix debug menu not launching 32bit debugger for WoW64 processes --- ProcessHacker/actions.c | 55 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index d039f5ed237b..71ead0b015a2 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -59,7 +59,6 @@ static PWSTR DangerousProcesses[] = }; 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; @@ -1397,9 +1396,17 @@ BOOLEAN PhUiDebugProcess( _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")) { @@ -1419,39 +1426,33 @@ BOOLEAN PhUiDebugProcess( 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; + status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, +#ifdef _WIN64 + Process->IsWow64 ? &aeDebugWow64KeyName : &aeDebugKeyName, +#else + &aeDebugKeyName, +#endif + 0 + ); - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &aeDebugKeyName, - 0 - ))) + if (NT_SUCCESS(status)) + { + if (debugger = PH_AUTO(PhQueryRegistryString(keyHandle, L"Debugger"))) { - if (debugger = PH_AUTO(PhQueryRegistryString(keyHandle, L"Debugger"))) + if (PhSplitStringRefAtChar(&debugger->sr, '"', &dummy, &commandPart) && + PhSplitStringRefAtChar(&commandPart, '"', &commandPart, &dummy)) { - if (PhSplitStringRefAtChar(&debugger->sr, '"', &dummy, &commandPart) && - PhSplitStringRefAtChar(&commandPart, '"', &commandPart, &dummy)) - { - DebuggerCommand = PhCreateString2(&commandPart); - } + DebuggerCommand = PhCreateString2(&commandPart); } - - NtClose(keyHandle); } - PhEndInitOnce(&DebuggerCommandInitOnce); + NtClose(keyHandle); } - if (!DebuggerCommand) + if (PhIsNullOrEmptyString(DebuggerCommand)) { PhShowError(hWnd, L"Unable to locate the debugger."); return FALSE; @@ -1462,7 +1463,7 @@ BOOLEAN PhUiDebugProcess( PhAppendCharStringBuilder(&commandLineBuilder, '"'); PhAppendStringBuilder(&commandLineBuilder, &DebuggerCommand->sr); PhAppendCharStringBuilder(&commandLineBuilder, '"'); - PhAppendFormatStringBuilder(&commandLineBuilder, L" -p %u", HandleToUlong(Process->ProcessId)); + PhAppendFormatStringBuilder(&commandLineBuilder, L" -p %lu", HandleToUlong(Process->ProcessId)); status = PhCreateProcessWin32( NULL, From 581416405e399cbbae53854678b8d78e95ac36c4 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 3 Aug 2017 21:21:12 +1000 Subject: [PATCH 342/839] Add status message when KPH fails to load --- ProcessHacker/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 216a7f81a09a..0d1cc99b1db5 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -558,6 +558,7 @@ VOID PhInitializeKph( { static PH_STRINGREF kprocesshacker = PH_STRINGREF_INIT(L"kprocesshacker.sys"); static PH_STRINGREF processhackerSig = PH_STRINGREF_INIT(L"ProcessHacker.sig"); + NTSTATUS status; PPH_STRING kprocesshackerFileName; PPH_STRING processhackerSigFileName; KPH_PARAMETERS parameters; @@ -573,7 +574,11 @@ VOID PhInitializeKph( parameters.SecurityLevel = KphSecuritySignatureCheck; parameters.CreateDynamicConfiguration = TRUE; - if (NT_SUCCESS(KphConnect2Ex(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters))) + if (NT_SUCCESS(status = KphConnect2Ex( + KPH_DEVICE_SHORT_NAME, + kprocesshackerFileName->Buffer, + ¶meters + ))) { if (signature = PhpReadSignature(processhackerSigFileName->Buffer, &signatureSize)) { @@ -581,6 +586,10 @@ VOID PhInitializeKph( PhFree(signature); } } + else + { + PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); + } PhDereferenceObject(kprocesshackerFileName); PhDereferenceObject(processhackerSigFileName); From 5eb68bb76dcacb5ff8d9f756634af9c6b12354d7 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 3 Aug 2017 21:22:00 +1000 Subject: [PATCH 343/839] OnlineChecks: Move sendto menu on the modules tab --- plugins/OnlineChecks/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index dc1ee8297a9d..faaf6dc9c7fe 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -258,6 +258,7 @@ VOID NTAPI MainMenuInitializingCallback( } PPH_EMENU_ITEM CreateSendToMenu( + _In_ BOOLEAN ProcessesMenu, _In_ PPH_EMENU_ITEM Parent, _In_ PPH_STRING FileName ) @@ -270,7 +271,7 @@ PPH_EMENU_ITEM CreateSendToMenu( 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)) + if (ProcessesMenu && (menuItem = PhFindEMenuItem(Parent, PH_EMENU_FIND_STARTSWITH, L"Search online", 0))) { insertIndex = PhIndexOfEMenuItem(Parent, menuItem); PhInsertEMenuItem(Parent, sendToMenu, insertIndex + 1); @@ -299,7 +300,7 @@ VOID NTAPI ProcessMenuInitializingCallback( else processItem = NULL; - sendToMenu = CreateSendToMenu(menuInfo->Menu, processItem ? processItem->FileName : 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) @@ -322,7 +323,7 @@ VOID NTAPI ModuleMenuInitializingCallback( else moduleItem = NULL; - sendToMenu = CreateSendToMenu(menuInfo->Menu, moduleItem ? moduleItem->FileName : NULL); + sendToMenu = CreateSendToMenu(FALSE, menuInfo->Menu, moduleItem ? moduleItem->FileName : NULL); if (!moduleItem) { @@ -364,7 +365,7 @@ VOID NTAPI ServiceMenuInitializingCallback( PhAutoDereferenceObject(serviceFileName); } - sendToMenu = CreateSendToMenu(menuInfo->Menu, serviceFileName ? serviceFileName : NULL); + sendToMenu = CreateSendToMenu(FALSE, menuInfo->Menu, serviceFileName ? serviceFileName : NULL); if (!serviceItem || !serviceFileName) { From 9f69ed72dfac1ed548c0e704420eb465f213fcad Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 09:16:38 +1000 Subject: [PATCH 344/839] OnlineChecks: Fix use-after-free --- plugins/OnlineChecks/main.c | 34 +++++++------------- plugins/OnlineChecks/onlnchk.h | 11 +++++-- plugins/OnlineChecks/upload.c | 57 +++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index faaf6dc9c7fe..502d5cf853f8 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -209,6 +209,12 @@ VOID NTAPI MenuItemCallback( case MENUITEM_JOTTI_UPLOAD: UploadToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD); break; + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE); + break; + case MENUITEM_JOTTI_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD_SERVICE); + break; case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: { static PH_FILETYPE_FILTER filters[] = @@ -339,35 +345,19 @@ VOID NTAPI ServiceMenuInitializingCallback( 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; - if (serviceItem) - { - QueryServiceFileName( - &serviceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ); - } - - if (serviceBinaryPath) - PhDereferenceObject(serviceBinaryPath); - - if (serviceFileName) - { - // TODO: memory leak or possible use-after-free bug? - PhAutoDereferenceObject(serviceFileName); - } - - sendToMenu = CreateSendToMenu(FALSE, menuInfo->Menu, serviceFileName ? serviceFileName : NULL); + sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Send to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"virustotal.com", serviceItem ? serviceItem : NULL), -1); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", serviceItem ? serviceItem : NULL), -1); + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); + PhInsertEMenuItem(menuInfo->Menu, sendToMenu, -1); - if (!serviceItem || !serviceFileName) + if (!serviceItem) { sendToMenu->Flags |= PH_EMENU_DISABLED; } diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 79b1ff043651..ebd5e33898e5 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -200,14 +200,21 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( #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 +#define MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE 103 +#define MENUITEM_VIRUSTOTAL_UPLOAD_FILE 104 +#define MENUITEM_JOTTI_UPLOAD 105 +#define MENUITEM_JOTTI_UPLOAD_SERVICE 106 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, diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 11948fefec83..1e5ca87291cd 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -25,10 +25,13 @@ #include PPH_OBJECT_TYPE UploadContextType = NULL; +PH_INITONCE UploadContextTypeInitOnce = PH_INITONCE_INIT; SERVICE_INFO UploadServiceInfo[] = { { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"???", L"file" }, + { MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, 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[]" }, + { MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, }; BOOL ReadRequestString( @@ -794,6 +797,7 @@ NTSTATUS UploadFileThreadStart( switch (context->Service) { case MENUITEM_VIRUSTOTAL_UPLOAD: + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: { PSTR buffer = NULL; PSTR redirectUrl; @@ -842,6 +846,7 @@ NTSTATUS UploadFileThreadStart( } break; case MENUITEM_JOTTI_UPLOAD: + case MENUITEM_JOTTI_UPLOAD_SERVICE: { PSTR buffer = NULL; PSTR redirectUrl; @@ -958,7 +963,8 @@ NTSTATUS UploadCheckThreadStart( if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64))) { - if (context->Service == MENUITEM_VIRUSTOTAL_UPLOAD) + if (context->Service == MENUITEM_VIRUSTOTAL_UPLOAD || + context->Service == MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE) { if (fileSize64.QuadPart < 32 * 1024 * 1024) { @@ -1012,6 +1018,7 @@ NTSTATUS UploadCheckThreadStart( switch (context->Service) { case MENUITEM_VIRUSTOTAL_UPLOAD: + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: { PSTR uploadUrl = NULL; PSTR quote = NULL; @@ -1114,6 +1121,7 @@ NTSTATUS UploadCheckThreadStart( } break; case MENUITEM_JOTTI_UPLOAD: + case MENUITEM_JOTTI_UPLOAD_SERVICE: { // Create the default upload URL context->UploadUrl = PhFormatString(L"https://virusscan.jotti.org%s", serviceInfo->UploadObjectName); @@ -1276,14 +1284,12 @@ VOID UploadToOnlineService( _In_ ULONG Service ) { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - PUPLOAD_CONTEXT context; - if (PhBeginInitOnce(&initOnce)) + if (PhBeginInitOnce(&UploadContextTypeInitOnce)) { UploadContextType = PhCreateObjectType(L"OnlineChecksObjectType", 0, UploadContextDeleteProcedure); - PhEndInitOnce(&initOnce); + PhEndInitOnce(&UploadContextTypeInitOnce); } context = (PUPLOAD_CONTEXT)PhCreateObject(sizeof(UPLOAD_CONTEXT), UploadContextType); @@ -1296,3 +1302,44 @@ VOID UploadToOnlineService( PhCreateThread2(ShowUpdateDialogThread, (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 = (PUPLOAD_CONTEXT)PhCreateObject(sizeof(UPLOAD_CONTEXT), UploadContextType); + memset(context, 0, sizeof(UPLOAD_CONTEXT)); + + context->Service = Service; + context->FileName = serviceFileName; + context->BaseFileName = PhGetBaseName(context->FileName); + + PhCreateThread2(ShowUpdateDialogThread, (PVOID)context); + } + else + { + PhShowStatus(PhMainWndHandle, L"Unable to query the service", status, 0); + } + + if (serviceBinaryPath) + PhDereferenceObject(serviceBinaryPath); +} \ No newline at end of file From 7f7f3d2bddbaad4604272c812a269c37b5eabf0d Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 09:53:29 +1000 Subject: [PATCH 345/839] Add EnableKphWarnings setting --- ProcessHacker/main.c | 3 ++- ProcessHacker/settings.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 0d1cc99b1db5..fe24cc0c7568 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -588,7 +588,8 @@ VOID PhInitializeKph( } else { - PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); + if (PhGetIntegerSetting(L"EnableWarnings")) + PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); } PhDereferenceObject(kprocesshackerFileName); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 94df736bbbee..bbcfc1746789 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -41,6 +41,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableCycleCpuUsage", L"1"); PhpAddIntegerSetting(L"EnableInstantTooltips", L"0"); PhpAddIntegerSetting(L"EnableKph", L"1"); + PhpAddIntegerSetting(L"EnableKphWarnings", L"1"); PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"1"); PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); From 540eca62d06e66802b8e065a8a64f2f7e13900b3 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 11:46:38 +1000 Subject: [PATCH 346/839] Fix typo --- ProcessHacker/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index fe24cc0c7568..6d2b40ec275c 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -588,7 +588,7 @@ VOID PhInitializeKph( } else { - if (PhGetIntegerSetting(L"EnableWarnings")) + if (PhGetIntegerSetting(L"EnableKphWarnings")) PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); } From bd1b0debd3e51fe6f020763a892ddf9365bdf7f7 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 12:14:06 +1000 Subject: [PATCH 347/839] Fix github file encoding --- tools/GenerateZw/GenerateZw/Program.cs | 2 +- tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs | 2 +- tools/GenerateZw/GenerateZw/ZwGen.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/GenerateZw/GenerateZw/Program.cs b/tools/GenerateZw/GenerateZw/Program.cs index 20ba3a89ee3d..1f857a046f9c 100644 --- a/tools/GenerateZw/GenerateZw/Program.cs +++ b/tools/GenerateZw/GenerateZw/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs b/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs index 49da97e1ccc5..418173296108 100644 --- a/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs +++ b/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/tools/GenerateZw/GenerateZw/ZwGen.cs b/tools/GenerateZw/GenerateZw/ZwGen.cs index 737610f9e935..de079cd659c5 100644 --- a/tools/GenerateZw/GenerateZw/ZwGen.cs +++ b/tools/GenerateZw/GenerateZw/ZwGen.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; From 39270e371518b243dd50dfa67fa0efcb2d356e0a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 13:06:42 +1000 Subject: [PATCH 348/839] Add TokenSplitterEnable setting, Disable token properties splitter by default --- ProcessHacker/include/splitter.h | 38 +------------- ProcessHacker/prpgtok.c | 10 ++++ ProcessHacker/settings.c | 1 + ProcessHacker/splitter.c | 87 +++++++++++++++++++++++++++++--- ProcessHacker/tokprp.c | 21 ++++---- 5 files changed, 104 insertions(+), 53 deletions(-) diff --git a/ProcessHacker/include/splitter.h b/ProcessHacker/include/splitter.h index 75be46d7593b..9901b2ea6fcc 100644 --- a/ProcessHacker/include/splitter.h +++ b/ProcessHacker/include/splitter.h @@ -1,45 +1,11 @@ #ifndef PH_HSPLITTER_H #define PH_HSPLITTER_H -typedef struct _PH_HSPLITTER_CONTEXT -{ - union - { - ULONG Flags; - struct - { - ULONG Hot : 1; - ULONG HasFocus : 1; - ULONG Spare : 30; - }; - }; - - LONG SplitterOffset; - - HWND Window; - HWND ParentWindow; - HWND TopWindow; - HWND BottomWindow; - - HBRUSH FocusBrush; - HBRUSH HotBrush; - HBRUSH NormalBrush; -} PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; - -PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( +VOID PhInitializeHSplitter( + _In_ PWSTR SettingName, _In_ HWND ParentWindow, _In_ HWND TopWindow, _In_ HWND BottomWindow ); -VOID PhDeleteHSplitter( - _Inout_ PPH_HSPLITTER_CONTEXT Context - ); - -VOID PhHSplitterHandleWmSize( - _Inout_ PPH_HSPLITTER_CONTEXT Context, - _In_ INT Width, - _In_ INT Height - ); - #endif diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index d6de9b5eaf9f..d19a654673e9 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -24,6 +24,8 @@ #include #include +#include + NTSTATUS NTAPI PhpOpenProcessTokenForPage( _Out_ PHANDLE Handle, _In_ ACCESS_MASK DesiredAccess, @@ -85,6 +87,14 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_ADVANCED), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + if (!PhGetIntegerSetting(L"TokenSplitterEnable")) + { + 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); + } + PhDoPropPageLayout(hwndDlg); SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)TRUE); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index bbcfc1746789..7cd244685a9a 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -150,6 +150,7 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"ThreadStackTreeListColumns", L""); PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,400"); PhpAddStringSetting(L"TokenGroupsListViewColumns", L""); + PhpAddIntegerSetting(L"TokenSplitterEnable", L"0"); PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 4de4d68eb34f..208e3c22d8e9 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -28,6 +28,43 @@ #define SPLITTER_PADDING 6 +typedef struct _PH_HSPLITTER_CONTEXT +{ + union + { + ULONG Flags; + struct + { + ULONG Hot : 1; + ULONG HasFocus : 1; + ULONG Spare : 30; + }; + }; + + LONG SplitterOffset; + + HWND Window; + HWND ParentWindow; + HWND TopWindow; + HWND BottomWindow; + + HBRUSH FocusBrush; + HBRUSH HotBrush; + HBRUSH NormalBrush; + + PPH_STRING SettingName; +} PH_HSPLITTER_CONTEXT, *PPH_HSPLITTER_CONTEXT; + +VOID PhDeleteHSplitter( + _Inout_ PPH_HSPLITTER_CONTEXT Context + ); + +VOID PhHSplitterHandleWmSize( + _Inout_ PPH_HSPLITTER_CONTEXT Context, + _In_ INT Width, + _In_ INT Height + ); + LONG GetWindowWidth(HWND hwnd) { RECT Rect; @@ -60,7 +97,12 @@ LONG GetClientWindowHeight(HWND hwnd) return (Rect.bottom - Rect.top); } -LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK HSplitterWindowProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) { PPH_HSPLITTER_CONTEXT context = NULL; @@ -217,7 +259,38 @@ LRESULT CALLBACK HSplitterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM return DefWindowProc(hwnd, uMsg, wParam, lParam); } -PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( +LRESULT CALLBACK HSplitterParentWindowProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ UINT_PTR uIdSubclass, + _In_ ULONG_PTR dwRefData + ) +{ + PPH_HSPLITTER_CONTEXT context = (PPH_HSPLITTER_CONTEXT)dwRefData; + + switch (uMsg) + { + case WM_DESTROY: + { + RemoveWindowSubclass(hwnd, HSplitterParentWindowProc, uIdSubclass); + + PhDeleteHSplitter(context); + } + break; + case WM_SIZE: + { + PhHSplitterHandleWmSize(context, LOWORD(lParam), HIWORD(lParam)); + } + break; + } + + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + +VOID PhInitializeHSplitter( + _In_ PWSTR SettingName, _In_ HWND ParentWindow, _In_ HWND TopWindow, _In_ HWND BottomWindow @@ -228,11 +301,12 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( context = PhAllocate(sizeof(PH_HSPLITTER_CONTEXT)); memset(context, 0, sizeof(PH_HSPLITTER_CONTEXT)); - + + context->SettingName = PhCreateString(SettingName); context->ParentWindow = ParentWindow; context->TopWindow = TopWindow; context->BottomWindow = BottomWindow; - context->SplitterOffset = PhGetIntegerSetting(L"TokenSplitterPosition"); + context->SplitterOffset = PhGetIntegerSetting(SettingName); context->FocusBrush = CreateSolidBrush(RGB(0x0, 0x0, 0x0)); context->HotBrush = CreateSolidBrush(RGB(0x44, 0x44, 0x44)); context->NormalBrush = GetSysColorBrush(COLOR_WINDOW); @@ -276,14 +350,15 @@ PPH_HSPLITTER_CONTEXT PhInitializeHSplitter( ShowWindow(context->Window, SW_SHOW); UpdateWindow(context->Window); - return context; + SetWindowSubclass(ParentWindow, HSplitterParentWindowProc, 0, (ULONG_PTR)context); } VOID PhDeleteHSplitter( _Inout_ PPH_HSPLITTER_CONTEXT Context ) { - PhSetIntegerSetting(L"TokenSplitterPosition", Context->SplitterOffset); + PhSetIntegerSetting(PhGetString(Context->SettingName), Context->SplitterOffset); + PhDereferenceObject(Context->SettingName); if (Context->FocusBrush) DeleteObject(Context->FocusBrush); diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index bfbd1f002a85..24cb7c718740 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -52,7 +52,6 @@ typedef struct _TOKEN_PAGE_CONTEXT HWND GroupsListViewHandle; HWND PrivilegesListViewHandle; - PPH_HSPLITTER_CONTEXT HSplitterContext; PTOKEN_GROUPS Groups; PTOKEN_PRIVILEGES Privileges; @@ -428,12 +427,6 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSetControlTheme(groupsLv, L"explorer"); PhSetControlTheme(privilegesLv, L"explorer"); - tokenPageContext->HSplitterContext = PhInitializeHSplitter( - hwndDlg, - tokenPageContext->GroupsListViewHandle, - tokenPageContext->PrivilegesListViewHandle - ); - PhAddListViewColumn(groupsLv, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); PhAddListViewColumn(groupsLv, 1, 1, 1, LVCFMT_LEFT, 200, L"Flags"); @@ -579,6 +572,16 @@ INT_PTR CALLBACK PhpTokenPageProc( NtClose(tokenHandle); } + + if (PhGetIntegerSetting(L"TokenSplitterEnable")) + { + PhInitializeHSplitter( + L"TokenSplitterPosition", + hwndDlg, + tokenPageContext->GroupsListViewHandle, + tokenPageContext->PrivilegesListViewHandle + ); + } } break; case WM_DESTROY: @@ -588,7 +591,6 @@ INT_PTR CALLBACK PhpTokenPageProc( if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); - if (tokenPageContext->HSplitterContext) PhDeleteHSplitter(tokenPageContext->HSplitterContext); } break; case WM_COMMAND: @@ -922,9 +924,6 @@ INT_PTR CALLBACK PhpTokenPageProc( } } break; - case WM_SIZE: - PhHSplitterHandleWmSize(tokenPageContext->HSplitterContext, LOWORD(lParam), HIWORD(lParam)); - break; } REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->GroupsListViewHandle, uMsg, wParam, lParam); From 98c361ec26e4b26346e5a41b7cce052d12b28b2e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 15:05:00 +1000 Subject: [PATCH 349/839] Add consistency check --- ProcessHacker/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 6d2b40ec275c..df3481b33535 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -588,7 +588,7 @@ VOID PhInitializeKph( } else { - if (PhGetIntegerSetting(L"EnableKphWarnings")) + if (PhGetIntegerSetting(L"EnableKphWarnings") && PhGetOwnTokenAttributes().Elevated) PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); } From ae57d1530396cf1910f2bc5bc0ed654b85438533 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 16:07:30 +1000 Subject: [PATCH 350/839] Add ShowHexId setting (shows PIDs and TIDs as hexadecimal) --- ProcessHacker/include/phsettings.h | 1 + ProcessHacker/procprv.c | 8 +++++++- ProcessHacker/settings.c | 2 ++ ProcessHacker/thrdprv.c | 7 ++++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h index d722745c8815..4f79cc7a69eb 100644 --- a/ProcessHacker/include/phsettings.h +++ b/ProcessHacker/include/phsettings.h @@ -17,6 +17,7 @@ EXT ULONG PhCsHighlightingDuration; EXT ULONG PhCsPropagateCpuUsage; EXT ULONG PhCsScrollToNewProcesses; EXT ULONG PhCsShowCpuBelow001; +EXT ULONG PhCsShowHexId; EXT ULONG PhCsUpdateInterval; EXT ULONG PhCsColorNew; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index a6a1727ba6de..c30b5412d012 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -51,6 +51,7 @@ */ #include +#include #include #include @@ -457,7 +458,12 @@ PPH_PROCESS_ITEM PhCreateProcessItem( processItem->ProcessId = ProcessId; if (!PH_IS_FAKE_PROCESS_ID(ProcessId)) - PhPrintUInt32(processItem->ProcessIdString, HandleToUlong(ProcessId)); + { + if (PhCsShowHexId) + _ultow(HandleToUlong(ProcessId), processItem->ProcessIdString, 16); + else + PhPrintUInt32(processItem->ProcessIdString, HandleToUlong(ProcessId)); + } // Create the statistics buffers. PhInitializeCircularBuffer_FLOAT(&processItem->CpuKernelHistory, PhStatisticsSampleCount); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 7cd244685a9a..421f334f821a 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -137,6 +137,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerPairSetting(L"SessionShadowHotkey", L"106,2"); // VK_MULTIPLY,KBDCTRL PhpAddIntegerSetting(L"ShowCommitInSummary", L"1"); PhpAddIntegerSetting(L"ShowCpuBelow001", L"0"); + PhpAddIntegerSetting(L"ShowHexId", L"1"); PhpAddIntegerSetting(L"StartHidden", L"0"); PhpAddIntegerSetting(L"SysInfoWindowAlwaysOnTop", L"0"); PhpAddIntegerSetting(L"SysInfoWindowOneGraphPerCpu", L"0"); @@ -213,6 +214,7 @@ VOID PhUpdateCachedSettings( PH_UPDATE_SETTING(PropagateCpuUsage); PH_UPDATE_SETTING(ScrollToNewProcesses); PH_UPDATE_SETTING(ShowCpuBelow001); + PH_UPDATE_SETTING(ShowHexId); PH_UPDATE_SETTING(UpdateInterval); PH_UPDATE_SETTING(ColorNew); PH_UPDATE_SETTING(ColorRemoved); diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index a0cf4bee575b..3be1e25a73e2 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -387,7 +388,11 @@ PPH_THREAD_ITEM PhCreateThreadItem( ); memset(threadItem, 0, sizeof(PH_THREAD_ITEM)); threadItem->ThreadId = ThreadId; - PhPrintUInt32(threadItem->ThreadIdString, HandleToUlong(ThreadId)); + + if (PhCsShowHexId) + _ultow(HandleToUlong(ThreadId), threadItem->ThreadIdString, 16); + else + PhPrintUInt32(threadItem->ThreadIdString, HandleToUlong(ThreadId)); PhEmCallObjectOperation(EmThreadItemType, threadItem, EmObjectCreate); From 25a22fdafd39c3dc5d4190dd81d4c03e7087e5dc Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 4 Aug 2017 16:08:50 +1000 Subject: [PATCH 351/839] Disable ShowHexId setting by default --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 421f334f821a..28994dd78cbe 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -137,7 +137,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerPairSetting(L"SessionShadowHotkey", L"106,2"); // VK_MULTIPLY,KBDCTRL PhpAddIntegerSetting(L"ShowCommitInSummary", L"1"); PhpAddIntegerSetting(L"ShowCpuBelow001", L"0"); - PhpAddIntegerSetting(L"ShowHexId", L"1"); + PhpAddIntegerSetting(L"ShowHexId", L"0"); PhpAddIntegerSetting(L"StartHidden", L"0"); PhpAddIntegerSetting(L"SysInfoWindowAlwaysOnTop", L"0"); PhpAddIntegerSetting(L"SysInfoWindowOneGraphPerCpu", L"0"); From fee5f1b3769d605dcfa4e0e334adf895fc886609 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 6 Aug 2017 22:52:39 +1000 Subject: [PATCH 352/839] Update ntexapi.h with undocumented SYSDBG types --- phnt/include/ntexapi.h | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 5a42dbf6bffa..760260980a90 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -3152,6 +3152,51 @@ typedef struct _SYSDBG_TRIAGE_DUMP PHANDLE Handles; } SYSDBG_TRIAGE_DUMP, *PSYSDBG_TRIAGE_DUMP; +// private +typedef struct _SYSDBG_LIVEDUMP_CONTROL_FLAGS +{ + union + { + struct + { + ULONG UseDumpStorageStack : 1; + ULONG CompressMemoryPagesData : 1; + ULONG IncludeUserSpaceMemoryPages : 1; + ULONG Reserved : 28; + }; + ULONG AsUlong; + }; +} SYSDBG_LIVEDUMP_CONTROL_FLAGS, *PSYSDBG_LIVEDUMP_CONTROL_FLAGS; + +// private +typedef struct _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES +{ + union + { + struct + { + ULONG HypervisorPages : 1; + ULONG Reserved : 31; + }; + ULONG AsUlong; + }; +} SYSDBG_LIVEDUMP_CONTROL_ADDPAGES, *PSYSDBG_LIVEDUMP_CONTROL_ADDPAGES; + +// 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 From 02dda7743335a179c869774a3ae0b1815d96e63a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 7 Aug 2017 13:20:17 +1000 Subject: [PATCH 353/839] ToolStatus: Hide hybrid shutdown on unsupported OS --- plugins/ToolStatus/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index afd88f8ced2c..cbd82cbdfe67 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -855,6 +855,16 @@ LRESULT CALLBACK MainWndSubclassProc( 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); + + 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; } From 24fc6c344060a497add2fa3a225bcb33a87a61c5 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 7 Aug 2017 22:56:23 +1000 Subject: [PATCH 354/839] Add missing case from previous commit --- plugins/ToolStatus/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index cbd82cbdfe67..d358c8a64d7d 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -983,6 +983,16 @@ LRESULT CALLBACK MainWndSubclassProc( 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); + + 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); From 94131677af118701fe5c0019c2cb17ee56f92df6 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 16:01:22 +1000 Subject: [PATCH 355/839] Improve KPH error checking, Fix issue with unoffical builds and empty handles tab --- ProcessHacker/main.c | 45 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index df3481b33535..f5aae4442a07 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -517,8 +517,9 @@ VOID PhInitializeFont( } } -PUCHAR PhpReadSignature( +NTSTATUS PhpReadSignature( _In_ PWSTR FileName, + _Out_ PUCHAR *Signature, _Out_ PULONG SignatureSize ) { @@ -528,10 +529,10 @@ PUCHAR PhpReadSignature( ULONG bufferSize; IO_STATUS_BLOCK iosb; - if (!NT_SUCCESS(PhCreateFileWin32(&fileHandle, FileName, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, + 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 NULL; + return status; } bufferSize = 1024; @@ -542,13 +543,14 @@ PUCHAR PhpReadSignature( if (NT_SUCCESS(status)) { + *Signature = signature; *SignatureSize = (ULONG)iosb.Information; - return signature; + return status; } else { PhFree(signature); - return NULL; + return status; } } @@ -562,8 +564,6 @@ VOID PhInitializeKph( PPH_STRING kprocesshackerFileName; PPH_STRING processhackerSigFileName; KPH_PARAMETERS parameters; - PUCHAR signature; - ULONG signatureSize; if (WindowsVersion == WINDOWS_NEW) return; @@ -571,7 +571,7 @@ VOID PhInitializeKph( kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); - parameters.SecurityLevel = KphSecuritySignatureCheck; + parameters.SecurityLevel = KphSecuritySignatureAndPrivilegeCheck; parameters.CreateDynamicConfiguration = TRUE; if (NT_SUCCESS(status = KphConnect2Ex( @@ -580,11 +580,36 @@ VOID PhInitializeKph( ¶meters ))) { - if (signature = PhpReadSignature(processhackerSigFileName->Buffer, &signatureSize)) + PUCHAR signature; + ULONG signatureSize; + + status = PhpReadSignature( + processhackerSigFileName->Buffer, + &signature, + &signatureSize + ); + + if (NT_SUCCESS(status)) { - KphVerifyClient(signature, signatureSize); + status = KphVerifyClient(signature, signatureSize); + + if (!NT_SUCCESS(status)) + { + if (PhGetIntegerSetting(L"EnableKphWarnings")) + PhShowStatus(NULL, L"Unable to verify the kernel driver signature.", status, 0); + + KphDisconnect(); + } + PhFree(signature); } + else + { + if (PhGetIntegerSetting(L"EnableKphWarnings")) + PhShowStatus(NULL, L"Unable to load the kernel driver signature.", status, 0); + + KphDisconnect(); + } } else { From 62542aa2796d095a7e8963cdf4cb7047379a16c3 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 17:26:20 +1000 Subject: [PATCH 356/839] Fix previous commit Fix kimbers lewd disconnect --- ProcessHacker/main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index f5aae4442a07..0291db3d0c94 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -597,8 +597,6 @@ VOID PhInitializeKph( { if (PhGetIntegerSetting(L"EnableKphWarnings")) PhShowStatus(NULL, L"Unable to verify the kernel driver signature.", status, 0); - - KphDisconnect(); } PhFree(signature); @@ -607,8 +605,6 @@ VOID PhInitializeKph( { if (PhGetIntegerSetting(L"EnableKphWarnings")) PhShowStatus(NULL, L"Unable to load the kernel driver signature.", status, 0); - - KphDisconnect(); } } else From 3ab28ca091f538b17f70487652e897c12a57799c Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 23:19:04 +1000 Subject: [PATCH 357/839] Setup: Fix setup uninstall, Fix all users desktop shortcut, Add initial websetup support --- tools/CustomBuildTool/Source Files/Build.cs | 130 +++++++++++++--- tools/CustomBuildTool/Source Files/Program.cs | 55 +++++-- tools/CustomBuildTool/Source Files/Utils.cs | 52 +++---- .../CustomSetupTool/CustomSetupTool.vcxproj | 13 +- .../CustomSetupTool.vcxproj.filters | 35 +++-- .../CustomSetupTool/CustomSetupTool/appsup.c | 28 +++- .../CustomSetupTool/{page3.c => configpage.c} | 2 +- .../CustomSetupTool/download.c | 65 +++++--- .../{page5.c => downloadpage.c} | 8 +- .../CustomSetupTool/{error.c => errorpage.c} | 0 .../CustomSetupTool/CustomSetupTool/extract.c | 4 +- .../CustomSetupTool/include/appsup.h | 4 + .../CustomSetupTool/include/setup.h | 27 +++- .../{page4.c => installpage.c} | 5 +- .../{page2.c => licencepage.c} | 0 tools/CustomSetupTool/CustomSetupTool/main.c | 7 +- .../CustomSetupTool/resources/version.rc | 10 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 146 +++++++++++------- .../CustomSetupTool/{page1.c => startpage.c} | 3 + .../CustomSetupTool/uninstall.c | 9 +- .../CustomSetupTool/updatesetup.c | 29 ++++ 21 files changed, 432 insertions(+), 200 deletions(-) rename tools/CustomSetupTool/CustomSetupTool/{page3.c => configpage.c} (98%) rename tools/CustomSetupTool/CustomSetupTool/{page5.c => downloadpage.c} (94%) rename tools/CustomSetupTool/CustomSetupTool/{error.c => errorpage.c} (100%) rename tools/CustomSetupTool/CustomSetupTool/{page4.c => installpage.c} (98%) rename tools/CustomSetupTool/CustomSetupTool/{page2.c => licencepage.c} (100%) rename tools/CustomSetupTool/CustomSetupTool/{page1.c => startpage.c} (97%) create mode 100644 tools/CustomSetupTool/CustomSetupTool/updatesetup.c diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index a75528a64197..23ed51105d21 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -33,26 +33,34 @@ namespace CustomBuildTool public static class Build { private static DateTime TimeStart; - private static bool GitExportBuild; private static bool BuildNightly; + private static bool GitExportBuild; + private static string GitExePath; + private static string MSBuildExePath; + private static string CertUtilExePath; + private static string MakeAppxExePath; + private static string CustomSignToolPath; + private static string BuildBranch; private static string BuildOutputFolder; private static string BuildCommit; private static string BuildVersion; + private static string BuildWebSetupVersion; private static string BuildLongVersion; private static string BuildCount; private static string BuildRevision; private static string BuildMessage; - private static long BuildSetupFileLength; + private static long BuildBinFileLength; - private static string BuildSetupHash; private static string BuildBinHash; + + private static long BuildSetupFileLength; + private static string BuildSetupHash; private static string BuildSetupSig; - private static string GitExePath; - private static string MSBuildExePath; - private static string CertUtilExePath; - private static string MakeAppxExePath; - private static string CustomSignToolPath; + + private static long BuildWebSetupFileLength; + private static string BuildWebSetupHash; + private static string BuildWebSetupSig; #region Build Config private static readonly string[] sdk_directories = @@ -663,11 +671,48 @@ public static bool CopyKeyFiles() return true; } + public static bool BuildWebSetupExe() + { + 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("Building build-setup.exe...", ConsoleColor.Cyan); - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildVerbose)) + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildApi)) return false; try @@ -802,16 +847,22 @@ public static bool BuildChecksumsFile() 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"); @@ -830,7 +881,7 @@ public static bool BuildChecksumsFile() public static bool BuildUpdateSignature() { - Program.PrintColorMessage("Building release signature...", ConsoleColor.Cyan); + Program.PrintColorMessage("Building release signatures...", ConsoleColor.Cyan); if (!File.Exists(CustomSignToolPath)) { @@ -844,14 +895,24 @@ public static bool BuildUpdateSignature() return true; } + if (!File.Exists(BuildOutputFolder + "\\processhacker-build-websetup.exe")) + { + Program.PrintColorMessage("[SKIPPED] websetup-setup.exe 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; } - BuildSetupSig = Win32.ShellExecute( + BuildWebSetupSig = Win32.ShellExecute( CustomSignToolPath, + "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-websetup.exe -h" + ); + BuildSetupSig = Win32.ShellExecute( + CustomSignToolPath, "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-setup.exe -h" ); @@ -865,11 +926,17 @@ public static string GetBuildLogString() public static void WebServiceUpdateConfig() { + if (string.IsNullOrEmpty(BuildVersion)) + return; if (string.IsNullOrEmpty(BuildSetupHash)) return; + if (string.IsNullOrEmpty(BuildSetupSig)) + return; if (string.IsNullOrEmpty(BuildBinHash)) return; - if (string.IsNullOrEmpty(BuildSetupSig)) + if (string.IsNullOrEmpty(BuildWebSetupSig)) + return; + if (string.IsNullOrEmpty(BuildWebSetupVersion)) return; string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\""); @@ -877,14 +944,23 @@ public static void WebServiceUpdateConfig() string buildPostString = Json.Serialize(new BuildUpdateRequest { Updated = TimeStart.ToString("o"), - Version = BuildVersion, FileLength = BuildSetupFileLength.ToString(), ForumUrl = "https://wj32.org/processhacker/nightly.php", - Setupurl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-setup.exe", - Binurl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-bin.zip", - HashSetup = BuildSetupHash, - HashBin = BuildBinHash, - sig = BuildSetupSig, + + SetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-setup.exe", + SetupVersion = BuildVersion, + SetupHash = BuildSetupHash, + SetupSig = BuildSetupSig, + + BinUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-bin.zip", + BinHash = BuildBinHash, + //BinSig = BuildBinSig, + + WebSetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-websetup.exe", + WebSetupHash = BuildWebSetupHash, + WebSetupVersion = BuildWebSetupVersion, + WebSetupSig = BuildWebSetupSig, + Message = buildSummary, Changelog = buildChangelog, }); @@ -930,12 +1006,14 @@ public static bool AppveyorUploadBuildFiles() { string[] buildFileArray = { + BuildOutputFolder + "\\processhacker-build-websetup.exe", BuildOutputFolder + "\\processhacker-build-setup.exe", BuildOutputFolder + "\\processhacker-build-bin.zip", BuildOutputFolder + "\\processhacker-build-checksums.txt" }; string[] releaseFileArray = { + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-websetup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" @@ -1007,16 +1085,22 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) { if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { + StringBuilder compilerOptions = new StringBuilder(); 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;"); + 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=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + + "/p:ExternalCompilerOptions=\"" + compilerOptions.ToString() + "\" " + Solution ); @@ -1029,16 +1113,22 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) { + StringBuilder compilerOptions = new StringBuilder(); 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;"); + 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=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + + "/p:ExternalCompilerOptions=\"" + compilerOptions.ToString() + "\" " + Solution ); diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 18cd8d9e43b6..05eda6988b8f 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -108,7 +108,7 @@ public static void Main(string[] args) if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildVerbose + BuildFlags.BuildVerbose | BuildFlags.BuildApi )) { return; @@ -127,7 +127,9 @@ public static void Main(string[] args) Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyKProcessHacker( @@ -138,7 +140,9 @@ public static void Main(string[] args) return; if (!Build.BuildSolution("plugins\\Plugins.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyWow64Files(BuildFlags.None)) @@ -159,7 +163,9 @@ public static void Main(string[] args) if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) + BuildFlags.BuildDebug | BuildFlags.BuildVerbose | + BuildFlags.BuildApi + )) return; if (!Build.CopyKProcessHacker( @@ -177,7 +183,8 @@ public static void Main(string[] args) if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | - BuildFlags.BuildDebug | BuildFlags.BuildVerbose + BuildFlags.BuildDebug | BuildFlags.BuildVerbose | + BuildFlags.BuildApi )) { return; @@ -197,7 +204,9 @@ public static void Main(string[] args) Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) @@ -206,7 +215,10 @@ public static void Main(string[] args) if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; - if (!Build.BuildSolution("plugins\\Plugins.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyWow64Files(BuildFlags.None)) @@ -214,6 +226,8 @@ public static void Main(string[] args) if (!Build.BuildBinZip()) return; + if (!Build.BuildWebSetupExe()) + return; if (!Build.BuildSetupExe()) return; if (!Build.BuildChecksumsFile()) @@ -232,13 +246,19 @@ public static void Main(string[] args) Build.ShowBuildEnvironment("appx", true, true); Build.CopyKeyFiles(); - if (!Build.BuildSolution("ProcessHacker.sln", BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + 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)) + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyWow64Files(BuildFlags.None)) @@ -271,7 +291,9 @@ public static void Main(string[] args) Build.CopyKeyFiles(); if (!Build.BuildSolution("ProcessHacker.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) @@ -281,7 +303,9 @@ public static void Main(string[] args) return; if (!Build.BuildSolution("plugins\\Plugins.sln", - BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) return; if (!Build.CopyWow64Files(BuildFlags.None)) @@ -373,9 +397,10 @@ private static bool Restart(string Arguments) public enum BuildFlags { None, - Build32bit = 0x1, - Build64bit = 0x2, - BuildDebug = 0x4, - BuildVerbose = 0x8 + Build32bit = 1, + Build64bit = 2, + BuildDebug = 4, + BuildVerbose = 8, + BuildApi = 16, } } \ No newline at end of file diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index dd7677d380e8..7a9beb885330 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -335,38 +335,26 @@ public VisualStudioInstance(ISetupInstance2 FromInstance) [DataContract] public class BuildUpdateRequest { - [DataMember(Name = "updated")] - public string Updated { get; set; } - - [DataMember(Name = "version")] - public string Version { get; set; } - - [DataMember(Name = "size")] - public string FileLength { get; set; } - - [DataMember(Name = "forum_url")] - public string ForumUrl { get; set; } - - [DataMember(Name = "setup_url")] - public string Setupurl { get; set; } - - [DataMember(Name = "bin_url")] - public string Binurl { get; set; } - - [DataMember(Name = "hash_setup")] - public string HashSetup { get; set; } - - [DataMember(Name = "hash_bin")] - public string HashBin { get; set; } - - [DataMember(Name = "sig")] - public string sig { get; set; } - - [DataMember(Name = "message")] - public string Message { get; set; } - - [DataMember(Name = "changelog")] - public string Changelog { get; set; } + [DataMember(Name = "updated")] public string Updated { get; set; } + [DataMember(Name = "size")] public string FileLength { get; set; } + [DataMember(Name = "forum_url")] public string ForumUrl { get; set; } + + [DataMember(Name = "bin_url")] public string BinUrl { get; set; } + [DataMember(Name = "hash_bin")] 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 = "hash")] public string SetupHash { get; set; } + [DataMember(Name = "sig")] public string SetupSig { get; set; } + [DataMember(Name = "version")] public string SetupVersion { get; set; } + + [DataMember(Name = "websetup_url")] public string WebSetupUrl { get; set; } + [DataMember(Name = "websetup_hash")] public string WebSetupHash { get; set; } + [DataMember(Name = "websetup_sig")] public string WebSetupSig { get; set; } + [DataMember(Name = "websetup_version")] public string WebSetupVersion { get; set; } + + [DataMember(Name = "message")] public string Message { get; set; } + [DataMember(Name = "changelog")] public string Changelog { get; set; } } [Flags] diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 02cea7f702b1..9cde444d6474 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -109,7 +109,7 @@ - + @@ -125,14 +125,15 @@ - - - - - + + + + + + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index b2499c2417c6..fed6da63ac64 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -36,18 +36,6 @@ Source Files - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - Source Files @@ -69,9 +57,6 @@ Source Files - - Source Files\pages - Source Files\json @@ -105,9 +90,27 @@ Source Files\json - + + Source Files\pages + + + Source Files\pages + + Source Files\pages + + Source Files\pages + + + Source Files\pages + + + Source Files\pages + + + Source Files + diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index e4a887d1f3c5..c94f746e94aa 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -376,6 +376,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); @@ -404,7 +406,9 @@ BOOLEAN DialogPromptExit( return buttonPressed == IDNO; } -BOOLEAN CheckProcessHackerRunning(VOID) +BOOLEAN CheckProcessHackerRunning( + VOID + ) { HANDLE mutantHandle; OBJECT_ATTRIBUTES oa; @@ -432,25 +436,35 @@ BOOLEAN CheckProcessHackerRunning(VOID) 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 = PhConcatStrings2(installPath->Buffer, 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; diff --git a/tools/CustomSetupTool/CustomSetupTool/page3.c b/tools/CustomSetupTool/CustomSetupTool/configpage.c similarity index 98% rename from tools/CustomSetupTool/CustomSetupTool/page3.c rename to tools/CustomSetupTool/CustomSetupTool/configpage.c index b4dbe1e2adb2..f016e5902fb6 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page3.c +++ b/tools/CustomSetupTool/CustomSetupTool/configpage.c @@ -72,7 +72,7 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK), TRUE); //Button_SetCheck(GetDlgItem(hwndDlg, IDC_KPH_CHECK), TRUE); //Button_SetCheck(GetDlgItem(hwndDlg, IDC_DBGTOOLS_CHECK), TRUE); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK), TRUE); + //Button_SetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK), TRUE); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 9b6ee0885a3a..eeb0259e5789 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -81,7 +81,7 @@ BOOLEAN ParseVersionString( PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->SetupFileVersion)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); @@ -161,7 +161,6 @@ json_object_ptr json_get_object( return NULL; } - BOOLEAN SetupQueryUpdateData( _Inout_ PPH_SETUP_CONTEXT Context ) @@ -175,6 +174,7 @@ BOOLEAN SetupQueryUpdateData( PVOID jsonObject = NULL; PPH_STRING versionHeader = UpdateVersionString(); PPH_STRING windowsHeader = UpdateWindowsString(); + PSTR value = NULL; if (!(httpSessionHandle = WinHttpOpen( NULL, @@ -280,34 +280,53 @@ BOOLEAN SetupQueryUpdateData( if (!(jsonObject = json_tokener_parse(stringBuffer))) goto CleanupExit; - Context->Version = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "version"))); - //Context->RevVersion = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "rev"))); - Context->RelDate = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "updated"))); - Context->Size = PhFormatSize(json_object_get_int64(json_get_object(jsonObject, "size")), -1); - Context->Hash = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "hash_setup"))); - Context->Signature = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "sig"))); - Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "forum_url"))); - Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "bin_url"))); - //Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jsonObject, "setup_url"))); - //Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "changelog")); - - if (PhIsNullOrEmptyString(Context->Version)) - goto CleanupExit; + if (value = json_object_get_string(json_get_object(jsonObject, "updated"))) + Context->RelDate = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "size"))) + Context->Size = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "forum_url"))) + Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(value); + + if (value = json_object_get_string(json_get_object(jsonObject, "bin_url"))) + Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "hash_bin"))) + Context->BinFileHash = PhConvertUtf8ToUtf16(value); + + if (value = json_object_get_string(json_get_object(jsonObject, "setup_url"))) + Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "sig"))) + Context->SetupFileSignature = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "version"))) + Context->SetupFileVersion = PhConvertUtf8ToUtf16(value); + + if (value = json_object_get_string(json_get_object(jsonObject, "websetup_url"))) + Context->WebSetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "websetup_sig"))) + Context->WebSetupFileSignature = PhConvertUtf8ToUtf16(value); + if (value = json_object_get_string(json_get_object(jsonObject, "websetup_version"))) + Context->WebSetupFileVersion = PhConvertUtf8ToUtf16(value); + if (!ParseVersionString(Context)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Signature)) - 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->BinFileDownloadUrl)) goto CleanupExit; + if (PhIsNullOrEmptyString(Context->BinFileHash)) + goto CleanupExit; + + if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileSignature)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileVersion)) + goto CleanupExit; success = TRUE; @@ -381,7 +400,7 @@ BOOLEAN UpdateDownloadUpdateData( ); } - Context->SetupFilePath = PhFormatString( + Context->FilePath = PhFormatString( L"%s%s\\processhacker-%lu.%lu.%lu-bin.zip", PhGetStringOrEmpty(setupTempPath), PhGetStringOrEmpty(randomGuidString), @@ -389,10 +408,10 @@ BOOLEAN UpdateDownloadUpdateData( Context->LatestMinorVersion, Context->LatestRevisionVersion ); - if (PhIsNullOrEmptyString(Context->SetupFilePath)) + if (PhIsNullOrEmptyString(Context->FilePath)) goto CleanupExit; - if (fullSetupPath = PhGetFullPath(PhGetString(Context->SetupFilePath), &indexOfFileName)) + if (fullSetupPath = PhGetFullPath(PhGetString(Context->FilePath), &indexOfFileName)) { PPH_STRING directoryPath; @@ -408,7 +427,7 @@ BOOLEAN UpdateDownloadUpdateData( if (!NT_SUCCESS(PhCreateFileWin32( &tempFileHandle, - PhGetString(Context->SetupFilePath), + PhGetString(Context->FilePath), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, diff --git a/tools/CustomSetupTool/CustomSetupTool/page5.c b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c similarity index 94% rename from tools/CustomSetupTool/CustomSetupTool/page5.c rename to tools/CustomSetupTool/CustomSetupTool/downloadpage.c index b480f83c671b..ced62e004766 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page5.c +++ b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c @@ -29,6 +29,9 @@ NTSTATUS SetupDownloadProgressThread( if (!SetupQueryUpdateData(Context)) goto CleanupExit; + //if (SetupUpdateWebSetupBuild(Context)) + // goto CleanupExit; + if (!UpdateDownloadUpdateData(Context)) goto CleanupExit; @@ -85,8 +88,6 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( { case PSN_SETACTIVE: { - HANDLE threadHandle; - context->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); context->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); context->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); @@ -99,8 +100,7 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( // Disable Next/Back buttons PropSheet_SetWizButtons(context->DialogHandle, 0); - if (threadHandle = PhCreateThread(0, SetupDownloadProgressThread, context)) - NtClose(threadHandle); + PhCreateThread2(SetupDownloadProgressThread, context); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/error.c b/tools/CustomSetupTool/CustomSetupTool/errorpage.c similarity index 100% rename from tools/CustomSetupTool/CustomSetupTool/error.c rename to tools/CustomSetupTool/CustomSetupTool/errorpage.c diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 6d7113314572..a82b278aae02 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -74,10 +74,10 @@ BOOLEAN SetupExtractBuild( #else PPH_BYTES zipPathUtf8; - if (!Context->SetupFilePath) + if (PhIsNullOrEmptyString(Context->FilePath)) goto CleanupExit; - zipPathUtf8 = PhConvertUtf16ToUtf8(PhGetString(Context->SetupFilePath)); + zipPathUtf8 = PhConvertUtf16ToUtf8(PhGetString(Context->FilePath)); if (!(status = mz_zip_reader_init_file(&zip_archive, zipPathUtf8->Buffer, 0))) goto CleanupExit; diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h index 8b71866e1cdf..5a173e411ec9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h @@ -68,6 +68,10 @@ HBITMAP LoadPngImageFromResources( _In_ PCWSTR Name ); +BOOLEAN CheckProcessHackerInstalled( + VOID + ); + PPH_STRING GetProcessHackerInstallPath( VOID ); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 60498e436fd2..78eccacb55ad 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -104,17 +104,22 @@ typedef struct _PH_SETUP_CONTEXT BOOLEAN SetupStartAppAfterExit; ULONG ErrorCode; - PPH_STRING Version; + PPH_STRING FilePath; PPH_STRING RevVersion; PPH_STRING RelDate; PPH_STRING Size; - PPH_STRING Hash; - PPH_STRING Signature; PPH_STRING ReleaseNotesUrl; PPH_STRING BinFileDownloadUrl; - //PPH_STRING SetupFileDownloadUrl; - PPH_STRING SetupFilePath; + PPH_STRING BinFileHash; + + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFileSignature; + PPH_STRING SetupFileVersion; + + PPH_STRING WebSetupFileDownloadUrl; + PPH_STRING WebSetupFileSignature; + PPH_STRING WebSetupFileVersion; HWND MainHeaderHandle; HWND StatusHandle; @@ -156,7 +161,7 @@ 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, @@ -224,6 +229,10 @@ VOID SetupUpgradeSettingsFile( VOID ); +VOID SetupCreateImageFileExecutionOptions( + VOID + ); + // download.c BOOLEAN SetupQueryUpdateData( @@ -246,6 +255,12 @@ VOID SetupShowUpdateDialog( VOID ); +// updatesetup.c + +NTSTATUS SetupUpdateWebSetupBuild( + _In_ PPH_SETUP_CONTEXT Context + ); + // uninstall.c VOID SetupShowUninstallDialog( diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/installpage.c similarity index 98% rename from tools/CustomSetupTool/CustomSetupTool/page4.c rename to tools/CustomSetupTool/CustomSetupTool/installpage.c index 94d2571c126d..8211041e995e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/installpage.c @@ -59,6 +59,9 @@ NTSTATUS SetupProgressThread( // Create autorun and shortcuts. SetupSetWindowsOptions(Context); + // Set the default image execution options. + SetupCreateImageFileExecutionOptions(); + // Setup new installation. if (!SetupExtractBuild(Context)) goto CleanupExit; @@ -76,7 +79,7 @@ NTSTATUS SetupProgressThread( return STATUS_FAIL_CHECK; } -INT_PTR CALLBACK SetupPropPage4_WndProc( +INT_PTR CALLBACK SetupInstallPropPage_WndProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _Inout_ WPARAM wParam, diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/licencepage.c similarity index 100% rename from tools/CustomSetupTool/CustomSetupTool/page2.c rename to tools/CustomSetupTool/CustomSetupTool/licencepage.c diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 7daea4ac97a9..80ab2834b87a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -157,6 +157,11 @@ INT WINAPI wWinMain( } } +#ifdef _DEBUG + if (CheckProcessHackerInstalled()) + SetupMode = SETUP_COMMAND_UNINSTALL; +#endif + switch (SetupMode) { case SETUP_COMMAND_INSTALL: @@ -219,7 +224,7 @@ INT WINAPI wWinMain( propSheetPage.pszTitle = PhApplicationName; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); propSheetPage.hInstance = PhInstanceHandle; - propSheetPage.pfnDlgProc = SetupPropPage4_WndProc; + propSheetPage.pfnDlgProc = SetupInstallPropPage_WndProc; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); // error page diff --git a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc index 780765938665..6d78a05f559e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resources/version.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc @@ -8,7 +8,13 @@ // 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 @@ -34,7 +40,9 @@ END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" +#if defined(PH_BUILD_API) "#include ""../../../../ProcessHacker/include/phappres.h""\r\n" +#endif "\0" END @@ -100,7 +108,7 @@ END // // RCDATA // -#if defined(APSTUDIO_INVOKED) || defined(PH_BUILD_API) +#if defined(PH_BUILD_API) IDR_BIN_DATA RCDATA "../../../../build/output/processhacker-build-bin.zip" diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 31009632e79a..256dc4f4a25a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -323,19 +323,7 @@ VOID SetupStartKph( )) { NtWaitForSingleObject(processHandle, FALSE, &timeout); - } - - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_START); - - if (serviceHandle) - { - if (StartService(serviceHandle, 0, NULL)) - success = TRUE; - - CloseServiceHandle(serviceHandle); + NtClose(processHandle); } } @@ -389,21 +377,40 @@ VOID SetupSetWindowsOptions( { 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"); + HANDLE keyHandle; PPH_STRING clientPathString; PPH_STRING startmenuFolderString; clientPathString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\ProcessHacker.exe"); + // Create the startmenu shortcut. if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) { - SetupCreateLink( - PhGetString(startmenuFolderString), - PhGetString(clientPathString), - PhGetString(Context->SetupInstallPath) - ); + SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); PhDereferenceObject(startmenuFolderString); } + // Create the desktop shortcut. + if (Context->SetupCreateDesktopShortcut) + { + if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + { + SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); + PhDereferenceObject(startmenuFolderString); + } + } + + // Create the all users shortcut. + if (Context->SetupCreateDesktopShortcutAllUsers) + { + if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + { + SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); + PhDereferenceObject(startmenuFolderString); + } + } + + // Create the PE Viewer startmenu shortcut. if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk")) { PPH_STRING peviewPathString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\peview.exe"); @@ -412,24 +419,25 @@ VOID SetupSetWindowsOptions( PhGetString(startmenuFolderString), PhGetString(peviewPathString), PhGetString(Context->SetupInstallPath) - ); + ); PhDereferenceObject(peviewPathString); PhDereferenceObject(startmenuFolderString); } + // Reset the settings file. if (Context->SetupResetSettings) { PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); SetupDeleteDirectoryFile(settingsFileName->Buffer); + PhDereferenceObject(settingsFileName); } + // Set the Windows default Task Manager. if (Context->SetupCreateDefaultTaskManager) { - HANDLE keyHandle; - if (NT_SUCCESS(PhOpenKey( &keyHandle, KEY_WRITE, @@ -440,19 +448,20 @@ VOID SetupSetWindowsOptions( { PPH_STRING value; UNICODE_STRING valueName; - + value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\""); + // Configure the default Task Manager. RtlInitUnicodeString(&valueName, L"Debugger"); NtSetValueKey(keyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); + NtClose(keyHandle); } } + // Create the run startup key. if (Context->SetupCreateSystemStartup) - { - HANDLE keyHandle; - + { if (NT_SUCCESS(PhOpenKey( &keyHandle, KEY_WRITE, @@ -470,46 +479,18 @@ VOID SetupSetWindowsOptions( 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); } } - - if (Context->SetupCreateDesktopShortcut) - { - PPH_STRING desktopFolderString; - - if (desktopFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")); - { - SetupCreateLink( - PhGetString(desktopFolderString), - PhGetString(clientPathString), - PhGetString(Context->SetupInstallPath) - ); - PhDereferenceObject(desktopFolderString); - } - } - else if (Context->SetupCreateDesktopShortcutAllUsers) - { - PPH_STRING startmenuFolderString; - - if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) - { - SetupCreateLink( - PhGetString(startmenuFolderString), - PhGetString(clientPathString), - PhGetString(Context->SetupInstallPath) - ); - PhDereferenceObject(startmenuFolderString); - } - } } VOID SetupDeleteWindowsOptions( _In_ PPH_SETUP_CONTEXT Context ) { + static PH_STRINGREF PhImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); 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"); PPH_STRING startmenuFolderString; @@ -527,7 +508,7 @@ VOID SetupDeleteWindowsOptions( PhDereferenceObject(startmenuFolderString); } - if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")); + if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) { SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); PhDereferenceObject(startmenuFolderString); @@ -539,9 +520,17 @@ VOID SetupDeleteWindowsOptions( PhDereferenceObject(startmenuFolderString); } - //PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); - //SetupDeleteDirectoryFile(settingsFileName->Buffer); - //PhDereferenceObject(settingsFileName); + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } if (NT_SUCCESS(PhOpenKey( &keyHandle, @@ -557,7 +546,7 @@ VOID SetupDeleteWindowsOptions( if (NT_SUCCESS(PhOpenKey( &keyHandle, - KEY_WRITE, + KEY_WRITE | DELETE, PH_KEY_CURRENT_USER, &CurrentUserRunKeyName, 0 @@ -611,4 +600,41 @@ VOID SetupUpgradeSettingsFile( PhDereferenceObject(oldSettingsFileName); PhDereferenceObject(settingsFilePath); +} + +VOID SetupCreateImageFileExecutionOptions( + VOID + ) +{ + static PH_STRINGREF PhImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); + HANDLE keyHandle; + + // Set the default Image File Execution Options. + if (NT_SUCCESS(PhCreateKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsKeyName, + 0, + 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_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 + }, sizeof(ULONG64)); + + NtClose(keyHandle); + } } \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/startpage.c similarity index 97% rename from tools/CustomSetupTool/CustomSetupTool/page1.c rename to tools/CustomSetupTool/CustomSetupTool/startpage.c index e473cf75ecaa..17a087b8664c 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/startpage.c @@ -108,6 +108,9 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( { case PSN_SETACTIVE: { +#ifdef _DEBUG + PostMessage(context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG3); +#endif // Reset the button state. PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT); } diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 1fe866ca7671..83d4028a1d41 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -44,7 +44,7 @@ NTSTATUS SetupUninstallBuild( _In_ PPH_SETUP_CONTEXT Context ) { - //context->SetupInstallPath = SetupFindInstallDirectory(); + Context->SetupInstallPath = SetupFindInstallDirectory(); // Stop Process Hacker. if (!ShutdownProcessHacker()) @@ -55,17 +55,16 @@ NTSTATUS SetupUninstallBuild( goto CleanupExit; // Remove autorun and shortcuts. - //SetupDeleteWindowsOptions(); + SetupDeleteWindowsOptions(Context); // Remove the uninstaller. - //SetupDeleteUninstallFile(); + SetupDeleteUninstallFile(Context); // Remove the ARP uninstall entry. SetupDeleteUninstallKey(); // Remove the previous installation. - //if (!RemoveDirectoryPath(PhGetString(SetupInstallPath))) - // goto CleanupExit; + RemoveDirectoryPath(PhGetString(Context->SetupInstallPath)); ShowUninstallCompleteDialog(Context); return STATUS_SUCCESS; diff --git a/tools/CustomSetupTool/CustomSetupTool/updatesetup.c b/tools/CustomSetupTool/CustomSetupTool/updatesetup.c new file mode 100644 index 000000000000..e892305e2f5a --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/updatesetup.c @@ -0,0 +1,29 @@ +/* + * 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 + +NTSTATUS SetupUpdateWebSetupBuild( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + return STATUS_SUCCESS; +} From 6f996c60366757b8b652ef3f6064a992e5e684d6 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 23:27:46 +1000 Subject: [PATCH 358/839] BuildTools: update binaries --- .../bin/Release/CustomBuildTool.exe | Bin 160256 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 73216 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 7ec0fe7844142e97d27ca7fde66e3e1a96e1c9df..d66268864f76598e02d1647233a2a1e887d3da19 100644 GIT binary patch delta 24811 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 delta 19795 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 EKLdv$1ONa4 From 443d8324595d1fc09167fdc6b422fc439b745581 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 23:35:33 +1000 Subject: [PATCH 359/839] Fix previous commit --- tools/CustomBuildTool/Source Files/Utils.cs | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 7a9beb885330..ba180c1fc213 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -344,7 +344,7 @@ public class BuildUpdateRequest [DataMember(Name = "bin_sig")] public string BinSig { get; set; } [DataMember(Name = "setup_url")] public string SetupUrl { get; set; } - [DataMember(Name = "hash")] public string SetupHash { get; set; } + [DataMember(Name = "hash_setup")] public string SetupHash { get; set; } [DataMember(Name = "sig")] public string SetupSig { get; set; } [DataMember(Name = "version")] public string SetupVersion { get; set; } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index d66268864f76598e02d1647233a2a1e887d3da19..a4a1356fa0497add7d6a45e8608db4a98934ff9f 100644 GIT binary patch delta 309 zcmZqp!rAbJb3z9b*S?8ea*U@Yt_)|an9Rti&g#I$$RIQM;2WLEk&Nn$7bZ6{)-$e| z%*Z6nYqOAnfsuiMA&!B8&0w=4ld&UX!RAosbQ%90d?%R>Go0Xi2_*0EaWWrf_{yiu zyqjSyyC#s$&+p8<8OZhovNiaV!R$;R+m*kW`82~N-e%^@K-p&I$w0CbC?2_)J7H;w zfTc*xzMzSw9&66F@->P*EZKbE%0fPIF-D*pLikwx5_40zG7^h3;)_#DN(&|zJ>Icd z=ZOf14%fcUNT4Gbn1EOY=-e2fe`4M+Fa!m=1WQlc|28=Km`}&_$t5aJr|jFD^M-?w pcL6uZR}ipka^qWRpnvDS&28p;x1H}DGaTZ32_&!aaWWrfc+aQI zyqjSyyC#s$$?wd(8OZhovX%Ig!R$;R+n&Ff`82~N-e%^@K-p&I$w0CbC?33-J7H;w zfNn;I#S_amuXP)k0@cLQT{a)MvXDa#N1StjKt!M$rX=xFfwe`eDT3+i+45`yy0LJn8OY7 q5d>@lvZrs)`3RIsfPVJ^$zZ_+7RY7RTuR&&@~g6kF6di!vKAQuNfbC&cOuSbR1uO&I8-Z1}U-@BCFKFAdjUjP4cHf{pKH3wD1cHEvKfYy$T1 zd&-c32CmMK3Nqy>H3Mk|&aHHc?FK9q2khXzaxHX+Ovx>zloXdEi+LVU^4h?B-UfW< zW^X+3f+_#hj6KE&7`qB+5zK_L5g}=zorPzKg$$T)`)c5b0URcFcTNTz z%LBZE(bYzRZQ={D17bwaw&Vj>X4$rXjPD52oILc#gaTllVDy3_!6vLBrd<X=D7 zMbBlh5V)=k9iGpBDGf!yOO~k}vSe4D_uAARAviB72I7>V-f(q=mZ{TPwZj~?@#=bw z^K6%vsRu=odM>)UQRBSYt7Yn#C<;uOr8X12hN3mJ2(dYOvyouiEGL#LM)d3uG3^(m eXN~{q=_Qt`^<*zLFm06*WWzsxz?CFTCjSKyBG0`5 delta 1970 zcmaLYZAep57zgnG-R^SEY1TF|v8<5`E4`_y8D`pxh>s{s7gu8Zgl6l$F1*WRxLBZEFX}hp>zOT&LEN(1ozZncUo6i~7V!f}ZGt0OEmBIa; z_p`;y_Nn|}`nyl_WVb{G!%cu&dSV4Cv>SA-brmxw0>j!3#y*fe;MkQUe$MwYp~Ij> z#uogQ%rE$A4#jC<%w$H%syR9H?0{WovX(dF9yl#L6w zSo^aOIIIeoi5^-68MkLF=#VlN1L4y^!2%q*t#C-qnLxRkl(mOsH5^ND0WNi-kvQMJ zziGkLi5gKo2pybdDEIepmhqKrm=o7$1om>`oFY)Gnan(!V}v0M87npcDV%~^Xpc^D zY$y)c$;l~ao9WbWu!XFcWB14gI0a8yfbX0*PXh2fYKgiHc1yvZQ*#ZnL&{wOTvQJT zyU~4FK#7_l*4)w*S*dCU=IO0j3cR3gQ5tLUIlz$!!N6`w8zCLaaAN~+kk!p-z${u? zi)w1!@Rjn=PLK_8Oy02qXy9NU*_|1!zP~9K@TexDzF+OqYgFI=nd|_^Hs=B7qqaRJ zaDG?&PK>gYR z;HA#|UQtLhjCc40^t*bzBRUo+Dg@|UhnpF(R?3)vOwD68P0O>58LO|cn=yYK#Vhk^ zot~$W8LQ9ZVa)$JlBegJNjPJaZBoIi=wd9e>D~#OR6>@mW@GIjnfB+*TH}9mddRfT Xh{Z?+dw$0YR+0N9?nzG)MrZ#6;cexO From 77213a8c88f6b15bd9530dd650fb81a40fac2b15 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 8 Aug 2017 23:38:14 +1000 Subject: [PATCH 360/839] Fix spacing --- tools/CustomSetupTool/CustomSetupTool/setup.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 256dc4f4a25a..e23626d0026c 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -625,14 +625,14 @@ VOID SetupCreateImageFileExecutionOptions( 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_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 + 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_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 }, sizeof(ULONG64)); NtClose(keyHandle); From 34b9d9c91ed6f55bf00faff064df98926435a896 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 9 Aug 2017 00:02:16 +1000 Subject: [PATCH 361/839] Updater: Fix crash --- plugins/Updater/updater.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 9affa094d471..98956efd01df 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -309,11 +309,11 @@ BOOLEAN QueryUpdateData( HINTERNET httpRequestHandle = NULL; ULONG stringBufferLength = 0; PSTR stringBuffer = NULL; - PH_STRING_BUILDER sb; PVOID jsonObject = NULL; mxml_node_t* xmlNode = NULL; PPH_STRING versionHeader = UpdateVersionString(); PPH_STRING windowsHeader = UpdateWindowsString(); + PSTR tempValue = NULL; if (!(httpSessionHandle = WinHttpOpen( NULL, @@ -421,18 +421,29 @@ BOOLEAN QueryUpdateData( 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, "changelog")); - - if (Context->BuildMessage) + if (tempValue = GetJsonValueAsString(jsonObject, "version")) + { + Context->Version = PhConvertUtf8ToUtf16(tempValue); + Context->RevVersion = PhConvertUtf8ToUtf16(tempValue); + } + if (tempValue = GetJsonValueAsString(jsonObject, "updated")) + Context->RelDate = PhConvertUtf8ToUtf16(tempValue); + if (tempValue = GetJsonValueAsString(jsonObject, "hash_setup")) + Context->Hash = PhConvertUtf8ToUtf16(tempValue); + if (tempValue = GetJsonValueAsString(jsonObject, "sig")) + Context->Signature = PhConvertUtf8ToUtf16(tempValue); + if (tempValue = GetJsonValueAsString(jsonObject, "forum_url")) + Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(tempValue); + if (tempValue = GetJsonValueAsString(jsonObject, "setup_url")) + Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(tempValue); + if (tempValue = GetJsonValueAsString(jsonObject, "changelog")) + Context->BuildMessage = PhConvertUtf8ToUtf16(tempValue); + + if (!PhIsNullOrEmptyString(Context->BuildMessage)) { + PH_STRING_BUILDER sb; + PhInitializeStringBuilder(&sb, 0x100); for (SIZE_T i = 0; i < Context->BuildMessage->Length / sizeof(WCHAR); i++) @@ -450,9 +461,6 @@ BOOLEAN QueryUpdateData( if (PhIsNullOrEmptyString(Context->Version)) goto CleanupExit; - if (!ParseVersionString(Context)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->RevVersion)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->RelDate)) @@ -468,6 +476,9 @@ BOOLEAN QueryUpdateData( if (PhIsNullOrEmptyString(Context->Signature)) goto CleanupExit; + if (!ParseVersionString(Context)) + goto CleanupExit; + success = TRUE; CleanupExit: From 55991c592e0b0013b80bc6f2f7e2c3f2af9a9fc9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 10 Aug 2017 21:40:40 +1000 Subject: [PATCH 362/839] Update headers, Add plugin cache exports --- ProcessHacker/ProcessHacker.def | 2 + phlib/apiimport.c | 1 - phlib/include/apiimport.h | 10 --- phlib/include/kphapi.h | 14 ++-- phlib/include/phutil.h | 14 ++++ phlib/include/symprvp.h | 34 ++++++++++ phlib/util.c | 116 +++++++++++++++++++++----------- 7 files changed, 133 insertions(+), 58 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index d2e5565ca064..c0ca340aa41f 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -345,6 +345,8 @@ EXPORTS PhGetApplicationDirectory PhGetApplicationFileName PhGetBaseName + PhGetCacheDirectory + PhGetCacheFileName PhGetDllFileName PhGetFileDialogFileName PhGetFileDialogFilterIndex diff --git a/phlib/apiimport.c b/phlib/apiimport.c index 12e2bc7e52d5..a14facf43a3c 100644 --- a/phlib/apiimport.c +++ b/phlib/apiimport.c @@ -58,7 +58,6 @@ _##Name Name##_Import(VOID) \ 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); diff --git a/phlib/include/apiimport.h b/phlib/include/apiimport.h index 973798657c33..574b6d9eb86c 100644 --- a/phlib/include/apiimport.h +++ b/phlib/include/apiimport.h @@ -1,15 +1,6 @@ #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)( @@ -74,7 +65,6 @@ typedef HRESULT (WINAPI *_SHParseDisplayName)( #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); diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index f905de9a09d3..a802089b7587 100644 --- a/phlib/include/kphapi.h +++ b/phlib/include/kphapi.h @@ -131,17 +131,17 @@ typedef enum _KPH_SECURITY_LEVEL typedef struct _KPH_DYN_STRUCT_DATA { - SHORT EgeGuid; - SHORT EpObjectTable; + SHORT EgeGuid; // dt nt!_ETW_GUID_ENTRY Guid + SHORT EpObjectTable; // dt nt!_EPROCESS ObjectTable SHORT Reserved0; SHORT Reserved1; SHORT Reserved2; - SHORT EreGuidEntry; - SHORT HtHandleContentionEvent; - SHORT OtName; - SHORT OtIndex; + 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; + SHORT ObAttributesShift; // dt nt!_HANDLE_TABLE_ENTRY } KPH_DYN_STRUCT_DATA, *PKPH_DYN_STRUCT_DATA; typedef struct _KPH_DYN_PACKAGE diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 270041ab4c2c..9c3722bce4aa 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1049,6 +1049,20 @@ PhParseCommandLineFuzzy( _Out_opt_ PPH_STRING *FullFileName ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetCacheDirectory( + VOID + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetCacheFileName( + _In_ PPH_STRING FileName + ); + FORCEINLINE HANDLE NTAPI diff --git a/phlib/include/symprvp.h b/phlib/include/symprvp.h index 9d1f309d08f6..80512985ed87 100644 --- a/phlib/include/symprvp.h +++ b/phlib/include/symprvp.h @@ -53,6 +53,40 @@ typedef BOOL (WINAPI *_SymFromNameW)( _Inout_ PSYMBOL_INFOW Symbol ); +typedef BOOL (WINAPI *_SymEnumTypesW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymEnumTypes)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, + _In_opt_ PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymGetModuleInfoW64)( + _In_ HANDLE hProcess, + _In_ ULONG64 qwAddr, + _Out_ PIMAGEHLP_MODULEW64 ModuleInfo + ); + +typedef BOOL (WINAPI *_SymGetTypeFromName)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PCSTR Name, + _Inout_ PSYMBOL_INFO Symbol + ); + +typedef BOOL(WINAPI *_SymGetTypeFromNameW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PCWSTR Name, + _Inout_ PSYMBOL_INFOW Symbol + ); + typedef BOOL (WINAPI *_SymGetLineFromAddr64)( _In_ HANDLE hProcess, _In_ ULONG64 dwAddr, diff --git a/phlib/util.c b/phlib/util.c index 2fd650b27028..e5f0f6d7b38a 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -586,6 +586,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)); @@ -597,53 +600,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 = PhInstanceHandle; - 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 | (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; - 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 (TaskDialogIndirect( + &config, + &button, + NULL, + NULL + ) == S_OK) + { + return button == IDYES; } else { - return PhShowMessage( - hWnd, - MB_YESNO | MB_ICONWARNING, - L"Are you sure you want to %s?", - action->Buffer - ) == IDYES; + return FALSE; } } @@ -5029,3 +5016,52 @@ BOOLEAN PhParseCommandLineFuzzy( return FALSE; } + +PPH_STRING PhGetCacheDirectory( + VOID + ) +{ + return PhGetKnownLocation(CSIDL_LOCAL_APPDATA, L"\\Process Hacker\\Cache\\"); +} + +PPH_STRING PhGetCacheFileName( + _In_ PPH_STRING FileName + ) +{ + PPH_STRING cacheDirectory; + PPH_STRING cacheFilePath; + PPH_STRING cacheFullFilePath = NULL; + ULONG indexOfFileName = -1; + WCHAR alphastring[16] = L""; + + cacheDirectory = PhGetCacheDirectory(); + 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 != -1) + { + if (directoryPath = PhSubstring(cacheFullFilePath, 0, indexOfFileName)) + { + SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); + PhDereferenceObject(directoryPath); + } + } + } + + PhDereferenceObject(cacheFilePath); + PhDereferenceObject(cacheDirectory); + + return cacheFullFilePath; +} From 556ce3ee3f40ea601416180434412863ad6aecd7 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 10 Aug 2017 23:07:10 +1000 Subject: [PATCH 363/839] Export json wrapper, Update plugin imports, Update plugin cache paths --- ProcessHacker/ProcessHacker.def | 20 +- phlib/include/json.h | 161 ++++++ {plugins/CommonUtil => phlib}/json.c | 65 +-- .../CommonUtil/json-c => phlib/jsonc}/AUTHORS | 0 .../CommonUtil/json-c => phlib/jsonc}/COPYING | 0 .../json-c => phlib/jsonc}/ChangeLog | 0 .../json-c => phlib/jsonc}/arraylist.c | 0 .../json-c => phlib/jsonc}/arraylist.h | 0 .../CommonUtil/json-c => phlib/jsonc}/bits.h | 0 .../json-c => phlib/jsonc}/config.h | 0 .../json-c => phlib/jsonc}/config.h.in | 0 .../CommonUtil/json-c => phlib/jsonc}/debug.c | 0 .../CommonUtil/json-c => phlib/jsonc}/debug.h | 0 .../CommonUtil/json-c => phlib/jsonc}/json.h | 0 .../json-c => phlib/jsonc}/json_c_version.c | 0 .../json-c => phlib/jsonc}/json_c_version.h | 0 .../json-c => phlib/jsonc}/json_config.h | 0 .../json-c => phlib/jsonc}/json_inttypes.h | 0 .../json-c => phlib/jsonc}/json_object.c | 0 .../json-c => phlib/jsonc}/json_object.h | 0 .../jsonc}/json_object_iterator.c | 0 .../jsonc}/json_object_iterator.h | 0 .../jsonc}/json_object_private.h | 0 .../json-c => phlib/jsonc}/json_tokener.c | 0 .../json-c => phlib/jsonc}/json_tokener.h | 0 .../json-c => phlib/jsonc}/json_util.c | 0 .../json-c => phlib/jsonc}/json_util.h | 0 .../json-c => phlib/jsonc}/libjson.c | 0 .../json-c => phlib/jsonc}/linkhash.c | 0 .../json-c => phlib/jsonc}/linkhash.h | 0 .../json-c => phlib/jsonc}/math_compat.h | 0 .../json-c => phlib/jsonc}/printbuf.c | 0 .../json-c => phlib/jsonc}/printbuf.h | 0 .../json-c => phlib/jsonc}/random_seed.c | 0 .../json-c => phlib/jsonc}/random_seed.h | 0 phlib/phlib.vcxproj | 32 +- phlib/phlib.vcxproj.filters | 96 ++++ plugins/CommonUtil/CommonUtil.vcxproj | 42 +- plugins/CommonUtil/CommonUtil.vcxproj.filters | 90 ---- plugins/CommonUtil/http.c | 123 ----- plugins/CommonUtil/main.c | 21 - plugins/ExtraPlugins/cloud.c | 51 +- plugins/ExtraPlugins/main.h | 1 + plugins/ExtraPlugins/setup/updater.c | 98 +--- plugins/NetworkTools/update.c | 172 +++---- plugins/OnlineChecks/onlnchk.h | 1 + plugins/OnlineChecks/upload.c | 50 +- plugins/OnlineChecks/virustotal.c | 74 +-- plugins/Updater/page3.c | 6 +- plugins/Updater/page4.c | 6 +- plugins/Updater/updater.c | 234 ++++----- plugins/Updater/updater.h | 14 +- plugins/include/commonutil.h | 457 ------------------ tools/CustomBuildTool/Source Files/Build.cs | 51 +- tools/CustomBuildTool/Source Files/Program.cs | 4 +- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 161792 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 57 files changed, 652 insertions(+), 1217 deletions(-) create mode 100644 phlib/include/json.h rename {plugins/CommonUtil => phlib}/json.c (73%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/AUTHORS (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/COPYING (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/ChangeLog (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/arraylist.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/arraylist.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/bits.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/config.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/config.h.in (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/debug.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/debug.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_c_version.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_c_version.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_config.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_inttypes.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_object.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_object.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_object_iterator.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_object_iterator.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_object_private.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_tokener.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_tokener.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_util.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/json_util.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/libjson.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/linkhash.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/linkhash.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/math_compat.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/printbuf.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/printbuf.h (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/random_seed.c (100%) rename {plugins/CommonUtil/json-c => phlib/jsonc}/random_seed.h (100%) delete mode 100644 plugins/CommonUtil/http.c diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index c0ca340aa41f..36df3d1a187b 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -617,4 +617,22 @@ EXPORTS mxmlNewOpaque mxmlNewElement mxmlSaveFd - mxml_opaque_cb \ No newline at end of file + mxml_opaque_cb + +; json + PhCreateJsonParser + PhFreeJsonParser + PhGetJsonValueAsString + PhGetJsonValueAsLong64 + PhCreateJsonObject + PhGetJsonObject + PhGetJsonObjectLength + PhGetJsonObjectBool + PhAddJsonObject + PhCreateJsonArray + PhAddJsonArrayObject + PhGetJsonArrayString + PhGetJsonArrayLong64 + PhGetJsonArrayLength + PhGetJsonArrayIndexObject + PhGetJsonObjectAsArrayList \ No newline at end of file diff --git a/phlib/include/json.h b/phlib/include/json.h new file mode 100644 index 000000000000..fded874784d0 --- /dev/null +++ b/phlib/include/json.h @@ -0,0 +1,161 @@ +/* + * 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 +PSTR +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 +PSTR +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 +PVOID +NTAPI +PhGetJsonObjectAsArrayList( + _In_ PVOID Object + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json.c b/phlib/json.c similarity index 73% rename from plugins/CommonUtil/json.c rename to phlib/json.c index 8b1d3e8f062b..d22c766033da 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,21 +42,21 @@ 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( +PSTR PhGetJsonValueAsString( _In_ PVOID Object, _In_ PSTR Key ) @@ -61,7 +64,7 @@ PSTR UtilGetJsonValueAsString( return json_object_get_string(json_get_object(Object, Key)); } -INT64 UtilGetJsonValueAsUlong( +INT64 PhGetJsonValueAsLong64( _In_ PVOID Object, _In_ PSTR Key ) @@ -69,14 +72,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 +87,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 +111,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 +126,14 @@ VOID UtilAddJsonArray( json_object_array_add(Object, jsonEntry); } -PSTR UtilGetJsonArrayString( +PSTR PhGetJsonArrayString( _In_ PVOID Object ) { return _strdup( json_object_to_json_string(Object) ); // leak } -INT64 UtilGetJsonArrayUlong( +INT64 PhGetJsonArrayLong64( _In_ PVOID Object, _In_ INT Index ) @@ -138,14 +141,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 +156,27 @@ PVOID UtilGetObjectArrayIndex( return json_object_array_get_idx(Object, Index); } -PPH_LIST UtilGetObjectArrayList( +PVOID 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 +} 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 100% rename from plugins/CommonUtil/json-c/arraylist.c rename to phlib/jsonc/arraylist.c diff --git a/plugins/CommonUtil/json-c/arraylist.h b/phlib/jsonc/arraylist.h similarity index 100% rename from plugins/CommonUtil/json-c/arraylist.h rename to phlib/jsonc/arraylist.h diff --git a/plugins/CommonUtil/json-c/bits.h b/phlib/jsonc/bits.h similarity index 100% rename from plugins/CommonUtil/json-c/bits.h rename to phlib/jsonc/bits.h diff --git a/plugins/CommonUtil/json-c/config.h b/phlib/jsonc/config.h similarity index 100% rename from plugins/CommonUtil/json-c/config.h rename to phlib/jsonc/config.h 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 100% rename from plugins/CommonUtil/json-c/debug.c rename to phlib/jsonc/debug.c diff --git a/plugins/CommonUtil/json-c/debug.h b/phlib/jsonc/debug.h similarity index 100% rename from plugins/CommonUtil/json-c/debug.h rename to phlib/jsonc/debug.h diff --git a/plugins/CommonUtil/json-c/json.h b/phlib/jsonc/json.h similarity index 100% rename from plugins/CommonUtil/json-c/json.h rename to phlib/jsonc/json.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/plugins/CommonUtil/json-c/json_c_version.h b/phlib/jsonc/json_c_version.h similarity index 100% rename from plugins/CommonUtil/json-c/json_c_version.h rename to phlib/jsonc/json_c_version.h 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/plugins/CommonUtil/json-c/json_inttypes.h b/phlib/jsonc/json_inttypes.h similarity index 100% rename from plugins/CommonUtil/json-c/json_inttypes.h rename to phlib/jsonc/json_inttypes.h diff --git a/plugins/CommonUtil/json-c/json_object.c b/phlib/jsonc/json_object.c similarity index 100% rename from plugins/CommonUtil/json-c/json_object.c rename to phlib/jsonc/json_object.c diff --git a/plugins/CommonUtil/json-c/json_object.h b/phlib/jsonc/json_object.h similarity index 100% rename from plugins/CommonUtil/json-c/json_object.h rename to phlib/jsonc/json_object.h diff --git a/plugins/CommonUtil/json-c/json_object_iterator.c b/phlib/jsonc/json_object_iterator.c similarity index 100% rename from plugins/CommonUtil/json-c/json_object_iterator.c rename to phlib/jsonc/json_object_iterator.c diff --git a/plugins/CommonUtil/json-c/json_object_iterator.h b/phlib/jsonc/json_object_iterator.h similarity index 100% rename from plugins/CommonUtil/json-c/json_object_iterator.h rename to phlib/jsonc/json_object_iterator.h diff --git a/plugins/CommonUtil/json-c/json_object_private.h b/phlib/jsonc/json_object_private.h similarity index 100% rename from plugins/CommonUtil/json-c/json_object_private.h rename to phlib/jsonc/json_object_private.h diff --git a/plugins/CommonUtil/json-c/json_tokener.c b/phlib/jsonc/json_tokener.c similarity index 100% rename from plugins/CommonUtil/json-c/json_tokener.c rename to phlib/jsonc/json_tokener.c diff --git a/plugins/CommonUtil/json-c/json_tokener.h b/phlib/jsonc/json_tokener.h similarity index 100% rename from plugins/CommonUtil/json-c/json_tokener.h rename to phlib/jsonc/json_tokener.h diff --git a/plugins/CommonUtil/json-c/json_util.c b/phlib/jsonc/json_util.c similarity index 100% rename from plugins/CommonUtil/json-c/json_util.c rename to phlib/jsonc/json_util.c diff --git a/plugins/CommonUtil/json-c/json_util.h b/phlib/jsonc/json_util.h similarity index 100% rename from plugins/CommonUtil/json-c/json_util.h rename to phlib/jsonc/json_util.h diff --git a/plugins/CommonUtil/json-c/libjson.c b/phlib/jsonc/libjson.c similarity index 100% rename from plugins/CommonUtil/json-c/libjson.c rename to phlib/jsonc/libjson.c diff --git a/plugins/CommonUtil/json-c/linkhash.c b/phlib/jsonc/linkhash.c similarity index 100% rename from plugins/CommonUtil/json-c/linkhash.c rename to phlib/jsonc/linkhash.c diff --git a/plugins/CommonUtil/json-c/linkhash.h b/phlib/jsonc/linkhash.h similarity index 100% rename from plugins/CommonUtil/json-c/linkhash.h rename to phlib/jsonc/linkhash.h diff --git a/plugins/CommonUtil/json-c/math_compat.h b/phlib/jsonc/math_compat.h similarity index 100% rename from plugins/CommonUtil/json-c/math_compat.h rename to phlib/jsonc/math_compat.h diff --git a/plugins/CommonUtil/json-c/printbuf.c b/phlib/jsonc/printbuf.c similarity index 100% rename from plugins/CommonUtil/json-c/printbuf.c rename to phlib/jsonc/printbuf.c diff --git a/plugins/CommonUtil/json-c/printbuf.h b/phlib/jsonc/printbuf.h similarity index 100% rename from plugins/CommonUtil/json-c/printbuf.h rename to phlib/jsonc/printbuf.h diff --git a/plugins/CommonUtil/json-c/random_seed.c b/phlib/jsonc/random_seed.c similarity index 100% rename from plugins/CommonUtil/json-c/random_seed.c rename to phlib/jsonc/random_seed.c diff --git a/plugins/CommonUtil/json-c/random_seed.h b/phlib/jsonc/random_seed.h similarity index 100% rename from plugins/CommonUtil/json-c/random_seed.h rename to phlib/jsonc/random_seed.h diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index e395b2239ea8..fe378512fe80 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,4 +1,4 @@ - + @@ -161,6 +161,18 @@ + + + + + + + + + + + + @@ -201,6 +213,7 @@ + @@ -236,6 +249,23 @@ + + + + + + + + + + + + + + + + + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index dee92e1b386b..2ebb7ab5f4b3 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -15,6 +15,12 @@ {874b89c3-fb60-46f3-a62c-49ac88e3026d} + + {fd6c74dc-d93e-4dd1-9a8b-5bdf525e0709} + + + {4f0e2a3e-3009-4e13-8d38-e33c46d00044} + @@ -176,6 +182,42 @@ 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 + @@ -364,5 +406,59 @@ 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 + \ No newline at end of file diff --git a/plugins/CommonUtil/CommonUtil.vcxproj b/plugins/CommonUtil/CommonUtil.vcxproj index c2b017b83820..aa57362d61bc 100644 --- a/plugins/CommonUtil/CommonUtil.vcxproj +++ b/plugins/CommonUtil/CommonUtil.vcxproj @@ -55,62 +55,28 @@ - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) + windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) + windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) + windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) + windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/CommonUtil/CommonUtil.vcxproj.filters b/plugins/CommonUtil/CommonUtil.vcxproj.filters index 83a49f7f2074..6df6a7997e96 100644 --- a/plugins/CommonUtil/CommonUtil.vcxproj.filters +++ b/plugins/CommonUtil/CommonUtil.vcxproj.filters @@ -25,45 +25,6 @@ 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 - @@ -72,57 +33,6 @@ 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 - diff --git a/plugins/CommonUtil/http.c b/plugins/CommonUtil/http.c deleted file mode 100644 index c6034aed6a8e..000000000000 --- a/plugins/CommonUtil/http.c +++ /dev/null @@ -1,123 +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" -#include -#include -#include - -PPH_BYTES UtilReadSocketString( - _In_ SOCKET Handle - ) -{ - 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; - - return PhCreateBytesEx(data, dataLength); -} - -PPH_BYTES UtilReadWinHttpString( - _In_ HINTERNET Handle - ) -{ - 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; - - return PhCreateBytesEx(data, dataLength); -} \ No newline at end of file diff --git a/plugins/CommonUtil/main.c b/plugins/CommonUtil/main.c index ea019c1f52aa..c5e5c664c546 100644 --- a/plugins/CommonUtil/main.c +++ b/plugins/CommonUtil/main.c @@ -24,26 +24,6 @@ #include "main.h" PPH_PLUGIN PluginInstance; -COMMONUTIL_INTERFACE PluginInterface = -{ - COMMONUTIL_INTERFACE_VERSION, - UtilCreateJsonParser, - UtilCleanupJsonParser, - UtilGetJsonValueAsString, - UtilGetJsonValueAsUlong, - UtilGetJsonObjectBool, - UtilCreateJsonObject, - UtilGetJsonObject, - UtilGetJsonObjectLength, - UtilJsonAddObject, - UtilCreateJsonArray, - UtilAddJsonArray, - UtilGetJsonArrayString, - UtilGetJsonArrayUlong, - UtilGetArrayLength, - UtilGetObjectArrayIndex, - UtilGetObjectArrayList -}; LOGICAL DllMain( _In_ HINSTANCE Instance, @@ -66,7 +46,6 @@ LOGICAL DllMain( info->Author = L"dmex"; info->Description = L"Common Plugin Extensions"; info->HasOptions = FALSE; - info->Interface = &PluginInterface; } break; } diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index 57509cc969c0..f8a791741008 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -53,7 +53,7 @@ NTSTATUS QueryPluginsCallbackThread( HINTERNET httpRequestHandle = NULL; ULONG xmlStringBufferLength = 0; PSTR xmlStringBuffer = NULL; - PVOID rootJsonObject; + PVOID rootJsonObject = NULL; PWCT_CONTEXT context = Parameter; if (!(httpSessionHandle = WinHttpOpen( @@ -109,10 +109,10 @@ NTSTATUS QueryPluginsCallbackThread( if (!ReadRequestString(httpRequestHandle, &xmlStringBuffer, &xmlStringBufferLength)) goto CleanupExit; - if (!(rootJsonObject = CreateJsonParser(xmlStringBuffer))) + if (!(rootJsonObject = PhCreateJsonParser(xmlStringBuffer))) goto CleanupExit; - for (INT i = 0; i < JsonGetArrayLength(rootJsonObject); i++) + for (INT i = 0; i < PhGetJsonArrayLength(rootJsonObject); i++) { PVOID jvalue; PPLUGIN_NODE entry; @@ -124,27 +124,27 @@ NTSTATUS QueryPluginsCallbackThread( 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")); + jvalue = PhGetJsonArrayIndexObject(rootJsonObject, i); + entry->Id = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_id")); + entry->InternalName = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_internal_name")); + entry->Name = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_name")); + entry->Version = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_version")); + entry->Author = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_author")); + entry->Description = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_description")); + entry->IconUrl = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_icon")); + entry->Requirements = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_requirements")); + entry->FeedbackUrl = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_feedback")); + entry->Screenshots = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_screenshots")); + entry->AddedTime = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_datetime_added")); + entry->UpdatedTime = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_datetime_updated")); + entry->Download_count = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_count")); + entry->Download_link_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_link_32")); + entry->Download_link_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_link_64")); + entry->SHA2_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_hash_32")); + entry->SHA2_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_hash_64")); + entry->HASH_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_signed_32")); + entry->HASH_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_signed_64")); + entry->FileName = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_filename")); swscanf( PhGetString(entry->UpdatedTime), @@ -253,6 +253,9 @@ NTSTATUS QueryPluginsCallbackThread( CleanupExit: + if (rootJsonObject) + PhFreeJsonParser(rootJsonObject); + if (httpRequestHandle) WinHttpCloseHandle(httpRequestHandle); diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h index 8bb75a8fb1d5..b425a322b6c3 100644 --- a/plugins/ExtraPlugins/main.h +++ b/plugins/ExtraPlugins/main.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 0dc634b825aa..d8a6d0d95c14 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -288,87 +288,28 @@ NTSTATUS UpdateDownloadThread( 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) }; + URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; 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), + context->SetupFilePath = PhGetCacheFileName(PhaFormatString( + L"%s.zip", 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), @@ -382,29 +323,33 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } + context->FileDownloadUrl = PhFormatString( + L"https://wj32.org/processhacker/plugins/download.php?id=%s&type=64", + PhGetStringOrEmpty(context->Node->Id) + ); + // Set lengths to non-zero enabling these params to be cracked. - httpUrlComponents.dwSchemeLength = (ULONG)-1; - httpUrlComponents.dwHostNameLength = (ULONG)-1; - httpUrlComponents.dwUrlPathLength = (ULONG)-1; + httpParts.dwSchemeLength = (ULONG)-1; + httpParts.dwHostNameLength = (ULONG)-1; + httpParts.dwUrlPathLength = (ULONG)-1; if (!WinHttpCrackUrl( PhGetString(context->FileDownloadUrl), 0, 0, - &httpUrlComponents + &httpParts )) { goto CleanupExit; } // Create the Host string. - downloadHostPath = PhCreateStringEx(httpUrlComponents.lpszHostName, httpUrlComponents.dwHostNameLength * sizeof(WCHAR)); + downloadHostPath = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); // Create the Path string. - downloadUrlPath = PhCreateStringEx(httpUrlComponents.lpszUrlPath, httpUrlComponents.dwUrlPathLength * sizeof(WCHAR)); + downloadUrlPath = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - // Open the HTTP session with the system proxy configuration if available if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, @@ -418,12 +363,10 @@ NTSTATUS UpdateDownloadThread( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption( httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, + &(ULONG) { WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, sizeof(ULONG) ); } @@ -431,7 +374,7 @@ NTSTATUS UpdateDownloadThread( if (!(httpConnectionHandle = WinHttpConnect( httpSessionHandle, PhGetString(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, + httpParts.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) { @@ -445,7 +388,7 @@ NTSTATUS UpdateDownloadThread( NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) + WINHTTP_FLAG_REFRESH | (httpParts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) ))) { goto CleanupExit; @@ -612,9 +555,6 @@ NTSTATUS UpdateDownloadThread( if (httpSessionHandle) WinHttpCloseHandle(httpSessionHandle); - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); PhClearReference(&downloadHostPath); PhClearReference(&downloadUrlPath); PhClearReference(&userAgentString); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index f57bd05db5ba..45a2db920356 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -289,13 +289,9 @@ NTSTATUS GeoIPUpdateThread( 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; + PPH_STRING directoryPath = NULL; URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; @@ -309,42 +305,9 @@ NTSTATUS GeoIPUpdateThread( if (!(fwLinkUrl = QueryFwLinkUrl(context))) goto CleanupExit; - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) + context->SetupFilePath = PhGetCacheFileName(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); + if (PhIsNullOrEmptyString(context->SetupFilePath)) 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; - - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } - } // Create output file if (!NT_SUCCESS(PhCreateFileWin32( @@ -555,79 +518,71 @@ NTSTATUS GeoIPUpdateThread( } } - PPH_STRING path; - PPH_STRING fullSetupPath; - PPH_BYTES mmdbGzPath; - gzFile file; - - path = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); - mmdbGzPath = PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath)); - - if (RtlDoesFileExists_U(PhGetString(path))) { - if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(path)))) - { - goto CleanupExit; - } - } + PPH_STRING gzpath; + PPH_BYTES mmdbGzPath; + gzFile gzfile; - if (fullSetupPath = PhGetFullPath(PhGetString(path), &indexOfFileName)) - { - PPH_STRING directoryPath; + gzpath = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); + mmdbGzPath = PH_AUTO(PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath))); - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) + if (RtlDoesFileExists_U(PhGetString(gzpath))) { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); + if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(gzpath)))) + goto CleanupExit; } - } - 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 - ))) + if (gzfile = gzopen(mmdbGzPath->Buffer, "rb")) { - IO_STATUS_BLOCK isb; - BYTE buffer[PAGE_SIZE]; - - while (!gzeof(file)) + HANDLE mmdbFileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &mmdbFileHandle, + PhGetStringOrEmpty(gzpath), + 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 + ))) { - 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); + } } } @@ -645,10 +600,19 @@ NTSTATUS GeoIPUpdateThread( if (httpSessionHandle) WinHttpCloseHandle(httpSessionHandle); - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); - PhClearReference(&userAgentString); + if (userAgentString) + PhDereferenceObject(userAgentString); + + if (RtlDoesFileExists_U(PhGetString(context->SetupFilePath))) + { + PhDeleteFileWin32(PhGetString(context->SetupFilePath)); + } + + if (directoryPath) + { + RemoveDirectory(PhGetString(directoryPath)); + PhDereferenceObject(directoryPath); + } if (success) { diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index ebd5e33898e5..c7a30d3097ad 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 1e5ca87291cd..4d060c290ff0 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -810,22 +810,22 @@ NTSTATUS UploadFileThreadStart( goto CleanupExit; } - if (jsonRootObject = CreateJsonParser(buffer)) + if (jsonRootObject = PhCreateJsonParser(buffer)) { - if (!GetJsonValueAsUlong(jsonRootObject, "response_code")) + if (PhGetJsonValueAsLong64(jsonRootObject, "response_code") != 0) goto CleanupExit; - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "resource")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "scan_id")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "sha256")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "verbose_msg")); + // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "resource")); + // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_id")); + // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha256")); + // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "verbose_msg")); - if (redirectUrl = GetJsonValueAsString(jsonRootObject, "permalink")) + if (redirectUrl = PhGetJsonValueAsString(jsonRootObject, "permalink")) { PhMoveReference(&context->LaunchCommand, PhZeroExtendToUtf16(redirectUrl)); } - CleanupJsonParser(jsonRootObject); + PhFreeJsonParser(jsonRootObject); } else { @@ -859,14 +859,14 @@ NTSTATUS UploadFileThreadStart( goto CleanupExit; } - if (rootJsonObject = CreateJsonParser(buffer)) + if (rootJsonObject = PhCreateJsonParser(buffer)) { - if (redirectUrl = GetJsonValueAsString(rootJsonObject, "redirecturl")) + if (redirectUrl = PhGetJsonValueAsString(rootJsonObject, "redirecturl")) { PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%hs", redirectUrl)); } - CleanupJsonParser(rootJsonObject); + PhFreeJsonParser(rootJsonObject); } } break; @@ -1048,28 +1048,30 @@ NTSTATUS UploadCheckThreadStart( goto CleanupExit; } - if (rootJsonObject = CreateJsonParser(subRequestBuffer->Buffer)) + if (rootJsonObject = PhCreateJsonParser(subRequestBuffer->Buffer)) { - if (context->FileExists = GetJsonValueAsBool(rootJsonObject, "file_exists")) + context->FileExists = PhGetJsonObjectBool(rootJsonObject, "file_exists"); + + if (context->FileExists) { INT64 detected = 0; INT64 detectedMax = 0; PVOID detectionRatio; - if (detectionRatio = JsonGetObject(rootJsonObject, "detection_ratio")) + if (detectionRatio = PhGetJsonObject(rootJsonObject, "detection_ratio")) { - detected = GetJsonArrayUlong(detectionRatio, 0); - detectedMax = GetJsonArrayUlong(detectionRatio, 1); + detected = PhGetJsonArrayLong64(detectionRatio, 0); + detectedMax = PhGetJsonArrayLong64(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")); + context->UploadUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "upload_url")); + context->ReAnalyseUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "reanalyse_url")); + context->LastAnalysisUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_url")); + context->FirstAnalysisDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "first_analysis_date")); + context->LastAnalysisDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_date")); + context->LastAnalysisAgo = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_ago")); PhMoveReference(&context->FirstAnalysisDate, VirusTotalStringToTime(context->FirstAnalysisDate)); PhMoveReference(&context->LastAnalysisDate, VirusTotalStringToTime(context->LastAnalysisDate)); @@ -1106,7 +1108,7 @@ NTSTATUS UploadCheckThreadStart( } else { - context->UploadUrl = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "upload_url")); + context->UploadUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "upload_url")); // No file found... Start the upload. if (!PhIsNullOrEmptyString(context->UploadUrl)) @@ -1116,7 +1118,7 @@ NTSTATUS UploadCheckThreadStart( } } - CleanupJsonParser(rootJsonObject); + PhFreeJsonParser(rootJsonObject); } } break; diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index a32907038f96..0a93f6e81a92 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -211,7 +211,7 @@ PPH_LIST VirusTotalJsonToResultList( ULONG arrayLength; PPH_LIST results; - if (!(arrayLength = JsonGetArrayLength(JsonObject))) + if (!(arrayLength = PhGetJsonArrayLength(JsonObject))) return NULL; results = PhCreateList(arrayLength); @@ -222,17 +222,17 @@ PPH_LIST VirusTotalJsonToResultList( PVOID jsonArrayObject; PSTR fileHash; - if (!(jsonArrayObject = JsonGetObjectArrayIndex(JsonObject, i))) + if (!(jsonArrayObject = PhGetJsonArrayIndexObject(JsonObject, i))) continue; result = PhAllocate(sizeof(VIRUSTOTAL_API_RESULT)); memset(result, 0, sizeof(VIRUSTOTAL_API_RESULT)); - fileHash = GetJsonValueAsString(jsonArrayObject, "hash"); - result->Found = GetJsonValueAsBool(jsonArrayObject, "found") == TRUE; - result->Positives = GetJsonValueAsUlong(jsonArrayObject, "positives"); - result->Total = GetJsonValueAsUlong(jsonArrayObject, "total"); + fileHash = PhGetJsonValueAsString(jsonArrayObject, "hash"); result->FileHash = fileHash ? PhZeroExtendToUtf16(fileHash) : NULL; + result->Found = PhGetJsonObjectBool(jsonArrayObject, "found") == TRUE; + result->Positives = PhGetJsonValueAsLong64(jsonArrayObject, "positives"); + result->Total = PhGetJsonValueAsLong64(jsonArrayObject, "total"); PhAddItemList(results, result); } @@ -279,13 +279,13 @@ 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); @@ -585,27 +585,27 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( //PVOID jsonScanObject; PVIRUSTOTAL_FILE_REPORT_RESULT result; - if (!(jsonRootObject = CreateJsonParser(subRequestBuffer))) + if (!(jsonRootObject = PhCreateJsonParser(subRequestBuffer))) goto CleanupExit; - if (!GetJsonValueAsUlong(jsonRootObject, "response_code")) + if (PhGetJsonValueAsLong64(jsonRootObject, "response_code") != 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")) + result->Total = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "total"), FALSE); + result->Positives = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "positives"), FALSE); + result->Resource = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "resource")); + result->ScanId = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_id")); + result->Md5 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "md5")); + result->Sha1 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha1")); + result->Sha256 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha256")); + result->ScanDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_date")); + result->Permalink = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "permalink")); + result->StatusMessage = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "verbose_msg")); + + //if (jsonScanObject = PhGetJsonObject(jsonRootObject, "scans")) //{ // PPH_LIST jsonArrayList; // @@ -617,9 +617,9 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( // { // 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"); + // //PSTR version = PhGetJsonValueAsString(object->Entry, "version"); + // //PSTR result = PhGetJsonValueAsString(object->Entry, "result"); + // //PSTR update = PhGetJsonValueAsString(object->Entry, "update"); // // PhFree(object); // } @@ -659,7 +659,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( PPH_LIST resultTempList = NULL; PPH_LIST virusTotalResults = NULL; - jsonArray = CreateJsonArray(); + jsonArray = PhCreateJsonArray(); resultTempList = PhCreateList(30); PhAcquireQueuedLockExclusive(&ProcessListLock); @@ -692,19 +692,19 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( VirusTotalBuildJsonArray(resultTempList->Items[i], jsonArray); } - if (!(jsonArrayToSendString = GetJsonArrayString(jsonArray))) + if (!(jsonArrayToSendString = PhGetJsonArrayString(jsonArray))) goto CleanupExit; if (!(jsonApiResult = VirusTotalSendHttpRequest(PhCreateBytes(jsonArrayToSendString)))) goto CleanupExit; - if (!(rootJsonObject = CreateJsonParser(jsonApiResult))) + if (!(rootJsonObject = PhCreateJsonParser(jsonApiResult))) 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)) @@ -758,12 +758,12 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (rootJsonObject) { - CleanupJsonParser(rootJsonObject); + PhFreeJsonParser(rootJsonObject); } if (jsonArray) { - CleanupJsonParser(jsonArray); + PhFreeJsonParser(jsonArray); } if (jsonApiResult) diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index c5fe5c051573..40cda4253452 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -88,10 +88,8 @@ VOID ShowAvailableDialog( config.pszWindowTitle = L"Process Hacker - Updater"; config.pszMainInstruction = L"A newer build of Process Hacker is available."; - config.pszContent = PhaFormatString(L"Version: %lu.%lu.%lu\r\nDownload size: %s\r\n\r\nView Changelog", - Context->MajorVersion, - Context->MinorVersion, - Context->RevisionVersion, + config.pszContent = PhaFormatString(L"Version: %s\r\nDownload size: %s\r\n\r\nView Changelog", + PhGetStringOrEmpty(Context->Version), PhGetStringOrEmpty(Context->Size) )->Buffer; diff --git a/plugins/Updater/page4.c b/plugins/Updater/page4.c index a8df5182aaa4..8c29f12be712 100644 --- a/plugins/Updater/page4.c +++ b/plugins/Updater/page4.c @@ -70,11 +70,7 @@ 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"; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 98956efd01df..1e5c29dd70f1 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -227,14 +227,14 @@ PPH_STRING UpdateWindowsString( return buildLabHeader; } -BOOLEAN ParseVersionString( - _Inout_ PPH_UPDATER_CONTEXT Context +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString ) { PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); + PhInitializeStringRef(&remaining, PhGetString(VersionString)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); @@ -244,10 +244,12 @@ BOOLEAN ParseVersionString( PhStringToInteger64(&minorPart, 10, &minorInteger); PhStringToInteger64(&revisionPart, 10, &revisionInteger); - Context->MajorVersion = (ULONG)majorInteger; - Context->MinorVersion = (ULONG)minorInteger; - Context->RevisionVersion = (ULONG)revisionInteger; - return TRUE; + return MAKE_VERSION_ULONGLONG( + (ULONG)majorInteger, + (ULONG)minorInteger, + (ULONG)revisionInteger, + 0 + ); } BOOLEAN ReadRequestString( @@ -414,50 +416,32 @@ BOOLEAN QueryUpdateData( if (!ReadRequestString(httpRequestHandle, &stringBuffer, &stringBufferLength)) goto CleanupExit; - // Check the buffer for valid data if (stringBuffer == NULL || stringBuffer[0] == '\0') goto CleanupExit; - if (!(jsonObject = CreateJsonParser(stringBuffer))) + if (!(jsonObject = PhCreateJsonParser(stringBuffer))) goto CleanupExit; - Context->Size = PhFormatSize(GetJsonValueAsUlong(jsonObject, "size"), 2); - if (tempValue = GetJsonValueAsString(jsonObject, "version")) + Context->Size = PhFormatSize(PhGetJsonValueAsLong64(jsonObject, "size"), 2); + if (tempValue = PhGetJsonValueAsString(jsonObject, "version")) { Context->Version = PhConvertUtf8ToUtf16(tempValue); Context->RevVersion = PhConvertUtf8ToUtf16(tempValue); } - if (tempValue = GetJsonValueAsString(jsonObject, "updated")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "updated")) Context->RelDate = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = GetJsonValueAsString(jsonObject, "hash_setup")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "hash_setup")) Context->Hash = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = GetJsonValueAsString(jsonObject, "sig")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "sig")) Context->Signature = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = GetJsonValueAsString(jsonObject, "forum_url")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "forum_url")) Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = GetJsonValueAsString(jsonObject, "setup_url")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "setup_url")) Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = GetJsonValueAsString(jsonObject, "changelog")) + if (tempValue = PhGetJsonValueAsString(jsonObject, "changelog")) Context->BuildMessage = PhConvertUtf8ToUtf16(tempValue); - if (!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)); - } - - CleanupJsonParser(jsonObject); + PhFreeJsonParser(jsonObject); if (PhIsNullOrEmptyString(Context->Version)) goto CleanupExit; @@ -475,8 +459,7 @@ BOOLEAN QueryUpdateData( goto CleanupExit; if (PhIsNullOrEmptyString(Context->Signature)) goto CleanupExit; - - if (!ParseVersionString(Context)) + if (PhIsNullOrEmptyString(Context->BuildMessage)) goto CleanupExit; success = TRUE; @@ -501,6 +484,23 @@ BOOLEAN QueryUpdateData( PhClearReference(&versionHeader); PhClearReference(&windowsHeader); + if (!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; } @@ -536,12 +536,7 @@ NTSTATUS UpdateCheckSilentThread( 0 ); #else - latestVersion = MAKE_VERSION_ULONGLONG( - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion, - 0 - ); + latestVersion = ParseVersionString(context->Version); #endif // Compare the current version against the latest available version @@ -609,12 +604,7 @@ NTSTATUS UpdateCheckThread( 0 ); #else - latestVersion = MAKE_VERSION_ULONGLONG( - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion, - 0 - ); + latestVersion = ParseVersionString(context->Version); #endif if (currentVersion == latestVersion) @@ -637,6 +627,25 @@ NTSTATUS UpdateCheckThread( 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 = PhGetCacheFileName(downloadFileName); + PhDereferenceObject(downloadFileName); + + return filePath; +} + NTSTATUS UpdateDownloadThread( _In_ PVOID Parameter ) @@ -648,16 +657,11 @@ NTSTATUS UpdateDownloadThread( 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) }; + URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; ULONG64 timeTicks = 0; @@ -673,57 +677,43 @@ NTSTATUS UpdateDownloadThread( context->CurrentMinorVersion, context->CurrentRevisionVersion ); + if (PhIsNullOrEmptyString(userAgentString)) goto CleanupExit; - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; + // Set lengths to non-zero enabling these params to be cracked. + httpParts.dwSchemeLength = (ULONG)-1; + httpParts.dwHostNameLength = (ULONG)-1; + httpParts.dwUrlPathLength = (ULONG)-1; - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) - goto CleanupExit; - if (PhIsNullOrEmptyString(setupTempPath)) + if (!WinHttpCrackUrl( + PhGetStringOrEmpty(context->SetupFileDownloadUrl), + 0, + 0, + &httpParts + )) + { + context->ErrorCode = GetLastError(); goto CleanupExit; + } - // Generate random guid for our directory path. - PhGenerateGuid(&randomGuid); - - if (randomGuidString = PhFormatGuid(&randomGuid)) - { - PPH_STRING guidSubString; + // Create the Host string. + downloadHostPath = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); - // Strip the left and right curly brackets. - guidSubString = PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2); + if (PhIsNullOrEmptyString(downloadHostPath)) + goto CleanupExit; - PhMoveReference(&randomGuidString, guidSubString); - } + // Create the remote path string. + downloadUrlPath = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); - // 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)) + if (PhIsNullOrEmptyString(downloadUrlPath)) 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; + // Create the local path string. + context->SetupFilePath = UpdaterParseDownloadFileName(downloadUrlPath); - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } - } + if (PhIsNullOrEmptyString(context->SetupFilePath)) + goto CleanupExit; // Create output file if (!NT_SUCCESS(PhCreateFileWin32( @@ -739,38 +729,6 @@ NTSTATUS UpdateDownloadThread( 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..."); // Open the HTTP session with the system proxy configuration if available @@ -799,7 +757,7 @@ NTSTATUS UpdateDownloadThread( if (!(httpConnectionHandle = WinHttpConnect( httpSessionHandle, PhGetString(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, + httpParts.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) { @@ -814,7 +772,7 @@ NTSTATUS UpdateDownloadThread( NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) + WINHTTP_FLAG_REFRESH | (httpParts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) ))) { context->ErrorCode = GetLastError(); @@ -861,11 +819,7 @@ NTSTATUS UpdateDownloadThread( IO_STATUS_BLOCK isb; BYTE buffer[PAGE_SIZE]; - status = PhFormatString(L"Downloading update %lu.%lu.%lu...", - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion - ); + 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); @@ -995,12 +949,14 @@ NTSTATUS UpdateDownloadThread( if (httpSessionHandle) WinHttpCloseHandle(httpSessionHandle); - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); - PhClearReference(&downloadHostPath); - PhClearReference(&downloadUrlPath); - PhClearReference(&userAgentString); + if (downloadHostPath) + PhDereferenceObject(downloadHostPath); + + if (downloadUrlPath) + PhDereferenceObject(downloadUrlPath); + + if (userAgentString) + WinHttpCloseHandle(userAgentString); if (UpdateDialogThreadHandle) { diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index ae1d6e5e9f46..d4bf4abda0aa 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2011-2015 dmex + * Copyright (C) 2011-2017 dmex * * This file is part of Process Hacker. * @@ -28,10 +28,11 @@ #define INITGUID #include #include -#include -#include +#include #include +#include #include +#include #include #include @@ -88,9 +89,6 @@ typedef struct _PH_UPDATER_CONTEXT HWND DialogHandle; ULONG ErrorCode; - ULONG MinorVersion; - ULONG MajorVersion; - ULONG RevisionVersion; ULONG CurrentMinorVersion; ULONG CurrentMajorVersion; ULONG CurrentRevisionVersion; @@ -174,6 +172,10 @@ BOOLEAN UpdaterInstalledUsingSetup( VOID ); +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString + ); + // options.c VOID ShowOptionsDialog( diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index c5511468865c..70040158bdf9 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -27,468 +27,11 @@ #define COMMONUTIL_PLUGIN_NAME L"ProcessHacker.CommonUtil" #define COMMONUTIL_INTERFACE_VERSION 1 -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_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 -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 diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 23ed51105d21..c6a02b535668 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -45,7 +45,7 @@ public static class Build private static string BuildOutputFolder; private static string BuildCommit; private static string BuildVersion; - private static string BuildWebSetupVersion; + //private static string BuildWebSetupVersion; private static string BuildLongVersion; private static string BuildCount; private static string BuildRevision; @@ -58,9 +58,9 @@ public static class Build private static string BuildSetupHash; private static string BuildSetupSig; - private static long BuildWebSetupFileLength; - private static string BuildWebSetupHash; - private static string BuildWebSetupSig; + //private static long BuildWebSetupFileLength; + //private static string BuildWebSetupHash; + //private static string BuildWebSetupSig; #region Build Config private static readonly string[] sdk_directories = @@ -123,6 +123,7 @@ public static class Build "guisup.h", "hexedit.h", "hndlinfo.h", + "json.h", "kphapi.h", "kphuser.h", "lsasup.h", @@ -696,8 +697,8 @@ public static bool BuildWebSetupExe() try { - var webSetupVersion = System.Diagnostics.FileVersionInfo.GetVersionInfo(BuildOutputFolder + "\\processhacker-build-websetup.exe"); - BuildWebSetupVersion = webSetupVersion.FileVersion.Replace(",", "."); + //var webSetupVersion = System.Diagnostics.FileVersionInfo.GetVersionInfo(BuildOutputFolder + "\\processhacker-build-websetup.exe"); + //BuildWebSetupVersion = webSetupVersion.FileVersion.Replace(",", "."); } catch (Exception ex) { @@ -847,22 +848,22 @@ public static bool BuildChecksumsFile() 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-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-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-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"); @@ -895,22 +896,12 @@ public static bool BuildUpdateSignature() return true; } - if (!File.Exists(BuildOutputFolder + "\\processhacker-build-websetup.exe")) - { - Program.PrintColorMessage("[SKIPPED] websetup-setup.exe 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; } - BuildWebSetupSig = Win32.ShellExecute( - CustomSignToolPath, - "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-websetup.exe -h" - ); BuildSetupSig = Win32.ShellExecute( CustomSignToolPath, "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-setup.exe -h" @@ -934,10 +925,6 @@ public static void WebServiceUpdateConfig() return; if (string.IsNullOrEmpty(BuildBinHash)) return; - if (string.IsNullOrEmpty(BuildWebSetupSig)) - return; - if (string.IsNullOrEmpty(BuildWebSetupVersion)) - return; string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\""); string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\" --abbrev-commit"); @@ -956,10 +943,10 @@ public static void WebServiceUpdateConfig() BinHash = BuildBinHash, //BinSig = BuildBinSig, - WebSetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-websetup.exe", - WebSetupHash = BuildWebSetupHash, - WebSetupVersion = BuildWebSetupVersion, - WebSetupSig = BuildWebSetupSig, + //WebSetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-websetup.exe", + //WebSetupHash = BuildWebSetupHash, + //WebSetupVersion = BuildWebSetupVersion, + //WebSetupSig = BuildWebSetupSig, Message = buildSummary, Changelog = buildChangelog, @@ -1006,14 +993,14 @@ public static bool AppveyorUploadBuildFiles() { string[] buildFileArray = { - BuildOutputFolder + "\\processhacker-build-websetup.exe", + //BuildOutputFolder + "\\processhacker-build-websetup.exe", BuildOutputFolder + "\\processhacker-build-setup.exe", BuildOutputFolder + "\\processhacker-build-bin.zip", BuildOutputFolder + "\\processhacker-build-checksums.txt" }; string[] releaseFileArray = { - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-websetup.exe", + //BuildOutputFolder + "\\processhacker-websetup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 05eda6988b8f..572f1a1d682c 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -226,8 +226,8 @@ public static void Main(string[] args) if (!Build.BuildBinZip()) return; - if (!Build.BuildWebSetupExe()) - return; + //if (!Build.BuildWebSetupExe()) + // return; if (!Build.BuildSetupExe()) return; if (!Build.BuildChecksumsFile()) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index a4a1356fa0497add7d6a45e8608db4a98934ff9f..9571ac2a843046720c3a6d2246eed1203b5e0b9f 100644 GIT binary patch delta 18662 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 R{<%Vasqb_C(en4w{{SQo(8K@$ delta 19275 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 Kxz+Vos{J327W^Fm delta 14505 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 From 34b798e03ad61f24da92e69e8f315dc526de8d0a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 10 Aug 2017 23:22:04 +1000 Subject: [PATCH 364/839] Setup: Update to latest phlib --- .../CustomSetupTool/CustomSetupTool.vcxproj | 30 +- .../CustomSetupTool.vcxproj.filters | 86 +- .../CustomSetupTool/download.c | 186 ++-- .../CustomSetupTool/downloadpage.c | 3 - .../CustomSetupTool/include/setup.h | 15 +- .../CustomSetupTool/installpage.c | 6 +- .../CustomSetupTool/json-c/AUTHORS | 5 - .../CustomSetupTool/json-c/COPYING | 42 - .../CustomSetupTool/json-c/ChangeLog | 214 ----- .../CustomSetupTool/json-c/arraylist.c | 101 -- .../CustomSetupTool/json-c/arraylist.h | 56 -- .../CustomSetupTool/json-c/bits.h | 28 - .../CustomSetupTool/json-c/config.h | 89 -- .../CustomSetupTool/json-c/config.h.in | 174 ---- .../CustomSetupTool/json-c/debug.c | 83 -- .../CustomSetupTool/json-c/debug.h | 71 -- .../CustomSetupTool/json-c/json.h | 34 - .../CustomSetupTool/json-c/json_c_version.c | 20 - .../CustomSetupTool/json-c/json_c_version.h | 22 - .../CustomSetupTool/json-c/json_config.h | 3 - .../CustomSetupTool/json-c/json_inttypes.h | 28 - .../CustomSetupTool/json-c/json_object.c | 860 ----------------- .../CustomSetupTool/json-c/json_object.h | 611 ------------ .../json-c/json_object_iterator.c | 168 ---- .../json-c/json_object_iterator.h | 239 ----- .../json-c/json_object_private.h | 47 - .../CustomSetupTool/json-c/json_tokener.c | 888 ------------------ .../CustomSetupTool/json-c/json_tokener.h | 208 ---- .../CustomSetupTool/json-c/json_util.c | 301 ------ .../CustomSetupTool/json-c/json_util.h | 41 - .../CustomSetupTool/json-c/libjson.c | 26 - .../CustomSetupTool/json-c/linkhash.c | 604 ------------ .../CustomSetupTool/json-c/linkhash.h | 292 ------ .../CustomSetupTool/json-c/math_compat.h | 28 - .../CustomSetupTool/json-c/printbuf.c | 194 ---- .../CustomSetupTool/json-c/printbuf.h | 81 -- .../CustomSetupTool/json-c/random_seed.c | 238 ----- .../CustomSetupTool/json-c/random_seed.h | 25 - tools/CustomSetupTool/CustomSetupTool/setup.c | 90 +- .../CustomSetupTool/updatesetup.c | 29 - .../CustomSetupTool/websetuppage.c | 136 +++ 41 files changed, 237 insertions(+), 6165 deletions(-) delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/COPYING delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/bits.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/config.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/debug.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/debug.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c delete mode 100644 tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h delete mode 100644 tools/CustomSetupTool/CustomSetupTool/updatesetup.c create mode 100644 tools/CustomSetupTool/CustomSetupTool/websetuppage.c diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 9cde444d6474..e6877e29be51 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -112,17 +112,6 @@ - - - - - - - - - - - @@ -133,28 +122,11 @@ - + - - - - - - - - - - - - - - - - - diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index fed6da63ac64..f27d961d9b0b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -57,39 +57,6 @@ Source Files - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - - - Source Files\json - Source Files\pages @@ -108,7 +75,7 @@ Source Files\pages - + Source Files @@ -125,57 +92,6 @@ Header Files\zip - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - - - Header Files\json - diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index eeb0259e5789..0fc0f7fc1aa7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -3,8 +3,6 @@ #include #include -#include "json-c\json.h" - PPH_STRING SetupGetVersion( VOID ) @@ -74,14 +72,14 @@ PPH_STRING UpdateWindowsString( return buildLabHeader; } -BOOLEAN ParseVersionString( - _Inout_ PPH_SETUP_CONTEXT Context +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString ) { PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - - PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->SetupFileVersion)); + + PhInitializeStringRef(&remaining, PhGetString(VersionString)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); @@ -91,10 +89,12 @@ BOOLEAN ParseVersionString( PhStringToInteger64(&minorPart, 10, &minorInteger); PhStringToInteger64(&revisionPart, 10, &revisionInteger); - Context->LatestMajorVersion = (ULONG)majorInteger; - Context->LatestMinorVersion = (ULONG)minorInteger; - Context->LatestRevisionVersion = (ULONG)revisionInteger; - return TRUE; + return MAKE_VERSION_ULONGLONG( + (ULONG)majorInteger, + (ULONG)minorInteger, + (ULONG)revisionInteger, + 0 + ); } BOOLEAN ReadRequestString( @@ -146,21 +146,6 @@ BOOLEAN ReadRequestString( return TRUE; } -json_object_ptr json_get_object( - _In_ json_object_ptr rootObj, - _In_ const PSTR key -) -{ - json_object_ptr returnObj; - - if (json_object_object_get_ex(rootObj, key, &returnObj)) - { - return returnObj; - } - - return NULL; -} - BOOLEAN SetupQueryUpdateData( _Inout_ PPH_SETUP_CONTEXT Context ) @@ -277,38 +262,35 @@ BOOLEAN SetupQueryUpdateData( if (stringBuffer == NULL || stringBuffer[0] == '\0') goto CleanupExit; - if (!(jsonObject = json_tokener_parse(stringBuffer))) + if (!(jsonObject = PhCreateJsonParser(stringBuffer))) goto CleanupExit; - if (value = json_object_get_string(json_get_object(jsonObject, "updated"))) + if (value = PhGetJsonValueAsString(jsonObject, "updated")) Context->RelDate = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "size"))) + if (value = PhGetJsonValueAsString(jsonObject, "size")) Context->Size = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "forum_url"))) + if (value = PhGetJsonValueAsString(jsonObject, "forum_url")) Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "bin_url"))) + if (value = PhGetJsonValueAsString(jsonObject, "bin_url")) Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "hash_bin"))) + if (value = PhGetJsonValueAsString(jsonObject, "hash_bin")) Context->BinFileHash = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "setup_url"))) + if (value = PhGetJsonValueAsString(jsonObject, "setup_url")) Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "sig"))) + if (value = PhGetJsonValueAsString(jsonObject, "sig")) Context->SetupFileSignature = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "version"))) + if (value = PhGetJsonValueAsString(jsonObject, "version")) Context->SetupFileVersion = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "websetup_url"))) + if (value = PhGetJsonValueAsString(jsonObject, "websetup_url")) Context->WebSetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "websetup_sig"))) + if (value = PhGetJsonValueAsString(jsonObject, "websetup_sig")) Context->WebSetupFileSignature = PhConvertUtf8ToUtf16(value); - if (value = json_object_get_string(json_get_object(jsonObject, "websetup_version"))) + if (value = PhGetJsonValueAsString(jsonObject, "websetup_version")) Context->WebSetupFileVersion = PhConvertUtf8ToUtf16(value); - if (!ParseVersionString(Context)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->RelDate)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->Size)) @@ -327,7 +309,14 @@ BOOLEAN SetupQueryUpdateData( goto CleanupExit; if (PhIsNullOrEmptyString(Context->SetupFileVersion)) goto CleanupExit; - + + if (PhIsNullOrEmptyString(Context->WebSetupFileDownloadUrl)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->WebSetupFileSignature)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->WebSetupFileVersion)) + goto CleanupExit; + success = TRUE; CleanupExit: @@ -342,7 +331,7 @@ BOOLEAN SetupQueryUpdateData( WinHttpCloseHandle(httpSessionHandle); if (jsonObject) - json_object_put(jsonObject); + PhFreeJsonParser(jsonObject); if (stringBuffer) PhFree(stringBuffer); @@ -362,14 +351,11 @@ BOOLEAN UpdateDownloadUpdateData( HINTERNET httpSessionHandle = NULL; HINTERNET httpConnectionHandle = NULL; HINTERNET httpRequestHandle = NULL; - PPH_STRING setupTempPath = NULL; + PPH_STRING downloadFileName = NULL; PPH_STRING downloadHostPath = NULL; PPH_STRING downloadUrlPath = NULL; PPH_STRING userAgentString = NULL; - PPH_STRING fullSetupPath = NULL; - PPH_STRING randomGuidString = NULL; ULONG indexOfFileName = -1; - GUID randomGuid; URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; @@ -385,46 +371,45 @@ BOOLEAN UpdateDownloadUpdateData( Context->CurrentRevisionVersion ); - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) - goto CleanupExit; - - PhGenerateGuid(&randomGuid); + httpUrlComponents.dwSchemeLength = (ULONG)-1; + httpUrlComponents.dwHostNameLength = (ULONG)-1; + httpUrlComponents.dwUrlPathLength = (ULONG)-1; - if (randomGuidString = PhFormatGuid(&randomGuid)) + if (!WinHttpCrackUrl( + PhGetString(Context->BinFileDownloadUrl), + 0, + 0, + &httpUrlComponents + )) { - PhMoveReference( - &randomGuidString, - PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2) - ); + Context->ErrorCode = GetLastError(); + goto CleanupExit; } - Context->FilePath = PhFormatString( - L"%s%s\\processhacker-%lu.%lu.%lu-bin.zip", - PhGetStringOrEmpty(setupTempPath), - PhGetStringOrEmpty(randomGuidString), - Context->LatestMajorVersion, - Context->LatestMinorVersion, - Context->LatestRevisionVersion + downloadHostPath = PhCreateStringEx( + httpUrlComponents.lpszHostName, + httpUrlComponents.dwHostNameLength * sizeof(WCHAR) + ); + downloadUrlPath = PhCreateStringEx( + httpUrlComponents.lpszUrlPath, + httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) ); - if (PhIsNullOrEmptyString(Context->FilePath)) - goto CleanupExit; - if (fullSetupPath = PhGetFullPath(PhGetString(Context->FilePath), &indexOfFileName)) { - PPH_STRING directoryPath; + PH_STRINGREF pathPart; + PH_STRINGREF baseNamePart; - if (indexOfFileName == -1) + if (!PhSplitStringRefAtLastChar(&downloadUrlPath->sr, '/', &pathPart, &baseNamePart)) goto CleanupExit; - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } + downloadFileName = PhCreateString2(&baseNamePart); } + Context->FilePath = PhGetCacheFileName(downloadFileName); + + if (PhIsNullOrEmptyString(Context->FilePath)) + goto CleanupExit; + if (!NT_SUCCESS(PhCreateFileWin32( &tempFileHandle, PhGetString(Context->FilePath), @@ -438,34 +423,9 @@ BOOLEAN UpdateDownloadUpdateData( goto CleanupExit; } - 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) - ); - - SetWindowText(Context->MainHeaderHandle, PhFormatString(L"Downloading Process Hacker %lu.%lu.%lu...", - Context->LatestMajorVersion, - Context->CurrentMinorVersion, - Context->LatestRevisionVersion + SetWindowText(Context->MainHeaderHandle, PhFormatString( + L"Downloading Process Hacker %s...", + PhGetString(Context->SetupFileVersion) )->Buffer); //SetWindowText(Context->SubHeaderHandle, L"Progress: ~ of ~ (0.0%)"); @@ -643,28 +603,10 @@ BOOLEAN UpdateDownloadUpdateData( 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); - // } - //} - + PhClearReference(&downloadFileName); + return downloadSuccess; } diff --git a/tools/CustomSetupTool/CustomSetupTool/downloadpage.c b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c index ced62e004766..8d82b65a09a4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/downloadpage.c +++ b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c @@ -29,9 +29,6 @@ NTSTATUS SetupDownloadProgressThread( if (!SetupQueryUpdateData(Context)) goto CleanupExit; - //if (SetupUpdateWebSetupBuild(Context)) - // goto CleanupExit; - if (!UpdateDownloadUpdateData(Context)) goto CleanupExit; diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 78eccacb55ad..fa8478a5403f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,6 @@ typedef struct _PH_SETUP_CONTEXT ULONG ErrorCode; PPH_STRING FilePath; - PPH_STRING RevVersion; PPH_STRING RelDate; PPH_STRING Size; PPH_STRING ReleaseNotesUrl; @@ -130,9 +130,6 @@ typedef struct _PH_SETUP_CONTEXT ULONG CurrentMajorVersion; ULONG CurrentMinorVersion; ULONG CurrentRevisionVersion; - ULONG LatestMajorVersion; - ULONG LatestMinorVersion; - ULONG LatestRevisionVersion; } PH_SETUP_CONTEXT, *PPH_SETUP_CONTEXT; VOID SetupLoadImage( @@ -235,6 +232,16 @@ VOID SetupCreateImageFileExecutionOptions( // 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 ); diff --git a/tools/CustomSetupTool/CustomSetupTool/installpage.c b/tools/CustomSetupTool/CustomSetupTool/installpage.c index 8211041e995e..c8372ca76d34 100644 --- a/tools/CustomSetupTool/CustomSetupTool/installpage.c +++ b/tools/CustomSetupTool/CustomSetupTool/installpage.c @@ -174,10 +174,8 @@ INT_PTR CALLBACK SetupInstallPropPage_WndProc( )->Buffer); #else SetWindowText(context->MainHeaderHandle, PhaFormatString( - L"Installing Process Hacker %lu.%lu.%lu", - context->LatestMajorVersion, - context->LatestMinorVersion, - context->LatestRevisionVersion + L"Installing Process Hacker %s", + PhGetString(context->SetupFileVersion) )->Buffer); #endif SendMessage(context->ProgressHandle, PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS b/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS deleted file mode 100644 index b389989c452b..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/AUTHORS +++ /dev/null @@ -1,5 +0,0 @@ -Michael Clark -Jehiah Czebotar -Eric Haszlakiewicz -C. Watford (christopher.watford@gmail.com) - diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING b/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING deleted file mode 100644 index 740d1258d425..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/COPYING +++ /dev/null @@ -1,42 +0,0 @@ - -Copyright (c) 2009-2012 Eric Haszlakiewicz - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -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 OR COPYRIGHT HOLDERS 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. - ----------------------------------------------------------------- - -Copyright (c) 2004, 2005 Metaparadigm Pte Ltd - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -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 OR COPYRIGHT HOLDERS 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. diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog b/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog deleted file mode 100644 index 451b8f68403a..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/ChangeLog +++ /dev/null @@ -1,214 +0,0 @@ - -0.12 - - * Address security issues: - * CVE-2013-6371: hash collision denial of service - * CVE-2013-6370: buffer overflow if size_t is larger than int - - * Avoid potential overflow in json_object_get_double - - * Eliminate the mc_abort() function and MC_ABORT macro. - - * Make the json_tokener_errors array local. It has been deprecated for - a while, and json_tokener_error_desc() should be used instead. - - * change the floating point output format to %.17g so values with - more than 6 digits show up in the output. - - * Remove the old libjson.so name compatibility support. The library is - only created as libjson-c.so now and headers are only installed - into the ${prefix}/json-c directory. - - * When supported by the linker, add the -Bsymbolic-functions flag. - - * Various changes to fix the build on MSVC. - - * Make strict mode more strict: - * number must not start with 0 - * no single-quote strings - * no comments - * trailing char not allowed - * only allow lowercase literals - - * Added a json_object_new_double_s() convenience function to allow - an exact string representation of a double to be specified when - creating the object and use it in json_tokener_parse_ex() so - a re-serialized object more exactly matches the input. - - * Add support NaN and Infinity - - -0.11 - - * IMPORTANT: the name of the library has changed to libjson-c.so and - the header files are now in include/json-c. - The pkgconfig name has also changed from json to json-c. - You should change your build to use appropriate -I and -l options. - A compatibility shim is in place so builds using the old name will - continue to work, but that will be removed in the next release. - * Maximum recursion depth is now a runtime option. - json_tokener_new() is provided for compatibility. - json_tokener_new_ex(depth) - * Include json_object_iterator.h in the installed headers. - * Add support for building on Android. - * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. - * Make it safe to delete keys while iterating with the json_object_object_foreach macro. - * Add a json_set_serializer() function to allow the string output of a json_object to be customized. - * Make float parsing locale independent. - * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. - * Enable -Werror when building. - * speed improvements to parsing 64-bit integers on systems with working sscanf - * Add a json_object_object_length function. - * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. - -0.10 - - * Add a json_object_to_json_string_ext() function to allow output to be - formatted in a more human readable form. - * Add json_object_object_get_ex(), a NULL-safe get object method, to be able - to distinguish between a key not present and the value being NULL. - * Add an alternative iterator implementation, see json_object_iterator.h - * Make json_object_iter public to enable external use of the - json_object_object_foreachC macro. - * Add a printbuf_memset() function to provide an effecient way to set and - append things like whitespace indentation. - * Adjust json_object_is_type and json_object_get_type so they return - json_type_null for NULL objects and handle NULL passed to - json_objct_object_get(). - * Rename boolean type to json_bool. - * Fix various compile issues for Visual Studio and MinGW. - * Allow json_tokener_parse_ex() to be re-used to parse multiple object. - Also, fix some parsing issues with capitalized hexadecimal numbers and - number in E notation. - * Add json_tokener_get_error() and json_tokener_error_desc() to better - encapsulate the process of retrieving errors while parsing. - * Various improvements to the documentation of many functions. - * Add new json_object_array_sort() function. - * Fix a bug in json_object_get_int(), which would incorrectly return 0 - when called on a string type object. - Eric Haszlakiewicz - * Add a json_type_to_name() function. - Eric Haszlakiewicz - * Add a json_tokener_parse_verbose() function. - Jehiah Czebotar - * Improve support for null bytes within JSON strings. - Jehiah Czebotar - * Fix file descriptor leak if memory allocation fails in json_util - Zachary Blair, zack_blair at hotmail dot com - * Add int64 support. Two new functions json_object_net_int64 and - json_object_get_int64. Binary compatibility preserved. - Eric Haszlakiewicz, EHASZLA at transunion com - Rui Miguel Silva Seabra, rms at 1407 dot org - * Fix subtle bug in linkhash where lookup could hang after all slots - were filled then successively freed. - Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com - * Make json_object_from_file take const char *filename - Spotted by Vikram Raj V, vsagar at attinteractive dot com - * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) - Brent Miller, bdmiller at yahoo dash inc dot com - * Correction to comment describing printbuf_memappend in printbuf.h - Brent Miller, bdmiller at yahoo dash inc dot com - -0.9 - * Add README.html README-WIN32.html config.h.win32 to Makefile.am - Michael Clark, - * Add const qualifier to the json_tokener_parse functions - Eric Haszlakiewicz, EHASZLA at transunion dot com - * Rename min and max so we can never clash with C or C++ std library - Ian Atha, thatha at yahoo dash inc dot com - * Fix any noticeable spelling or grammar errors. - * Make sure every va_start has a va_end. - * Check all pointers for validity. - Erik Hovland, erik at hovland dot org - * Fix json_object_get_boolean to return false for empty string - Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com - * optimizations to json_tokener_parse_ex(), printbuf_memappend() - Brent Miller, bdmiller at yahoo dash inc dot com - * Disable REFCOUNT_DEBUG by default in json_object.c - * Don't use this as a variable, so we can compile with a C++ compiler - * Add casts from void* to type of assignment when using malloc - * Add #ifdef __cplusplus guards to all of the headers - * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table - Michael Clark, - * Null pointer dereference fix. Fix json_object_get_boolean strlen test - to not return TRUE for zero length string. Remove redundant includes. - Erik Hovland, erik at hovland dot org - * Fixed warning reported by adding -Wstrict-prototypes - -Wold-style-definition to the compilatin flags. - Dotan Barak, dotanba at gmail dot com - * Add const correctness to public interfaces - Gerard Krol, g dot c dot krol at student dot tudelft dot nl - -0.8 - * Add va_end for every va_start - Dotan Barak, dotanba at gmail dot com - * Add macros to enable compiling out debug code - Geoffrey Young, geoff at modperlcookbook dot org - * Fix bug with use of capital E in numbers with exponents - Mateusz Loskot, mateusz at loskot dot net - * Add stddef.h include - * Patch allows for json-c compile with -Werror and not fail due to - -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations - Geoffrey Young, geoff at modperlcookbook dot org - -0.7 - * Add escaping of backslash to json output - * Add escaping of foward slash on tokenizing and output - * Changes to internal tokenizer from using recursion to - using a depth state structure to allow incremental parsing - -0.6 - * Fix bug in escaping of control characters - Johan Bj�rklund, johbjo09 at kth dot se - * Remove include "config.h" from headers (should only - be included from .c files) - Michael Clark - -0.5 - * Make headers C++ compatible by change *this to *obj - * Add ifdef C++ extern "C" to headers - * Use simpler definition of min and max in bits.h - Larry Lansing, llansing at fuzzynerd dot com - - * Remove automake 1.6 requirement - * Move autogen commands into autogen.sh. Update README - * Remove error pointer special case for Windows - * Change license from LGPL to MIT - Michael Clark - -0.4 - * Fix additional error case in object parsing - * Add back sign reversal in nested object parse as error pointer - value is negative, while error value is positive. - Michael Clark - -0.3 - * fix pointer arithmetic bug for error pointer check in is_error() macro - * fix type passed to printbuf_memappend in json_tokener - * update autotools bootstrap instructions in README - Michael Clark - -0.2 - * printbuf.c - C. Watford (christopher.watford@gmail.com) - Added a Win32/Win64 compliant implementation of vasprintf - * debug.c - C. Watford (christopher.watford@gmail.com) - Removed usage of vsyslog on Win32/Win64 systems, needs to be handled - by a configure script - * json_object.c - C. Watford (christopher.watford@gmail.com) - Added scope operator to wrap usage of json_object_object_foreach, this - needs to be rethought to be more ANSI C friendly - * json_object.h - C. Watford (christopher.watford@gmail.com) - Added Microsoft C friendly version of json_object_object_foreach - * json_tokener.c - C. Watford (christopher.watford@gmail.com) - Added a Win32/Win64 compliant implementation of strndup - * json_util.c - C. Watford (christopher.watford@gmail.com) - Added cast and mask to suffice size_t v. unsigned int conversion - correctness - * json_tokener.c - sign reversal issue on error info for nested object parse - spotted by Johan Bj�rklund (johbjo09 at kth.se) - * json_object.c - escape " in json_escape_str - * Change to automake and libtool to build shared and static library - Michael Clark - -0.1 - * initial release diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c deleted file mode 100644 index 97f2c921711b..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * $Id: arraylist.c,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. - * - */ - -#include "config.h" - -#ifdef STDC_HEADERS -# include -# include -#endif /* STDC_HEADERS */ - -#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) -# include -#endif /* HAVE_STRINGS_H */ - -#include "bits.h" -#include "arraylist.h" - -struct array_list* -array_list_new(array_list_free_fn *free_fn) -{ - struct array_list *arr; - - arr = (struct array_list*)calloc(1, sizeof(struct array_list)); - if(!arr) return NULL; - arr->size = ARRAY_LIST_DEFAULT_SIZE; - arr->length = 0; - arr->free_fn = free_fn; - if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { - free(arr); - return NULL; - } - return arr; -} - -extern void -array_list_free(struct array_list *arr) -{ - int i; - for(i = 0; i < arr->length; i++) - if(arr->array[i]) arr->free_fn(arr->array[i]); - free(arr->array); - free(arr); -} - -void* -array_list_get_idx(struct array_list *arr, int i) -{ - if(i >= arr->length) return NULL; - return arr->array[i]; -} - -static int array_list_expand_internal(struct array_list *arr, int max) -{ - void *t; - int 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; - arr->array = (void**)t; - (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); - arr->size = new_size; - return 0; -} - -int -array_list_put_idx(struct array_list *arr, int idx, void *data) -{ - if(array_list_expand_internal(arr, idx+1)) return -1; - if(arr->array[idx]) arr->free_fn(arr->array[idx]); - arr->array[idx] = data; - if(arr->length <= idx) arr->length = idx + 1; - return 0; -} - -int -array_list_add(struct array_list *arr, void *data) -{ - return array_list_put_idx(arr, arr->length, 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); -} - -int -array_list_length(struct array_list *arr) -{ - return arr->length; -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h b/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h deleted file mode 100644 index 089be0bd7215..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/arraylist.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * $Id: arraylist.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. - * - */ - -#ifndef _arraylist_h_ -#define _arraylist_h_ - -#ifdef __cplusplus -extern "C" { -#endif - -#define ARRAY_LIST_DEFAULT_SIZE 32 - -typedef void (array_list_free_fn) (void *data); - -struct array_list -{ - void **array; - int length; - int size; - array_list_free_fn *free_fn; -}; - -extern struct array_list* -array_list_new(array_list_free_fn *free_fn); - -extern void -array_list_free(struct array_list *al); - -extern void* -array_list_get_idx(struct array_list *al, int i); - -extern int -array_list_put_idx(struct array_list *al, int i, void *data); - -extern int -array_list_add(struct array_list *al, void *data); - -extern int -array_list_length(struct array_list *al); - -extern void -array_list_sort(struct array_list *arr, int(__cdecl* compar)(const void *, const void *)); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h b/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h deleted file mode 100644 index c8cbbc820d5b..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/bits.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * $Id: bits.h,v 1.10 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 _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 - -#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) -#define error_ptr(error) ((void*)error) -#define error_description(error) (json_tokener_errors[error]) -#define is_error(ptr) (ptr == NULL) - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/config.h b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h deleted file mode 100644 index df46f0a481a7..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in b/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in deleted file mode 100644 index 0dcab1a30015..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/config.h.in +++ /dev/null @@ -1,174 +0,0 @@ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* 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. */ -#undef HAVE_DECL_INFINITY - -/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. - */ -#undef HAVE_DECL_ISINF - -/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. - */ -#undef HAVE_DECL_ISNAN - -/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ -#undef HAVE_DECL_NAN - -/* Define to 1 if you have the declaration of `_finite', and to 0 if you - don't. */ -#undef HAVE_DECL__FINITE - -/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. - */ -#undef HAVE_DECL__ISNAN - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* 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. */ -#undef HAVE_ENDIAN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_LIMITS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_LOCALE_H - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#undef HAVE_MALLOC - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the `open' function. */ -#undef HAVE_OPEN - -/* Define to 1 if your system has a GNU libc compatible `realloc' function, - and to 0 otherwise. */ -#undef HAVE_REALLOC - -/* Define to 1 if you have the `setlocale' function. */ -#undef HAVE_SETLOCALE - -/* Define to 1 if you have the `snprintf' function. */ -#undef HAVE_SNPRINTF - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDARG_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP - -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - -/* Define to 1 if you have the `strerror' function. */ -#undef HAVE_STRERROR - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strncasecmp' function. */ -#undef HAVE_STRNCASECMP - -/* 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_CDEFS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PARAM_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `vasprintf' function. */ -#undef HAVE_VASPRINTF - -/* Define to 1 if you have the `vprintf' function. */ -#undef HAVE_VPRINTF - -/* Define to 1 if you have the `vsnprintf' function. */ -#undef HAVE_VSNPRINTF - -/* Define to 1 if you have the `vsyslog' function. */ -#undef HAVE_VSYSLOG - -/* Public define for json_inttypes.h */ -#undef JSON_C_HAVE_INTTYPES_H - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#undef LT_OBJDIR - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION - -/* 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/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c deleted file mode 100644 index 9dff7818bf86..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/debug.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * $Id: debug.c,v 1.5 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. - * - */ - -#include "config.h" - -#include -#include -#include -#include - -#if HAVE_SYSLOG_H -# include -#endif /* HAVE_SYSLOG_H */ - -#if HAVE_UNISTD_H -# include -#endif /* HAVE_UNISTD_H */ - -#if HAVE_SYS_PARAM_H -#include -#endif /* HAVE_SYS_PARAM_H */ - -#include "debug.h" - -static int _syslog = 0; -static int _debug = 0; - -void mc_set_debug(int debug) { _debug = debug; } -int mc_get_debug(void) { return _debug; } - -extern void mc_set_syslog(int syslog) -{ - _syslog = syslog; -} - -void mc_debug(const char *msg, ...) -{ - va_list ap; - if(_debug) { - va_start(ap, msg); -#if HAVE_VSYSLOG - if(_syslog) { - vsyslog(LOG_DEBUG, msg, ap); - } else -#endif - vprintf(msg, ap); - va_end(ap); - } -} - -void mc_error(const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); -#if HAVE_VSYSLOG - if(_syslog) { - vsyslog(LOG_ERR, msg, ap); - } else -#endif - vfprintf(stderr, msg, ap); - va_end(ap); -} - -void mc_info(const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); -#if HAVE_VSYSLOG - if(_syslog) { - vsyslog(LOG_INFO, msg, ap); - } else -#endif - vfprintf(stderr, msg, ap); - va_end(ap); -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h b/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h deleted file mode 100644 index 80ca3e43042e..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/debug.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * $Id: debug.h,v 1.5 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 _DEBUG_H_ -#define _DEBUG_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern void mc_set_debug(int debug); -extern int mc_get_debug(void); - -extern void mc_set_syslog(int syslog); - -extern void mc_debug(const char *msg, ...); -extern void mc_error(const char *msg, ...); -extern void mc_info(const char *msg, ...); - -#ifndef __STRING -#define __STRING(x) #x -#endif - -#ifndef PARSER_BROKEN_FIXED - -#define JASSERT(cond) do {} while(0) - -#else - -#define JASSERT(cond) do { \ - if (!(cond)) { \ - mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ - *(int *)0 = 1;\ - abort(); \ - }\ - } while(0) - -#endif - -#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) - -#ifdef MC_MAINTAINER_MODE -#define MC_SET_DEBUG(x) mc_set_debug(x) -#define MC_GET_DEBUG() mc_get_debug() -#define MC_SET_SYSLOG(x) mc_set_syslog(x) -#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) -#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) -#else -#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) -#define MC_GET_DEBUG() (0) -#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) -#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) -#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json.h deleted file mode 100644 index 4339b20e9060..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * $Id: json.h,v 1.6 2006/01/26 02:16:28 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_h_ -#define _json_h_ - -#ifdef __cplusplus -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_tokener.h" -#include "json_object_iterator.h" -#include "json_c_version.h" - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c deleted file mode 100644 index 13eb18855423..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.c +++ /dev/null @@ -1,20 +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. - */ -#include "config.h" - -#include "json_c_version.h" - -const char *json_c_version(void) -{ - return JSON_C_VERSION; -} - -int json_c_version_num(void) -{ - return JSON_C_VERSION_NUM; -} - diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_c_version.h deleted file mode 100644 index eed98a497501..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h deleted file mode 100644 index 405fda20dc5b..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_config.h +++ /dev/null @@ -1,3 +0,0 @@ - -/* Define to 1 if you have the header file. */ -#define JSON_C_HAVE_INTTYPES_H 1 diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_inttypes.h deleted file mode 100644 index 9de8d246d9dd..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.c deleted file mode 100644 index 57f3f0d237d5..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object.h deleted file mode 100644 index c649ab7c7bfb..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c deleted file mode 100644 index 7066649c3bc5..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.c +++ /dev/null @@ -1,168 +0,0 @@ -/** -******************************************************************************* -* @file json_object_iterator.c -* -* Copyright (c) 2009-2012 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. -* -* @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. -* -******************************************************************************* -*/ - -#include - -#include "json.h" -#include "json_object_private.h" - -#include "json_object_iterator.h" - -/** - * How It Works - * - * For each JSON Object, json-c maintains a linked list of zero - * or more lh_entry (link-hash entry) structures inside the - * Object's link-hash table (lh_table). - * - * Each lh_entry structure on the JSON Object's linked list - * represents a single name/value pair. The "next" field of the - * last lh_entry in the list is set to NULL, which terminates - * the list. - * - * We represent a valid iterator that refers to an actual - * name/value pair via a pointer to the pair's lh_entry - * structure set as the iterator's opaque_ field. - * - * We follow json-c's current pair list representation by - * representing a valid "end" iterator (one that refers past the - * last pair) with a NULL value in the iterator's opaque_ field. - * - * A JSON Object without any pairs in it will have the "head" - * field of its lh_table structure set to NULL. For such an - * object, json_object_iter_begin will return an iterator with - * the opaque_ field set to NULL, which is equivalent to the - * "end" iterator. - * - * When iterating, we simply update the iterator's opaque_ field - * to point to the next lh_entry structure in the linked list. - * opaque_ will become NULL once we iterate past the last pair - * in the list, which makes the iterator equivalent to the "end" - * iterator. - */ - -/// Our current representation of the "end" iterator; -/// -/// @note May not always be NULL -static const void* kObjectEndIterValue = NULL; - -/** - * **************************************************************************** - */ -struct json_object_iterator -json_object_iter_begin(struct json_object* obj) -{ - struct json_object_iterator iter; - struct lh_table* pTable; - - /// @note json_object_get_object will return NULL if passed NULL - /// or a non-json_type_object instance - pTable = json_object_get_object(obj); - JASSERT(NULL != pTable); - - /// @note For a pair-less Object, head is NULL, which matches our - /// definition of the "end" iterator - iter.opaque_ = pTable->head; - return iter; -} - -/** - * **************************************************************************** - */ -struct json_object_iterator -json_object_iter_end(const struct json_object* obj) -{ - struct json_object_iterator iter; - - JASSERT(NULL != obj); - JASSERT(json_object_is_type(obj, json_type_object)); - - iter.opaque_ = kObjectEndIterValue; - - return iter; -} - -/** - * **************************************************************************** - */ -void -json_object_iter_next(struct json_object_iterator* iter) -{ - JASSERT(NULL != iter); - JASSERT(kObjectEndIterValue != iter->opaque_); - - iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; -} - - -/** - * **************************************************************************** - */ -const char* -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); -} - - -/** - * **************************************************************************** - */ -struct json_object* -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); -} - - -/** - * **************************************************************************** - */ -json_bool -json_object_iter_equal(const struct json_object_iterator* iter1, - const struct json_object_iterator* iter2) -{ - JASSERT(NULL != iter1); - JASSERT(NULL != iter2); - - return (iter1->opaque_ == iter2->opaque_); -} - - -/** - * **************************************************************************** - */ -struct json_object_iterator -json_object_iter_init_default(void) -{ - struct json_object_iterator iter; - - /** - * @note Make this a negative, invalid value, such that - * accidental access to it would likely be trapped by the - * hardware as an invalid address. - */ - iter.opaque_ = NULL; - - return iter; -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h deleted file mode 100644 index 44c9fb25b6ca..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_iterator.h +++ /dev/null @@ -1,239 +0,0 @@ -/** -******************************************************************************* -* @file json_object_iterator.h -* -* Copyright (c) 2009-2012 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. -* -* @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. -* -* API attributes:
    -* * Thread-safe: NO
    -* * Reentrant: NO -* -******************************************************************************* -*/ - - -#ifndef JSON_OBJECT_ITERATOR_H -#define JSON_OBJECT_ITERATOR_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Forward declaration for the opaque iterator information. - */ -struct json_object_iter_info_; - -/** - * The opaque iterator that references a name/value pair within - * a JSON Object instance or the "end" iterator value. - */ -struct json_object_iterator { - const void* opaque_; -}; - - -/** - * forward declaration of json-c's JSON value instance structure - */ -struct json_object; - - -/** - * Initializes an iterator structure to a "default" value that - * is convenient for initializing an iterator variable to a - * default state (e.g., initialization list in a class' - * constructor). - * - * @code - * struct json_object_iterator iter = json_object_iter_init_default(); - * MyClass() : iter_(json_object_iter_init_default()) - * @endcode - * - * @note The initialized value doesn't reference any specific - * pair, is considered an invalid iterator, and MUST NOT - * be passed to any json-c API that expects a valid - * iterator. - * - * @note User and internal code MUST NOT make any assumptions - * about and dependencies on the value of the "default" - * iterator value. - * - * @return json_object_iterator - */ -struct json_object_iterator -json_object_iter_init_default(void); - -/** Retrieves an iterator to the first pair of the JSON Object. - * - * @warning Any modification of the underlying pair invalidates all - * iterators to that pair. - * - * @param obj JSON Object instance (MUST be of type json_object) - * - * @return json_object_iterator If the JSON Object has at - * least one pair, on return, the iterator refers - * to the first pair. If the JSON Object doesn't - * have any pairs, the returned iterator is - * equivalent to the "end" iterator for the same - * JSON Object instance. - * - * @code - * struct json_object_iterator it; - * struct json_object_iterator itEnd; - * struct json_object* obj; - * - * obj = json_tokener_parse("{'first':'george', 'age':100}"); - * it = json_object_iter_begin(obj); - * itEnd = json_object_iter_end(obj); - * - * while (!json_object_iter_equal(&it, &itEnd)) { - * printf("%s\n", - * json_object_iter_peek_name(&it)); - * json_object_iter_next(&it); - * } - * - * @endcode - */ -struct json_object_iterator -json_object_iter_begin(struct json_object* obj); - -/** Retrieves the iterator that represents the position beyond the - * last pair of the given JSON Object instance. - * - * @warning Do NOT write code that assumes that the "end" - * iterator value is NULL, even if it is so in a - * particular instance of the implementation. - * - * @note The reason we do not (and MUST NOT) provide - * "json_object_iter_is_end(json_object_iterator* iter)" - * type of API is because it would limit the underlying - * representation of name/value containment (or force us - * to add additional, otherwise unnecessary, fields to - * the iterator structure). The "end" iterator and the - * equality test method, on the other hand, permit us to - * cleanly abstract pretty much any reasonable underlying - * representation without burdening the iterator - * structure with unnecessary data. - * - * @note For performance reasons, memorize the "end" iterator prior - * to any loop. - * - * @param obj JSON Object instance (MUST be of type json_object) - * - * @return json_object_iterator On return, the iterator refers - * to the "end" of the Object instance's pairs - * (i.e., NOT the last pair, but "beyond the last - * pair" value) - */ -struct json_object_iterator -json_object_iter_end(const struct json_object* obj); - -/** Returns an iterator to the next pair, if any - * - * @warning Any modification of the underlying pair - * invalidates all iterators to that pair. - * - * @param iter [IN/OUT] Pointer to iterator that references a - * name/value pair; MUST be a valid, non-end iterator. - * WARNING: bad things will happen if invalid or "end" - * iterator is passed. Upon return will contain the - * reference to the next pair if there is one; if there - * are no more pairs, will contain the "end" iterator - * value, which may be compared against the return value - * of json_object_iter_end() for the same JSON Object - * instance. - */ -void -json_object_iter_next(struct json_object_iterator* iter); - - -/** Returns a const pointer to the name of the pair referenced - * by the given iterator. - * - * @param iter pointer to iterator that references a name/value - * pair; MUST be a valid, non-end iterator. - * - * @warning bad things will happen if an invalid or - * "end" iterator is passed. - * - * @return const char* Pointer to the name of the referenced - * name/value pair. The name memory belongs to the - * name/value pair, will be freed when the pair is - * deleted or modified, and MUST NOT be modified or - * freed by the user. - */ -const char* -json_object_iter_peek_name(const struct json_object_iterator* iter); - - -/** Returns a pointer to the json-c instance representing the - * value of the referenced name/value pair, without altering - * the instance's reference count. - * - * @param iter pointer to iterator that references a name/value - * pair; MUST be a valid, non-end iterator. - * - * @warning bad things will happen if invalid or - * "end" iterator is passed. - * - * @return struct json_object* Pointer to the json-c value - * instance of the referenced name/value pair; the - * value's reference count is not changed by this - * function: if you plan to hold on to this json-c node, - * take a look at json_object_get() and - * json_object_put(). IMPORTANT: json-c API represents - * the JSON Null value as a NULL json_object instance - * pointer. - */ -struct json_object* -json_object_iter_peek_value(const struct json_object_iterator* iter); - - -/** Tests two iterators for equality. Typically used to test - * for end of iteration by comparing an iterator to the - * corresponding "end" iterator (that was derived from the same - * JSON Object instance). - * - * @note The reason we do not (and MUST NOT) provide - * "json_object_iter_is_end(json_object_iterator* iter)" - * type of API is because it would limit the underlying - * representation of name/value containment (or force us - * to add additional, otherwise unnecessary, fields to - * the iterator structure). The equality test method, on - * the other hand, permits us to cleanly abstract pretty - * much any reasonable underlying representation. - * - * @param iter1 Pointer to first valid, non-NULL iterator - * @param iter2 POinter to second valid, non-NULL iterator - * - * @warning if a NULL iterator pointer or an uninitialized - * or invalid iterator, or iterators derived from - * different JSON Object instances are passed, bad things - * will happen! - * - * @return json_bool non-zero if iterators are equal (i.e., both - * reference the same name/value pair or are both at - * "end"); zero if they are not equal. - */ -json_bool -json_object_iter_equal(const struct json_object_iterator* iter1, - const struct json_object_iterator* iter2); - - -#ifdef __cplusplus -} -#endif - - -#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h deleted file mode 100644 index deff7e8df48f..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_object_private.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * $Id: json_object_private.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. - * - */ - -#ifndef _json_object_private_h_ -#define _json_object_private_h_ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (json_object_private_delete_fn)(struct json_object *o); - -struct json_object -{ - enum json_type o_type; - json_object_private_delete_fn *_delete; - json_object_to_json_string_fn *_to_json_string; - int _ref_count; - struct printbuf *_pb; - union data { - json_bool c_boolean; - double c_double; - int64_t c_int64; - struct lh_table *c_object; - struct array_list *c_array; - struct { - char *str; - size_t len; - } c_string; - } o; - json_object_delete_fn *_user_delete; - void *_userdata; -}; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c deleted file mode 100644 index 2959cbd6aea2..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 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) - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "bits.h" -#include "debug.h" -#include "printbuf.h" -#include "arraylist.h" -#include "json_inttypes.h" -#include "json_object.h" -#include "json_tokener.h" -#include "json_util.h" - -#ifdef HAVE_LOCALE_H -#include -#endif /* HAVE_LOCALE_H */ - -#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 */ - -#if !HAVE_STRNCASECMP && defined(_MSC_VER) - /* MSC has the version as _strnicmp */ -# define strncasecmp _strnicmp -#elif !HAVE_STRNCASECMP -# error You do not have strncasecmp on your system. -#endif /* HAVE_STRNCASECMP */ - -/* Use C99 NAN by default; if not available, nan("") should work too. */ -#ifndef NAN -#define NAN nan("") -#endif /* !NAN */ - -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_nan_str[] = "NaN"; -static const int json_nan_str_len = sizeof(json_nan_str) - 1; -static const char json_true_str[] = "true"; -static const int json_true_str_len = sizeof(json_true_str) - 1; -static const char json_false_str[] = "false"; -static const int json_false_str_len = sizeof(json_false_str) - 1; - -static const char* json_tokener_errors[] = { - "success", - "continue", - "nesting too deep", - "unexpected end of data", - "unexpected character", - "null expected", - "boolean expected", - "number expected", - "array value separator ',' expected", - "quoted object property name expected", - "object property name separator ':' expected", - "object value separator ',' expected", - "invalid string sequence", - "expected comment", - "buffer size overflow" -}; - -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()"; - return json_tokener_errors[jerr]; -} - -enum json_tokener_error json_tokener_get_error(json_tokener *tok) -{ - return tok->err; -} - -/* Stuff for decoding unicode sequences */ -#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) -#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) -#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) -static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; - -struct json_tokener* json_tokener_new_ex(int depth) -{ - struct json_tokener *tok; - - 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)); - if (!tok->stack) { - free(tok); - return NULL; - } - tok->pb = printbuf_new(); - tok->max_depth = depth; - json_tokener_reset(tok); - return tok; -} - -struct json_tokener* json_tokener_new(void) -{ - return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); -} - -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); -} - -static void json_tokener_reset_level(struct json_tokener *tok, int depth) -{ - tok->stack[depth].state = json_tokener_state_eatws; - tok->stack[depth].saved_state = json_tokener_state_start; - json_object_put(tok->stack[depth].current); - tok->stack[depth].current = NULL; - free(tok->stack[depth].obj_field_name); - tok->stack[depth].obj_field_name = NULL; -} - -void json_tokener_reset(struct json_tokener *tok) -{ - int i; - if (!tok) - return; - - for(i = tok->depth; i >= 0; i--) - json_tokener_reset_level(tok, i); - tok->depth = 0; - tok->err = json_tokener_success; -} - -struct json_object* json_tokener_parse(const char *str) -{ - enum json_tokener_error jerr_ignored; - struct json_object* obj; - obj = json_tokener_parse_verbose(str, &jerr_ignored); - return obj; -} - -struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) -{ - struct json_tokener* tok; - struct json_object* obj; - - tok = json_tokener_new(); - if (!tok) - return NULL; - obj = json_tokener_parse_ex(tok, str, -1); - *error = tok->err; - if(tok->err != json_tokener_success) { - if (obj != NULL) - json_object_put(obj); - obj = NULL; - } - - json_tokener_free(tok); - return obj; -} - -#define state tok->stack[tok->depth].state -#define saved_state tok->stack[tok->depth].saved_state -#define current tok->stack[tok->depth].current -#define obj_field_name tok->stack[tok->depth].obj_field_name - -/* Optimization: - * json_tokener_parse_ex() consumed a lot of CPU in its main loop, - * iterating character-by character. A large performance boost is - * achieved by using tighter loops to locally handle units such as - * comments and strings. Loops that handle an entire token within - * their scope also gather entire strings and pass them to - * printbuf_memappend() in a single call, rather than calling - * printbuf_memappend() one char at a time. - * - * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is - * common to both the main loop and the tighter loops. - */ - -/* PEEK_CHAR(dest, tok) macro: - * Peeks at the current char and stores it in dest. - * 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) \ - ) - -/* ADVANCE_CHAR() macro: - * Incrementes str & tok->char_offset. - * For convenience of existing conditionals, returns the old value of c (0 on eof) - * Implicit inputs: c var - */ -#define ADVANCE_CHAR(str, tok) \ - ( ++(str), ((tok)->char_offset)++, c) - - -/* End optimization macro defs */ - - -struct json_object* json_tokener_parse_ex(struct json_tokener *tok, - const char *str, int len) -{ - 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"); -#endif - - tok->char_offset = 0; - tok->err = json_tokener_success; - - /* this interface is presently not 64-bit clean due to the int len argument - and the internal printbuf interface that takes 32-bit int len arguments - so the function limits the maximum string size to INT32_MAX (2GB). - If the function is called with len == -1 then strlen is called to check - the string length is less than INT32_MAX (2GB) */ - if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { - tok->err = json_tokener_error_size; - return NULL; - } - - while (PEEK_CHAR(c, tok)) { - - redo_char: - switch(state) { - - case json_tokener_state_eatws: - /* Advance until we change state */ - while (isspace((int)c)) { - if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) - goto out; - } - if(c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) { - printbuf_reset(tok->pb); - printbuf_memappend_fast(tok->pb, &c, 1); - state = json_tokener_state_comment_start; - } else { - state = saved_state; - goto redo_char; - } - break; - - case json_tokener_state_start: - switch(c) { - case '{': - state = json_tokener_state_eatws; - saved_state = json_tokener_state_object_field_start; - current = json_object_new_object(); - break; - case '[': - state = json_tokener_state_eatws; - saved_state = json_tokener_state_array; - current = json_object_new_array(); - break; - case 'I': - case 'i': - state = json_tokener_state_inf; - printbuf_reset(tok->pb); - tok->st_pos = 0; - goto redo_char; - case 'N': - case 'n': - state = json_tokener_state_null; // or NaN - printbuf_reset(tok->pb); - tok->st_pos = 0; - goto redo_char; - case '\'': - if (tok->flags & JSON_TOKENER_STRICT) { - /* in STRICT mode only double-quote are allowed */ - tok->err = json_tokener_error_parse_unexpected; - goto out; - } - case '"': - state = json_tokener_state_string; - printbuf_reset(tok->pb); - tok->quote_char = c; - break; - case 'T': - case 't': - case 'F': - case 'f': - state = json_tokener_state_boolean; - printbuf_reset(tok->pb); - tok->st_pos = 0; - goto redo_char; -#if defined(__GNUC__) - case '0' ... '9': -#else - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': -#endif - case '-': - state = json_tokener_state_number; - printbuf_reset(tok->pb); - tok->is_double = 0; - goto redo_char; - default: - tok->err = json_tokener_error_parse_unexpected; - goto out; - } - break; - - case json_tokener_state_finish: - if(tok->depth == 0) goto out; - obj = json_object_get(current); - json_tokener_reset_level(tok, tok->depth); - tok->depth--; - goto redo_char; - - case json_tokener_state_inf: /* aka starts with 'i' */ - { - int size; - int size_inf; - int is_negative = 0; - - 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 == '-') - { - 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) - { - current = json_object_new_double(is_negative ? -INFINITY : INFINITY); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } else { - tok->err = json_tokener_error_parse_unexpected; - goto out; - } - tok->st_pos++; - } - break; - case json_tokener_state_null: /* aka starts with 'n' */ - { - int size; - int size_nan; - printbuf_memappend_fast(tok->pb, &c, 1); - size = json_min(tok->st_pos+1, json_null_str_len); - size_nan = json_min(tok->st_pos+1, json_nan_str_len); - if((!(tok->flags & JSON_TOKENER_STRICT) && - strncasecmp(json_null_str, tok->pb->buf, size) == 0) - || (strncmp(json_null_str, tok->pb->buf, size) == 0) - ) { - if (tok->st_pos == json_null_str_len) { - current = NULL; - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } - else if ((!(tok->flags & JSON_TOKENER_STRICT) && - strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || - (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0) - ) - { - if (tok->st_pos == json_nan_str_len) - { - current = json_object_new_double(NAN); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } else { - tok->err = json_tokener_error_parse_null; - goto out; - } - tok->st_pos++; - } - break; - - case json_tokener_state_comment_start: - if(c == '*') { - state = json_tokener_state_comment; - } else if(c == '/') { - state = json_tokener_state_comment_eol; - } else { - tok->err = json_tokener_error_parse_comment; - goto out; - } - printbuf_memappend_fast(tok->pb, &c, 1); - break; - - case json_tokener_state_comment: - { - /* Advance until we change state */ - const char *case_start = str; - while(c != '*') { - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - goto out; - } - } - printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); - state = json_tokener_state_comment_end; - } - break; - - case json_tokener_state_comment_eol: - { - /* Advance until we change state */ - const char *case_start = str; - while(c != '\n') { - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - goto out; - } - } - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); - state = json_tokener_state_eatws; - } - break; - - case json_tokener_state_comment_end: - printbuf_memappend_fast(tok->pb, &c, 1); - if(c == '/') { - MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); - state = json_tokener_state_eatws; - } else { - state = json_tokener_state_comment; - } - break; - - case json_tokener_state_string: - { - /* Advance until we change state */ - const char *case_start = str; - 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); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - break; - } else if(c == '\\') { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - saved_state = json_tokener_state_string; - state = json_tokener_state_string_escape; - break; - } - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - goto out; - } - } - } - break; - - case json_tokener_state_string_escape: - switch(c) { - case '"': - case '\\': - case '/': - printbuf_memappend_fast(tok->pb, &c, 1); - state = saved_state; - break; - case 'b': - case 'n': - case 'r': - case 't': - case 'f': - if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); - else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); - else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); - else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); - else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); - state = saved_state; - break; - case 'u': - tok->ucs_char = 0; - tok->st_pos = 0; - state = json_tokener_state_escape_unicode; - break; - default: - tok->err = json_tokener_error_parse_string; - goto out; - } - break; - - case json_tokener_state_escape_unicode: - { - unsigned int got_hi_surrogate = 0; - - /* 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(tok->st_pos == 4) { - unsigned char unescaped_utf[4]; - - if (got_hi_surrogate) { - if (IS_LOW_SURROGATE(tok->ucs_char)) { - /* Recalculate the ucs_char, then fall thru to process normally */ - tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); - } else { - /* Hi surrogate was not followed by a low surrogate */ - /* Replace the hi and process the rest normally */ - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); - } - got_hi_surrogate = 0; - } - - if (tok->ucs_char < 0x80) { - unescaped_utf[0] = tok->ucs_char; - printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); - } else if (tok->ucs_char < 0x800) { - unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); - unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); - printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); - } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { - /* Got a high surrogate. Remember it and look for the - * the beginning of another sequence, which should be the - * low surrogate. - */ - 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) && - (str[1] == '\\') && - (str[2] == 'u')) - { - /* Advance through the 16 bit surrogate, and move on to the - * next sequence. The next step is to process the following - * characters. - */ - if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { - 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); - goto out; - } - tok->ucs_char = 0; - tok->st_pos = 0; - continue; /* other json_tokener_state_escape_unicode */ - } else { - /* Got a high surrogate without another sequence following - * it. Put a replacement char in for the hi surrogate - * and pretend we finished. - */ - 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 */ - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); - } else if (tok->ucs_char < 0x10000) { - unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); - unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); - unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); - printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); - } else if (tok->ucs_char < 0x110000) { - unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); - unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); - unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); - unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); - printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); - } else { - /* Don't know what we got--insert the replacement char */ - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); - } - state = saved_state; - break; - } - } else { - tok->err = json_tokener_error_parse_string; - goto out; - } - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - if (got_hi_surrogate) /* Clean up any pending chars */ - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); - goto out; - } - } - } - break; - - case json_tokener_state_boolean: - { - int size1, size2; - printbuf_memappend_fast(tok->pb, &c, 1); - size1 = json_min(tok->st_pos+1, json_true_str_len); - size2 = json_min(tok->st_pos+1, json_false_str_len); - if((!(tok->flags & JSON_TOKENER_STRICT) && - strncasecmp(json_true_str, tok->pb->buf, size1) == 0) - || (strncmp(json_true_str, tok->pb->buf, size1) == 0) - ) { - if(tok->st_pos == json_true_str_len) { - current = json_object_new_boolean(1); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } else if((!(tok->flags & JSON_TOKENER_STRICT) && - strncasecmp(json_false_str, tok->pb->buf, size2) == 0) - || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { - if(tok->st_pos == json_false_str_len) { - current = json_object_new_boolean(0); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } else { - tok->err = json_tokener_error_parse_boolean; - goto out; - } - tok->st_pos++; - } - break; - - case json_tokener_state_number: - { - /* Advance until we change state */ - const char *case_start = str; - int case_len=0; - while(c && strchr(json_number_chars, c)) { - ++case_len; - if(c == '.' || c == 'e' || c == 'E') - tok->is_double = 1; - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, case_start, case_len); - goto out; - } - } - if (case_len>0) - printbuf_memappend_fast(tok->pb, case_start, case_len); - - // Check for -Infinity - if (tok->pb->buf[0] == '-' && case_len == 1 && - (c == 'i' || c == 'I')) - { - state = json_tokener_state_inf; - goto redo_char; - } - } - { - 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)) { - /* in strict mode, number must not start with 0 */ - tok->err = json_tokener_error_parse_number; - goto out; - } - current = json_object_new_int64(num64); - } - else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) - { - current = json_object_new_double_s(numd, tok->pb->buf); - } else { - tok->err = json_tokener_error_parse_number; - goto out; - } - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - break; - - 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; - } - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - } else { - if(tok->depth >= tok->max_depth-1) { - tok->err = json_tokener_error_depth; - goto out; - } - state = json_tokener_state_array_add; - tok->depth++; - json_tokener_reset_level(tok, tok->depth); - goto redo_char; - } - break; - - case json_tokener_state_array_add: - json_object_array_add(current, obj); - saved_state = json_tokener_state_array_sep; - state = json_tokener_state_eatws; - goto redo_char; - - case json_tokener_state_array_sep: - if(c == ']') { - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - } else if(c == ',') { - saved_state = json_tokener_state_array_after_sep; - state = json_tokener_state_eatws; - } else { - tok->err = json_tokener_error_parse_array; - goto out; - } - break; - - case json_tokener_state_object_field_start: - case json_tokener_state_object_field_start_after_sep: - if(c == '}') { - if (state == json_tokener_state_object_field_start_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 if (c == '"' || c == '\'') { - tok->quote_char = c; - printbuf_reset(tok->pb); - state = json_tokener_state_object_field; - } else { - tok->err = json_tokener_error_parse_object_key_name; - goto out; - } - break; - - case json_tokener_state_object_field: - { - /* Advance until we change state */ - const char *case_start = str; - while(1) { - if(c == tok->quote_char) { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - obj_field_name = strdup(tok->pb->buf); - saved_state = json_tokener_state_object_field_end; - state = json_tokener_state_eatws; - break; - } else if(c == '\\') { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - saved_state = json_tokener_state_object_field; - state = json_tokener_state_string_escape; - break; - } - if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, case_start, str-case_start); - goto out; - } - } - } - break; - - case json_tokener_state_object_field_end: - if(c == ':') { - saved_state = json_tokener_state_object_value; - state = json_tokener_state_eatws; - } else { - tok->err = json_tokener_error_parse_object_key_sep; - goto out; - } - break; - - case json_tokener_state_object_value: - if(tok->depth >= tok->max_depth-1) { - tok->err = json_tokener_error_depth; - goto out; - } - state = json_tokener_state_object_value_add; - tok->depth++; - json_tokener_reset_level(tok, tok->depth); - goto redo_char; - - case json_tokener_state_object_value_add: - json_object_object_add(current, obj_field_name, obj); - free(obj_field_name); - obj_field_name = NULL; - saved_state = json_tokener_state_object_sep; - state = json_tokener_state_eatws; - goto redo_char; - - case json_tokener_state_object_sep: - if(c == '}') { - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - } else if(c == ',') { - saved_state = json_tokener_state_object_field_start_after_sep; - state = json_tokener_state_eatws; - } else { - tok->err = json_tokener_error_parse_object_value_sep; - goto out; - } - break; - - } - if (!ADVANCE_CHAR(str, tok)) - goto out; - } /* while(POP_CHAR) */ - - out: - if (c && - (state == json_tokener_state_finish) && - (tok->depth == 0) && - (tok->flags & JSON_TOKENER_STRICT)) { - /* unexpected char after JSON data */ - tok->err = json_tokener_error_parse_unexpected; - } - if (!c) { /* We hit an eof char (0) */ - if(state != json_tokener_state_finish && - saved_state != json_tokener_state_finish) - tok->err = json_tokener_error_parse_eof; - } - -#ifdef HAVE_SETLOCALE - setlocale(LC_NUMERIC, oldlocale); - if (oldlocale) free(oldlocale); -#endif - - if (tok->err == json_tokener_success) - { - json_object *ret = json_object_get(current); - int ii; - - /* Partially reset, so we parse additional objects on subsequent calls. */ - for(ii = tok->depth; ii >= 0; ii--) - json_tokener_reset_level(tok, ii); - return ret; - } - - MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", - json_tokener_errors[tok->err], tok->char_offset); - return NULL; -} - -void json_tokener_set_flags(struct json_tokener *tok, int flags) -{ - tok->flags = flags; -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h deleted file mode 100644 index a72d2bdefe00..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/json_tokener.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 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_tokener_h_ -#define _json_tokener_h_ - -#include -#include "json_object.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum json_tokener_error { - json_tokener_success, - json_tokener_continue, - json_tokener_error_depth, - json_tokener_error_parse_eof, - json_tokener_error_parse_unexpected, - json_tokener_error_parse_null, - json_tokener_error_parse_boolean, - json_tokener_error_parse_number, - json_tokener_error_parse_array, - json_tokener_error_parse_object_key_name, - json_tokener_error_parse_object_key_sep, - json_tokener_error_parse_object_value_sep, - json_tokener_error_parse_string, - json_tokener_error_parse_comment, - json_tokener_error_size -}; - -enum json_tokener_state { - json_tokener_state_eatws, - json_tokener_state_start, - json_tokener_state_finish, - json_tokener_state_null, - json_tokener_state_comment_start, - json_tokener_state_comment, - json_tokener_state_comment_eol, - json_tokener_state_comment_end, - json_tokener_state_string, - json_tokener_state_string_escape, - json_tokener_state_escape_unicode, - json_tokener_state_boolean, - json_tokener_state_number, - json_tokener_state_array, - json_tokener_state_array_add, - json_tokener_state_array_sep, - json_tokener_state_object_field_start, - json_tokener_state_object_field, - json_tokener_state_object_field_end, - json_tokener_state_object_value, - json_tokener_state_object_value_add, - json_tokener_state_object_sep, - json_tokener_state_array_after_sep, - json_tokener_state_object_field_start_after_sep, - json_tokener_state_inf -}; - -struct json_tokener_srec -{ - enum json_tokener_state state, saved_state; - struct json_object *obj; - struct json_object *current; - char *obj_field_name; -}; - -#define JSON_TOKENER_DEFAULT_DEPTH 32 - -struct json_tokener -{ - char *str; - struct printbuf *pb; - int max_depth, depth, is_double, st_pos, char_offset; - enum json_tokener_error err; - unsigned int ucs_char; - char quote_char; - struct json_tokener_srec *stack; - int flags; -}; - -/** - * Be strict when parsing JSON input. Use caution with - * this flag as what is considered valid may become more - * restrictive from one release to the next, causing your - * code to fail on previously working input. - * - * This flag is not set by default. - * - * @see json_tokener_set_flags() - */ -#define JSON_TOKENER_STRICT 0x01 - -/** - * Given an error previously returned by json_tokener_get_error(), - * return a human readable description of the error. - * - * @return a generic error message is returned if an invalid error value is provided. - */ -const char *json_tokener_error_desc(enum json_tokener_error jerr); - -/** - * Retrieve the error caused by the last call to json_tokener_parse_ex(), - * or json_tokener_success if there is no error. - * - * When parsing a JSON string in pieces, if the tokener is in the middle - * of parsing this will return json_tokener_continue. - * - * See also json_tokener_error_desc(). - */ -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); - -/** - * Set flags that control how parsing will be done. - */ -extern 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 - * json_tokener_continue. - * json_tokener_parse_ex() can then be called with additional bytes in str - * to continue the parsing. - * - * If json_tokener_parse_ex() returns NULL and the error 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 - * called. - * - * When a valid JSON value is parsed, a non-NULL json_object will be - * returned. Also, json_tokener_get_error() will return json_tokener_success. - * Be sure to check the type with json_object_is_type() or - * 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 - * 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. - * - * Extra characters can be detected by comparing the tok->char_offset against - * the length of the last len parameter passed in. - * - * The tokener does \b not maintain an internal buffer so the caller is - * responsible for calling json_tokener_parse_ex with an appropriate str - * parameter starting with the extra characters. - * - * This interface is presently not 64-bit clean due to the int len argument - * so the function limits the maximum string size to INT32_MAX (2GB). - * If the function is called with len == -1 then strlen is called to check - * the string length is less than INT32_MAX (2GB) - * - * Example: - * @code -json_object *jobj = NULL; -const char *mystring = NULL; -int stringlen = 0; -enum json_tokener_error jerr; -do { - mystring = ... // get JSON string, e.g. read from file, etc... - stringlen = strlen(mystring); - jobj = json_tokener_parse_ex(tok, mystring, stringlen); -} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); -if (jerr != json_tokener_success) -{ - fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); - // Handle errors, as appropriate for your application. -} -if (tok->char_offset < stringlen) // XXX shouldn't access internal fields -{ - // Handle extra characters after parsed object as desired. - // e.g. issue an error, parse another object from that point, etc... -} -// Success, use jobj here. - -@endcode - * - * @param tok a json_tokener previously allocated with json_tokener_new() - * @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, - const char *str, int len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.c deleted file mode 100644 index fda5d1ec00a7..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h b/tools/CustomSetupTool/CustomSetupTool/json-c/json_util.h deleted file mode 100644 index 1005e58c5b0d..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c b/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c deleted file mode 100644 index 5284fd0e70b7..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/libjson.c +++ /dev/null @@ -1,26 +0,0 @@ - -/* dummy source file for compatibility purposes */ - -#if defined(HAVE_CDEFS_H) -#include -#endif - -#ifndef __warn_references - -#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) - -#define __warn_references(sym,msg) \ - __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); - -#else -#define __warn_references(sym,msg) /* nothing */ -#endif - -#endif - -#include "json_object.h" - -__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); - -/* __asm__(".section .gnu.warning." __STRING(sym) \ - " ; .ascii \"" msg "\" ; .text") */ diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c deleted file mode 100644 index 50de485638e7..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 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 -#include -#include -#include -#include -#include - -#include - -#ifdef HAVE_ENDIAN_H -# include /* attempt to define endianness */ -#endif - -#include "random_seed.h" -#include "linkhash.h" - -void lh_abort(const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); - vprintf(msg, ap); - va_end(ap); - exit(1); -} - -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); -} - -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 - * minor mofifications to compile with -Werror - */ - -/* -------------------------------------------------------------------------------- -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 -if SELF_TEST is defined. You can use this free for any purpose. It's in -the public domain. It has no warranty. - -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. -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 - a = i1; b = i2; c = i3; - mix(a,b,c); - a += i4; b += i5; c += i6; - mix(a,b,c); - a += i7; - final(a,b,c); -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(). - -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. -------------------------------------------------------------------------------- -*/ - -/* - * My best guess at if you are big-endian or little-endian. This may - * need adjustment. - */ -#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ - __BYTE_ORDER == __LITTLE_ENDIAN) || \ - (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) -# define HASH_LITTLE_ENDIAN 1 -# define HASH_BIG_ENDIAN 0 -#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ - __BYTE_ORDER == __BIG_ENDIAN) || \ - (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 1 -#else -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 0 -#endif - -#define hashsize(n) ((uint32_t)1<<(n)) -#define hashmask(n) (hashsize(n)-1) -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -/* -------------------------------------------------------------------------------- -mix -- mix 3 32-bit values reversibly. - -This is reversible, so any information in (a,b,c) before mix() is -still in (a,b,c) after mix(). - -If four pairs of (a,b,c) inputs are run through mix(), or through -mix() in reverse, there are at least 32 bits of the output that -are sometimes the same for one pair and different for another pair. -This was tested for: -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - 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 - all zero plus a counter that starts at zero. - -Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that -satisfy this are - 4 6 8 16 19 4 - 9 15 3 18 27 15 - 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 -the operations, constants, and arrangements of the variables. - -This does not achieve avalanche. There are input bits of (a,b,c) -that fail to affect some output bits of (a,b,c), especially of a. The -most thoroughly mixed value is c, but it doesn't really even achieve -avalanche in c. - -This allows some parallelism. Read-after-writes are good at doubling -the number of bits affected, so the goal of mixing pulls in the opposite -direction as the goal of parallelism. I did what I could. Rotates -seem to cost as much as shifts on every machine I could lay my hands -on, and rotates are much kinder to the top and bottom bits, so I used -rotates. -------------------------------------------------------------------------------- -*/ -#define mix(a,b,c) \ -{ \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c,16); c += b; \ - b -= a; b ^= rot(a,19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} - -/* -------------------------------------------------------------------------------- -final -- final mixing of 3 32-bit values (a,b,c) into c - -Pairs of (a,b,c) values differing in only a few bits will usually -produce values of c that look totally different. This was tested for -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - 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 - all zero plus a counter that starts at zero. - -These constants passed: - 14 11 25 16 4 14 24 - 12 14 25 16 4 14 24 -and these came close: - 4 8 15 26 3 22 24 - 10 8 15 26 3 22 24 - 11 8 15 26 3 22 24 -------------------------------------------------------------------------------- -*/ -#define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - - -/* -------------------------------------------------------------------------------- -hashlittle() -- hash a variable-length key into a 32-bit value - k : the key (the unaligned variable-length array of bytes) - length : the length of the key, counting by bytes - initval : can be any 4-byte value -Returns a 32-bit value. Every bit of the key affects every bit of -the return value. Two keys differing by one or two bits will have -totally different hash values. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 32 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. - -If you are hashing n strings (uint8_t **)k, do it like this: - for (i=0, h=0; i 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- 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 - * rest of the string. Every machine with memory protection I've seen - * 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). - */ -#ifndef VALGRIND - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : return c; /* zero length strings require no mixing */ - } - -#else /* make valgrind happy */ - - const uint8_t *k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ - case 1 : a+=k8[0]; break; - case 0 : return c; - } - -#endif /* !valgrind */ - - } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) - { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=k[0]; - break; - case 1 : a+=k8[0]; - break; - case 0 : return c; /* zero length requires no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- 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 1 : a+=k[0]; - break; - case 0 : return c; - } - } - - final(a,b,c); - return c; -} - -unsigned long lh_char_hash(const void *k) -{ - static volatile int random_seed = -1; - - if (random_seed == -1) { - int 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 - InterlockedCompareExchange(&random_seed, seed, -1); -#else -#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); -} - -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, - lh_entry_free_fn *free_fn, - lh_hash_fn *hash_fn, - lh_equal_fn *equal_fn) -{ - int i; - struct lh_table *t; - - t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); - if(!t) lh_abort("lh_table_new: calloc failed\n"); - 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"); - t->free_fn = free_fn; - t->hash_fn = hash_fn; - t->equal_fn = equal_fn; - for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; - return t; -} - -struct lh_table* lh_kchar_table_new(int size, const char *name, - lh_entry_free_fn *free_fn) -{ - return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); -} - -struct lh_table* lh_kptr_table_new(int size, const char *name, - lh_entry_free_fn *free_fn) -{ - return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); -} - -void 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; - } - 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); -} - -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) { - t->free_fn(c); - } - } - free(t->table); - free(t); -} - - -int lh_table_insert(struct lh_table *t, void *k, const void *v) -{ - unsigned long h, n; - - t->inserts++; - if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); - - 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].v = v; - t->count++; - - if(t->head == NULL) { - t->head = t->tail = &t->table[n]; - t->table[n].next = t->table[n].prev = NULL; - } else { - t->tail->next = &t->table[n]; - t->table[n].prev = t->tail; - t->table[n].next = NULL; - t->tail = &t->table[n]; - } - - return 0; -} - - -struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) -{ - 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 && - t->equal_fn(t->table[n].k, k)) return &t->table[n]; - if ((int)++n == t->size) n = 0; - count++; - } - return NULL; -} - - -const void* lh_table_lookup(struct lh_table *t, const void *k) -{ - void *result; - lh_table_lookup_ex(t, k, &result); - return result; -} - -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; - return TRUE; /* key found */ - } - if (v != NULL) *v = NULL; - return FALSE; /* key not found */ -} - -int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) -{ - ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ - - /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ - if(n < 0) { return -2; } - - if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; - t->count--; - if(t->free_fn) t->free_fn(e); - t->table[n].v = NULL; - t->table[n].k = LH_FREED; - if(t->tail == &t->table[n] && t->head == &t->table[n]) { - t->head = t->tail = NULL; - } else if (t->head == &t->table[n]) { - t->head->next->prev = NULL; - t->head = t->head->next; - } else if (t->tail == &t->table[n]) { - t->tail->prev->next = NULL; - t->tail = t->tail->prev; - } else { - t->table[n].prev->next = t->table[n].next; - t->table[n].next->prev = t->table[n].prev; - } - t->table[n].next = t->table[n].prev = NULL; - return 0; -} - - -int lh_table_delete(struct lh_table *t, const void *k) -{ - struct lh_entry *e = lh_table_lookup_entry(t, k); - if(!e) return -1; - return lh_table_delete_entry(t, e); -} - -int lh_table_length(struct lh_table *t) -{ - return t->count; -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h b/tools/CustomSetupTool/CustomSetupTool/json-c/linkhash.h deleted file mode 100644 index 950d09f35d70..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h b/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h deleted file mode 100644 index f40b8faf8fd4..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/math_compat.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __math_compat_h -#define __math_compat_h - -/* Define isnan and isinf on Windows/MSVC */ - -#ifndef HAVE_DECL_ISNAN -# ifdef HAVE_DECL__ISNAN -#include -#define isnan(x) _isnan(x) -# endif -#endif - -#ifndef HAVE_DECL_ISINF -# ifdef HAVE_DECL__FINITE -#include -#define isinf(x) (!_finite(x)) -# endif -#endif - -#ifndef HAVE_DECL_NAN -#error This platform does not have nan() -#endif - -#ifndef HAVE_DECL_INFINITY -#error This platform does not have INFINITY -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c deleted file mode 100644 index 94d41b0d6a5e..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * $Id: printbuf.c,v 1.5 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) - */ - -#include "config.h" - -#include -#include -#include - -#ifdef HAVE_STDARG_H -# include -#else /* !HAVE_STDARG_H */ -# error Not enough var arg support! -#endif /* HAVE_STDARG_H */ - -#include "bits.h" -#include "debug.h" -#include "printbuf.h" - -static int printbuf_extend(struct printbuf *p, size_t min_size); - -struct printbuf* printbuf_new(void) -{ - struct printbuf *p; - - p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); - if(!p) return NULL; - p->size = 32; - p->bpos = 0; - if(!(p->buf = (char*)malloc(p->size))) { - free(p); - return NULL; - } - return p; -} - - -/** - * Extend the buffer p so it has a size of at least min_size. - * - * If the current size is large enough, nothing is changed. - * - * Note: this does not check the available space! The caller - * is responsible for performing those calculations. - */ -static int printbuf_extend(struct printbuf *p, size_t min_size) -{ - char *t; - size_t new_size; - - if (p->size >= min_size) - return 0; - - new_size = json_max(p->size * 2, min_size + 8); -#ifdef PRINTBUF_DEBUG - MC_DEBUG("printbuf_memappend: realloc " - "bpos=%d min_size=%d old_size=%d new_size=%d\n", - p->bpos, min_size, p->size, new_size); -#endif /* PRINTBUF_DEBUG */ - if(!(t = (char*)realloc(p->buf, new_size))) - return -1; - p->size = new_size; - p->buf = t; - return 0; -} - -size_t 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) - return -1; - } - memcpy(p->buf + p->bpos, buf, size); - p->bpos += size; - p->buf[p->bpos]= '\0'; - return size; -} - -int printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t len) -{ - size_t size_needed; - - if (offset == -1) - offset = pb->bpos; - size_needed = offset + len; - if (pb->size < size_needed) - { - if (printbuf_extend(pb, size_needed) < 0) - return -1; - } - - memset(pb->buf + offset, charvalue, len); - if (pb->bpos < size_needed) - pb->bpos = size_needed; - - 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; - char *t; - int size; - char buf[128]; - - /* user stack buffer first */ - va_start(ap, msg); - size = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, 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 - if output is truncated whereas some return the number of bytes that - would have been written - this code handles both cases. */ - if(size == -1 || size > 127) { - va_start(ap, msg); - if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } - va_end(ap); - printbuf_memappend(p, t, size); - free(t); - return size; - } else { - printbuf_memappend(p, buf, size); - return size; - } -} - -void printbuf_reset(struct printbuf *p) -{ - p->buf[0] = '\0'; - p->bpos = 0; -} - -void printbuf_free(struct printbuf *p) -{ - if(p) { - free(p->buf); - free(p); - } -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h b/tools/CustomSetupTool/CustomSetupTool/json-c/printbuf.h deleted file mode 100644 index ed003b61f3c2..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/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/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c deleted file mode 100644 index ece374e5b74e..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * random_seed.c - * - * Copyright (c) 2013 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 "config.h" - -#define DEBUG_SEED(s) - - -#if defined ENABLE_RDRAND - -/* cpuid */ - -#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) -#define HAS_X86_CPUID 1 - -static void do_cpuid(int regs[], int h) -{ - __asm__ __volatile__( -#if defined __x86_64__ - "pushq %%rbx;\n" -#else - "pushl %%ebx;\n" -#endif - "cpuid;\n" -#if defined __x86_64__ - "popq %%rbx;\n" -#else - "popl %%ebx;\n" -#endif - : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) - : "a"(h)); -} - -#elif defined _MSC_VER - -#define HAS_X86_CPUID 1 -#define do_cpuid __cpuid - -#endif - -/* has_rdrand */ - -#if HAS_X86_CPUID - -static int has_rdrand() -{ - // CPUID.01H:ECX.RDRAND[bit 30] == 1 - int regs[4]; - do_cpuid(regs, 1); - return (regs[2] & (1 << 30)) != 0; -} - -#endif - -/* get_rdrand_seed - GCC x86 and X64 */ - -#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) - -#define HAVE_RDRAND 1 - -static int get_rdrand_seed() -{ - DEBUG_SEED("get_rdrand_seed"); - int _eax; - // rdrand eax - __asm__ __volatile__("1: .byte 0x0F\n" - " .byte 0xC7\n" - " .byte 0xF0\n" - " jnc 1b;\n" - : "=a" (_eax)); - return _eax; -} - -#endif - -#if defined _MSC_VER - -#if _MSC_VER >= 1700 -#define HAVE_RDRAND 1 - -/* get_rdrand_seed - Visual Studio 2012 and above */ - -static int get_rdrand_seed() -{ - DEBUG_SEED("get_rdrand_seed"); - int r; - while (_rdrand32_step(&r) == 0); - return r; -} - -#elif defined _M_IX86 -#define HAVE_RDRAND 1 - -/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ - -static int get_rdrand_seed() -{ - DEBUG_SEED("get_rdrand_seed"); - int _eax; -retry: - // rdrand eax - __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 - __asm jnc retry - __asm mov _eax, eax - return _eax; -} - -#endif -#endif - -#endif /* defined ENABLE_RDRAND */ - - -/* has_dev_urandom */ - -#if defined (__APPLE__) || defined(__unix__) || defined(__linux__) - -#include -#include -#include -#include -#include -#include - -#define HAVE_DEV_RANDOM 1 - -static const char *dev_random_file = "/dev/urandom"; - -static int has_dev_urandom() -{ - struct stat buf; - if (stat(dev_random_file, &buf)) { - return 0; - } - return ((buf.st_mode & S_IFCHR) != 0); -} - - -/* get_dev_random_seed */ - -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); - exit(1); - } - close(fd); - return r; -} - -#endif - - -/* get_cryptgenrandom_seed */ - -#ifdef _WIN32 - -#define HAVE_CRYPTGENRANDOM 1 - -#include - -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)) - { - fprintf(stderr, "error CryptAcquireContextW"); - exit(1); - } - - if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) - { - fprintf(stderr, "error CryptGenRandom"); - exit(1); - } - - CryptReleaseContext(hProvider, 0); - - return r; -} - -#endif - - -/* get_time_seed */ - -#include - -static int get_time_seed() -{ - DEBUG_SEED("get_time_seed"); - - return (int)time(NULL) * 433494437; -} - - -/* json_c_get_random_seed */ - -int json_c_get_random_seed() -{ -#if HAVE_RDRAND - if (has_rdrand()) return get_rdrand_seed(); -#endif -#if HAVE_DEV_RANDOM - if (has_dev_urandom()) return get_dev_random_seed(); -#endif -#if HAVE_CRYPTGENRANDOM - return get_cryptgenrandom_seed(); -#endif - return get_time_seed(); -} diff --git a/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h b/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h deleted file mode 100644 index 7362d67d9cd5..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/json-c/random_seed.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * random_seed.h - * - * Copyright (c) 2013 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 seed_h -#define seed_h - -#ifdef __cplusplus -extern "C" { -#endif - -extern int json_c_get_random_seed(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index e23626d0026c..39a98e1046af 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -185,30 +185,21 @@ BOOLEAN SetupCreateUninstallFile( if (RtlDoesFileExists_U(backupFilePath->Buffer)) { - GUID randomGuid; - PPH_STRING tempPath = NULL; - PPH_STRING guidString = NULL; - - tempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - GetTempPath((ULONG)tempPath->Length / sizeof(WCHAR), tempPath->Buffer); - - PhGenerateGuid(&randomGuid); - - if (guidString = PhFormatGuid(&randomGuid)) + if (!DeleteFile(backupFilePath->Buffer)) { - PhMoveReference(&guidString, PhSubstring(guidString, 1, guidString->Length / sizeof(WCHAR) - 2)); - } + PPH_STRING tempFileName; + PPH_STRING tempFilePath; - PhMoveReference(&guidString, PhConcatStrings( - 3, - PhGetString(tempPath), - L"\\", - PhGetString(guidString) - )); + tempFileName = PhCreateString(L"processhacker-setup.old"); + tempFilePath = PhGetCacheFileName(tempFileName); - if (!MoveFile(backupFilePath->Buffer, guidString->Buffer)) - { - Context->ErrorCode = GetLastError(); + if (!MoveFile(backupFilePath->Buffer, tempFilePath->Buffer)) + { + Context->ErrorCode = GetLastError(); + } + + PhDereferenceObject(tempFilePath); + PhDereferenceObject(tempFileName); } } @@ -239,59 +230,22 @@ VOID SetupDeleteUninstallFile( if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { - ULONG indexOfFileName = -1; - GUID randomGuid; - PPH_STRING setupTempPath = NULL; - PPH_STRING randomGuidString = NULL; - PPH_STRING fullSetupPath = NULL; - PPH_STRING tempFilePath = NULL; - - 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; + PPH_STRING tempFileName; + PPH_STRING tempFilePath; - // Generate random guid for our directory path. - PhGenerateGuid(&randomGuid); + tempFileName = PhCreateString(L"processhacker-setup.exe"); + tempFilePath = PhGetCacheFileName(tempFileName); - 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-setup.exe - // Example: C:\\Users\\dmex\\AppData\\Temp\\processhacker-setup.exe - tempFilePath = PhFormatString( - L"%s%s\\processhacker-setup.exe", - PhGetStringOrEmpty(setupTempPath), - PhGetStringOrEmpty(randomGuidString) - ); if (PhIsNullOrEmptyString(tempFilePath)) - goto CleanupExit; - - // Create the directory if it does not exist. - if (fullSetupPath = PhGetFullPath(PhGetString(tempFilePath), &indexOfFileName)) { - PPH_STRING directoryPath; - - if (indexOfFileName == -1) - goto CleanupExit; - - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } + PhDereferenceObject(tempFileName); + goto CleanupExit; } + + MoveFile(uninstallFilePath->Buffer, tempFilePath->Buffer); - MoveFile(uninstallFilePath->Buffer, fullSetupPath->Buffer); + PhDereferenceObject(tempFilePath); + PhDereferenceObject(tempFileName); } CleanupExit: diff --git a/tools/CustomSetupTool/CustomSetupTool/updatesetup.c b/tools/CustomSetupTool/CustomSetupTool/updatesetup.c deleted file mode 100644 index e892305e2f5a..000000000000 --- a/tools/CustomSetupTool/CustomSetupTool/updatesetup.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 - -NTSTATUS SetupUpdateWebSetupBuild( - _In_ PPH_SETUP_CONTEXT Context - ) -{ - return STATUS_SUCCESS; -} diff --git a/tools/CustomSetupTool/CustomSetupTool/websetuppage.c b/tools/CustomSetupTool/CustomSetupTool/websetuppage.c new file mode 100644 index 000000000000..30012b3fbef9 --- /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 = GetProp(GetParent(hwndDlg), L"SetupContext"); + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PPH_SETUP_CONTEXT)GetProp(hwndDlg, L"Context"); + } + + 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 From 0d5f77e91cf700b89d4d660f6828ba57f83f2202 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 10 Aug 2017 23:53:47 +1000 Subject: [PATCH 365/839] Setup: Fix default task manager option, Set mitigation options during update --- .../CustomSetupTool/CustomSetupTool.vcxproj | 2 +- .../CustomSetupTool.vcxproj.filters | 6 +- .../{installpage.c => install.c} | 0 tools/CustomSetupTool/CustomSetupTool/main.c | 189 +++++++++--------- tools/CustomSetupTool/CustomSetupTool/setup.c | 96 ++++++--- .../CustomSetupTool/CustomSetupTool/update.c | 3 + 6 files changed, 168 insertions(+), 128 deletions(-) rename tools/CustomSetupTool/CustomSetupTool/{installpage.c => install.c} (100%) diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index e6877e29be51..46933fea0f5e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -117,7 +117,7 @@ - + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index f27d961d9b0b..851ba8eb5075 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -69,15 +69,15 @@ Source Files\pages - - Source Files\pages - Source Files\pages Source Files + + Source Files\pages +
    diff --git a/tools/CustomSetupTool/CustomSetupTool/installpage.c b/tools/CustomSetupTool/CustomSetupTool/install.c similarity index 100% rename from tools/CustomSetupTool/CustomSetupTool/installpage.c rename to tools/CustomSetupTool/CustomSetupTool/install.c diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 80ab2834b87a..01f853af8ad7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -30,7 +30,7 @@ PH_COMMAND_LINE_OPTION options[] = { SETUP_COMMAND_REPAIR, L"repair", NoArgumentType }, }; -static NTSTATUS CreateSetupMutant( +NTSTATUS CreateSetupMutant( VOID ) { @@ -49,7 +49,7 @@ static NTSTATUS CreateSetupMutant( return NtCreateMutant(&MutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); } -static VOID SetupInitializeDpi( +VOID SetupInitializeDpi( VOID ) { @@ -62,7 +62,7 @@ static VOID SetupInitializeDpi( } } -static BOOLEAN NTAPI MainPropSheetCommandLineCallback( +BOOLEAN NTAPI MainPropSheetCommandLineCallback( _In_opt_ PPH_COMMAND_LINE_OPTION Option, _In_opt_ PPH_STRING Value, _In_opt_ PVOID Context @@ -121,6 +121,83 @@ INT CALLBACK MainPropSheet_Callback( return FALSE; } +VOID SetupShowInstallDialog( + VOID + ) +{ + PROPSHEETPAGE propSheetPage = { sizeof(PROPSHEETPAGE) }; + PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; + HPROPSHEETPAGE pages[6]; + + 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; + + // 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); + + // 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); +} + INT WINAPI wWinMain( _In_ HINSTANCE Instance, _In_opt_ HINSTANCE PrevInstance, @@ -128,6 +205,8 @@ INT WINAPI wWinMain( _In_ INT CmdShow ) { + PH_STRINGREF commandLine; + if (!NT_SUCCESS(CreateSetupMutant())) return 1; @@ -140,105 +219,27 @@ INT WINAPI wWinMain( PhGuiSupportInitialization(); SetupInitializeDpi(); + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + + if (!PhParseCommandLine( + &commandLine, + options, + ARRAYSIZE(options), + PH_COMMAND_LINE_IGNORE_FIRST_PART, + MainPropSheetCommandLineCallback, + NULL + )) { - PH_STRINGREF commandLine; - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - if (!PhParseCommandLine( - &commandLine, - options, - ARRAYSIZE(options), - PH_COMMAND_LINE_IGNORE_FIRST_PART, - MainPropSheetCommandLineCallback, - NULL - )) - { - return 1; - } + return 1; } -#ifdef _DEBUG - if (CheckProcessHackerInstalled()) - SetupMode = SETUP_COMMAND_UNINSTALL; -#endif + // DEBUG // if (CheckProcessHackerInstalled()) SetupMode = SETUP_COMMAND_UNINSTALL; switch (SetupMode) { case SETUP_COMMAND_INSTALL: default: - { - PROPSHEETPAGE propSheetPage = { sizeof(PROPSHEETPAGE) }; - PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; - HPROPSHEETPAGE pages[6]; - - 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; - - // 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); - - // 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); - } + SetupShowInstallDialog(); break; case SETUP_COMMAND_UNINSTALL: SetupShowUninstallDialog(); diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 39a98e1046af..aafdad667e6f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -263,6 +263,7 @@ VOID SetupStartKph( { HANDLE processHandle; LARGE_INTEGER timeout; + ULONG retries = 0; timeout.QuadPart = -10 * PH_TIMEOUT_SEC; @@ -279,6 +280,22 @@ VOID SetupStartKph( NtWaitForSingleObject(processHandle, FALSE, &timeout); NtClose(processHandle); } + + while (retries < 5) + { + SC_HANDLE serviceHandle; + SERVICE_STATUS serviceStatus; + + if (serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_START)) + { + StartService(serviceHandle, 0, NULL); + CloseServiceHandle(serviceHandle); + break; + } + + Sleep(1000); + retries++; + } } PhDereferenceObject(clientPath); @@ -288,38 +305,42 @@ BOOLEAN SetupUninstallKph( _In_ PPH_SETUP_CONTEXT Context ) { - while (TRUE) + ULONG retries = 0; + + while (retries < 5) { SC_HANDLE serviceHandle; SERVICE_STATUS serviceStatus; - if (!(serviceHandle = PhOpenService( - L"KProcessHacker2", - SERVICE_STOP | DELETE - ))) + if (serviceHandle = PhOpenService(L"KProcessHacker2", SERVICE_STOP | DELETE)) + { + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + DeleteService(serviceHandle); + CloseServiceHandle(serviceHandle); break; + } - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - DeleteService(serviceHandle); - - CloseServiceHandle(serviceHandle); + Sleep(1000); + retries++; } - while (TRUE) + retries = 0; + + while (retries < 5) { SC_HANDLE serviceHandle; SERVICE_STATUS serviceStatus; - if (!(serviceHandle = PhOpenService( - L"KProcessHacker3", - SERVICE_STOP | DELETE - ))) + if (serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_STOP | DELETE)) + { + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + DeleteService(serviceHandle); + CloseServiceHandle(serviceHandle); break; + } - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - DeleteService(serviceHandle); - - CloseServiceHandle(serviceHandle); + Sleep(1000); + retries++; } return TRUE; @@ -331,7 +352,6 @@ VOID SetupSetWindowsOptions( { 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"); - HANDLE keyHandle; PPH_STRING clientPathString; PPH_STRING startmenuFolderString; @@ -373,7 +393,7 @@ VOID SetupSetWindowsOptions( PhGetString(startmenuFolderString), PhGetString(peviewPathString), PhGetString(Context->SetupInstallPath) - ); + ); PhDereferenceObject(peviewPathString); PhDereferenceObject(startmenuFolderString); @@ -392,12 +412,17 @@ VOID SetupSetWindowsOptions( // Set the Windows default Task Manager. if (Context->SetupCreateDefaultTaskManager) { - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_WRITE, + NTSTATUS status; + HANDLE taskmgrKeyHandle = NULL; + + if (NT_SUCCESS(status = PhCreateKey( + &taskmgrKeyHandle, + KEY_READ | KEY_WRITE, PH_KEY_LOCAL_MACHINE, &TaskMgrImageOptionsKeyName, - 0 + 0, + 0, + NULL ))) { PPH_STRING value; @@ -407,17 +432,24 @@ VOID SetupSetWindowsOptions( // Configure the default Task Manager. RtlInitUnicodeString(&valueName, L"Debugger"); - NtSetValueKey(keyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); + NtSetValueKey(taskmgrKeyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); - NtClose(keyHandle); + NtClose(taskmgrKeyHandle); + } + else + { + PhShowStatus(NULL, L"Unable to set the Windows default Task Manager.", status, 0); } } // Create the run startup key. if (Context->SetupCreateSystemStartup) { - if (NT_SUCCESS(PhOpenKey( - &keyHandle, + NTSTATUS status; + HANDLE runKeyHandle = NULL; + + if (NT_SUCCESS(status = PhOpenKey( + &runKeyHandle, KEY_WRITE, PH_KEY_CURRENT_USER, &CurrentUserRunKeyName, @@ -434,8 +466,12 @@ VOID SetupSetWindowsOptions( 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); } } } diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 79a22c0e495e..6a44c237e06d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -47,6 +47,9 @@ NTSTATUS SetupUpdateBuild( if (!SetupExtractBuild(Context)) goto CleanupExit; + // Set the default image execution options. + SetupCreateImageFileExecutionOptions(); + SetupStartKph(Context); if (!SetupExecuteProcessHacker(Context)) From ba0b92b3d2f563fa196b88065acf75e4f369f03b Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 10 Aug 2017 23:54:20 +1000 Subject: [PATCH 366/839] CustomStartTool: Update to latest sdk --- tools/CustomStartTool/main.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/CustomStartTool/main.c b/tools/CustomStartTool/main.c index e967ae3f8434..a85e586a20f5 100644 --- a/tools/CustomStartTool/main.c +++ b/tools/CustomStartTool/main.c @@ -107,19 +107,28 @@ INT WINAPI wWinMain( PPH_STRING fileName; PPH_STRING currentDirectory; - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) + 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"); @@ -128,7 +137,7 @@ INT WINAPI wWinMain( if (!RtlDoesFileExists_U(fileName->Buffer) && CheckProcessHackerInstalled()) PhMoveReference(&fileName, GetProcessHackerPath()); - CreateProcess( + if (!CreateProcess( NULL, fileName->Buffer, NULL, @@ -139,7 +148,10 @@ INT WINAPI wWinMain( NULL, &info.StartupInfo, &processInfo - ); + )) + { + PhShowStatus(NULL, L"CreateProcess Error", PhGetLastWin32ErrorAsNtStatus(), 0); + } PhDereferenceObject(fileName); From 8c5112dc19caf782491b75726e8e163bc45be2de Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 00:37:20 +1000 Subject: [PATCH 367/839] SetupTool: Remove undocumented dependencies --- tools/CustomSetupTool/CustomSetupTool/main.c | 10 +++++++--- tools/CustomSetupTool/CustomSetupTool/setup.c | 18 +++++++++++++----- .../CustomSetupTool/uninstall.c | 4 ++-- tools/CustomSetupTool/CustomSetupTool/update.c | 4 ++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 01f853af8ad7..227c2cef77d4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -205,7 +205,7 @@ INT WINAPI wWinMain( _In_ INT CmdShow ) { - PH_STRINGREF commandLine; + PPH_STRING commandLine; if (!NT_SUCCESS(CreateSetupMutant())) return 1; @@ -219,10 +219,11 @@ INT WINAPI wWinMain( PhGuiSupportInitialization(); SetupInitializeDpi(); - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + if (!NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + return 1; if (!PhParseCommandLine( - &commandLine, + &commandLine->sr, options, ARRAYSIZE(options), PH_COMMAND_LINE_IGNORE_FIRST_PART, @@ -230,9 +231,12 @@ INT WINAPI wWinMain( NULL )) { + PhDereferenceObject(commandLine); return 1; } + PhDereferenceObject(commandLine); + // DEBUG // if (CheckProcessHackerInstalled()) SetupMode = SETUP_COMMAND_UNINSTALL; switch (SetupMode) diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index aafdad667e6f..1c8e6e5906f3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -170,15 +170,19 @@ BOOLEAN SetupCreateUninstallFile( _In_ PPH_SETUP_CONTEXT Context ) { - PH_STRINGREF currentFilePath; + PPH_STRING currentFilePath; PPH_STRING backupFilePath; PPH_STRING uninstallFilePath; - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->ImagePathName, ¤tFilePath); + if (!NT_SUCCESS(PhGetProcessImageFileNameWin32(NtCurrentProcess(), ¤tFilePath))) + return FALSE; // Check if the user has started the setup from the installation folder. - if (PhStartsWithStringRef2(¤tFilePath, PhGetString(Context->SetupInstallPath), TRUE)) + if (PhStartsWithStringRef2(¤tFilePath->sr, PhGetString(Context->SetupInstallPath), TRUE)) + { + PhDereferenceObject(currentFilePath); return TRUE; + } backupFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.bak"); uninstallFilePath = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\processhacker-setup.exe"); @@ -211,12 +215,14 @@ BOOLEAN SetupCreateUninstallFile( } } - if (!CopyFile(currentFilePath.Buffer, uninstallFilePath->Buffer, TRUE)) + if (!CopyFile(currentFilePath->Buffer, uninstallFilePath->Buffer, TRUE)) { Context->ErrorCode = GetLastError(); } PhDereferenceObject(uninstallFilePath); + PhDereferenceObject(currentFilePath); + return TRUE; } @@ -599,7 +605,9 @@ VOID SetupCreateImageFileExecutionOptions( static PH_STRINGREF PhImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); HANDLE keyHandle; - // Set the default Image File Execution Options. + if (WindowsVersion < WINDOWS_10) + return; + if (NT_SUCCESS(PhCreateKey( &keyHandle, KEY_WRITE | DELETE, diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 83d4028a1d41..e9c6c8a3ccc1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -82,14 +82,14 @@ static VOID TaskDialogCreateIcons( HICON smallIcon; largeIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), PH_LOAD_ICON_SIZE_LARGE, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON) ); smallIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), PH_LOAD_ICON_SIZE_LARGE, GetSystemMetrics(SM_CXSMICON), diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 6a44c237e06d..2bfaff5d7db2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -86,14 +86,14 @@ VOID TaskDialogCreateIcons( HICON smallIcon; largeIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), PH_LOAD_ICON_SIZE_LARGE, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON) ); smallIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), PH_LOAD_ICON_SIZE_LARGE, GetSystemMetrics(SM_CXSMICON), From adeb26589b765ccdc950ff3ac3de1b9d5083f510 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 00:39:40 +1000 Subject: [PATCH 368/839] peview: Enumerate all symbol types , Enable size column --- tools/peview/main.c | 8 +++++--- tools/peview/pdb.c | 2 +- tools/peview/pdbprp.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/peview/main.c b/tools/peview/main.c index f3bf35f5b892..33fa3d33ab96 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -61,7 +61,7 @@ INT WINAPI wWinMain( { { 0, L"h", NoArgumentType } }; - PH_STRINGREF commandLine; + PPH_STRING commandLine; if (!NT_SUCCESS(PhInitializePhLib())) return 1; @@ -75,16 +75,18 @@ INT WINAPI wWinMain( PhApplicationName = L"PE Viewer"; - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + if (!NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + return 1; PhParseCommandLine( - &commandLine, + &commandLine->sr, options, sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), PH_COMMAND_LINE_IGNORE_FIRST_PART, PvCommandLineCallback, NULL ); + PhDereferenceObject(commandLine); if (!PvFileName) { diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 61e222ad8daa..78475e31ef3d 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -2369,7 +2369,7 @@ NTSTATUS PeDumpFileSymbols( // Enumerate user defined types //PrintUserDefinedTypes(Context); - //SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); + SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); } CleanupExit: diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 39d66924472e..5f232ff44d79 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -763,7 +763,7 @@ VOID PvInitializeSymbolTree( 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, FALSE, L"Size", 40, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_SIZE, 0, 0); + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_SIZE, TRUE, L"Size", 40, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_SIZE, 0, 0); TreeNew_SetSort(TreeNewHandle, 0, NoSortOrder); From 25cddab3aafcbbbd38b12f155d92930ee1ed5e7b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 06:32:19 +1000 Subject: [PATCH 369/839] Add PhCreateDirectory/PhDeleteDirectory functions, Update PhCreateCacheFile functions --- ProcessHacker/ProcessHacker.def | 12 +- phlib/include/phnative.h | 14 ++ phlib/include/phutil.h | 16 +- phlib/native.c | 215 ++++++++++++++++++ phlib/settings.c | 2 +- phlib/util.c | 50 +++- plugins/ExtraPlugins/cloud.c | 5 +- plugins/ExtraPlugins/dialog.c | 5 - plugins/ExtraPlugins/plugin.c | 5 - plugins/ExtraPlugins/setup/updater.c | 55 +---- plugins/NetworkTools/update.c | 14 +- plugins/Updater/page5.c | 12 +- plugins/Updater/updater.c | 59 ++--- plugins/Updater/updater.h | 5 +- plugins/UserNotes/db.c | 2 +- .../CustomSetupTool/CustomSetupTool/appsup.c | 172 -------------- .../CustomSetupTool/download.c | 2 +- .../CustomSetupTool/CustomSetupTool/extract.c | 2 +- .../CustomSetupTool/include/appsup.h | 15 +- .../CustomSetupTool/CustomSetupTool/install.c | 6 +- tools/CustomSetupTool/CustomSetupTool/main.c | 82 +++---- tools/CustomSetupTool/CustomSetupTool/setup.c | 203 +++++++++++------ .../CustomSetupTool/uninstall.c | 2 +- tools/peview/main.c | 3 +- 24 files changed, 526 insertions(+), 432 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 36df3d1a187b..fcec0b535446 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -227,12 +227,14 @@ EXPORTS PhZeroExtendToUtf16Ex ; phnative + PhCreateDirectory PhCreateFileWin32 PhCreateFileWin32Ex PhCreateKey PhCreateNamedPipe PhCreatePipe PhConnectPipe + PhDeleteDirectory PhDeleteFileWin32 PhDisconnectNamedPipe PhEnumDirectoryFile @@ -345,8 +347,6 @@ EXPORTS PhGetApplicationDirectory PhGetApplicationFileName PhGetBaseName - PhGetCacheDirectory - PhGetCacheFileName PhGetDllFileName PhGetFileDialogFileName PhGetFileDialogFilterIndex @@ -635,4 +635,10 @@ EXPORTS PhGetJsonArrayLong64 PhGetJsonArrayLength PhGetJsonArrayIndexObject - PhGetJsonObjectAsArrayList \ No newline at end of file + PhGetJsonObjectAsArrayList + +; cache + PhGetCacheDirectory + PhClearCacheDirectory + PhCreateCacheFile + PhDeleteCacheFile diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index c5a60e72b0c6..b9dd90ab0716 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -974,6 +974,20 @@ PhDeleteFileWin32( _In_ PWSTR FileName ); +PHLIBAPI +NTSTATUS +NTAPI +PhCreateDirectory( + _In_ PPH_STRING DirectoryPath + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhDeleteDirectory( + _In_ PPH_STRING DirectoryPath + ); + PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 9c3722bce4aa..0d551968dc7f 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1056,10 +1056,24 @@ PhGetCacheDirectory( VOID ); +PHLIBAPI +VOID +NTAPI +PhClearCacheDirectory( + VOID + ); + PHLIBAPI PPH_STRING NTAPI -PhGetCacheFileName( +PhCreateCacheFile( + _In_ PPH_STRING FileName + ); + +PHLIBAPI +VOID +NTAPI +PhDeleteCacheFile( _In_ PPH_STRING FileName ); diff --git a/phlib/native.c b/phlib/native.c index a567ac5dad0e..435c8426825c 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6196,6 +6196,221 @@ NTSTATUS PhDeleteFileWin32( 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_FAIL_CHECK; + + if (RtlDoesFileExists_U(PhGetString(DirectoryPath))) + return STATUS_SUCCESS; + + remainingPart = PhGetStringRef(DirectoryPath); + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, '\\', &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 (!RtlDoesFileExists_U(PhGetString(tempPathString))) + { + HANDLE directoryHandle; + + // Create the directory. + if (NT_SUCCESS(PhCreateFileWin32( + &directoryHandle, + PhGetString(tempPathString), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + 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 (RtlDoesFileExists_U(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, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + PhEnumDirectoryFile(directoryHandle, NULL, PhpDeleteDirectoryCallback, fullName); + + NtClose(directoryHandle); + + // Delete the directory. + RemoveDirectory(PhGetString(fullName)); + } + } + else + { + if (Information->FileAttributes & FILE_ATTRIBUTE_READONLY) + { + HANDLE fileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(fullName), + FILE_GENERIC_WRITE, + 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); + } + } + + 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, + 0, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + IO_STATUS_BLOCK isb; + FILE_DISPOSITION_INFORMATION fileInfo; + + // Remove any files or folders inside the directory. + status = PhEnumDirectoryFile( + directoryHandle, + NULL, + PhpDeleteDirectoryCallback, + DirectoryPath + ); + + // Remove the directory. + fileInfo.DeleteFile = TRUE; + status = NtSetInformationFile( + directoryHandle, + &isb, + &fileInfo, + sizeof(FILE_DISPOSITION_INFORMATION), + FileDispositionInformation + ); + + NtClose(directoryHandle); + } + + if (!RtlDoesFileExists_U(PhGetString(DirectoryPath))) + return STATUS_SUCCESS; + + return status; +} + /** * Creates an anonymous pipe. * diff --git a/phlib/settings.c b/phlib/settings.c index 5687b891d26b..173afb1cbe91 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -925,7 +925,7 @@ NTSTATUS PhSaveSettings( if (indexOfFileName != -1) { directoryName = PhSubstring(fullPath, 0, indexOfFileName); - //SHCreateDirectoryEx(NULL, directoryName->Buffer, NULL); + //PhCreateDirectory(directoryName); PhDereferenceObject(directoryName); } diff --git a/phlib/util.c b/phlib/util.c index e5f0f6d7b38a..163208755298 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5021,10 +5021,22 @@ PPH_STRING PhGetCacheDirectory( VOID ) { - return PhGetKnownLocation(CSIDL_LOCAL_APPDATA, L"\\Process Hacker\\Cache\\"); + return PhGetKnownLocation(CSIDL_LOCAL_APPDATA, L"\\Process Hacker\\Cache"); } -PPH_STRING PhGetCacheFileName( +VOID PhClearCacheDirectory( + VOID + ) +{ + PPH_STRING cacheDirectory; + + cacheDirectory = PhGetCacheDirectory(); + PhDeleteDirectory(cacheDirectory); + + PhDereferenceObject(cacheDirectory); +} + +PPH_STRING PhCreateCacheFile( _In_ PPH_STRING FileName ) { @@ -5050,13 +5062,10 @@ PPH_STRING PhGetCacheFileName( { PPH_STRING directoryPath; - if (indexOfFileName != -1) + if (indexOfFileName != -1 && (directoryPath = PhSubstring(cacheFullFilePath, 0, indexOfFileName))) { - if (directoryPath = PhSubstring(cacheFullFilePath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } + PhCreateDirectory(directoryPath); + PhDereferenceObject(directoryPath); } } @@ -5065,3 +5074,28 @@ PPH_STRING PhGetCacheFileName( return cacheFullFilePath; } + +VOID PhDeleteCacheFile( + _In_ PPH_STRING FileName + ) +{ + PPH_STRING cacheDirectory; + PPH_STRING cacheFullFilePath; + ULONG indexOfFileName = -1; + + if (RtlDoesFileExists_U(PhGetString(FileName))) + { + PhDeleteFileWin32(PhGetString(FileName)); + } + + if (cacheFullFilePath = PhGetFullPath(PhGetString(FileName), &indexOfFileName)) + { + if (indexOfFileName != -1 && (cacheDirectory = PhSubstring(cacheFullFilePath, 0, indexOfFileName))) + { + PhDeleteDirectory(cacheDirectory); + PhDereferenceObject(cacheDirectory); + } + + PhDereferenceObject(cacheFullFilePath); + } +} diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index f8a791741008..d2afea082a3f 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -313,7 +313,7 @@ NTSTATUS SetupExtractBuild( extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr); fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName); - SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(fullSetupPath), NULL); + PhCreateDirectory(PhGetString(fullSetupPath)); PhDereferenceObject(fullSetupPath); PhDereferenceObject(extractPath); @@ -340,7 +340,8 @@ NTSTATUS SetupExtractBuild( { if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) { - SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(directoryPath), NULL); + PhCreateDirectory(PhGetString(directoryPath)); + PhDereferenceObject(directoryPath); } } diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c index 50ca89609f1e..2a1f9708ac07 100644 --- a/plugins/ExtraPlugins/dialog.c +++ b/plugins/ExtraPlugins/dialog.c @@ -434,11 +434,6 @@ INT_PTR CALLBACK CloudPluginsDlgProc( 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( diff --git a/plugins/ExtraPlugins/plugin.c b/plugins/ExtraPlugins/plugin.c index 2e9b5bbed56f..1bfd3d10c733 100644 --- a/plugins/ExtraPlugins/plugin.c +++ b/plugins/ExtraPlugins/plugin.c @@ -208,15 +208,10 @@ PPHAPP_PLUGIN PhCreateDisabledPlugin( 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 ) diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index d8a6d0d95c14..8a1ce61eb1d3 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -91,44 +91,6 @@ PPH_STRING UpdaterGetOpaqueXmlNodeText( 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 ) @@ -284,6 +246,7 @@ NTSTATUS UpdateDownloadThread( BOOLEAN downloadSuccess = FALSE; BOOLEAN hashSuccess = FALSE; BOOLEAN signatureSuccess = FALSE; + LONG updateResult = PH_UPDATEISERRORED; HANDLE tempFileHandle = NULL; HINTERNET httpSessionHandle = NULL; HINTERNET httpConnectionHandle = NULL; @@ -302,7 +265,7 @@ NTSTATUS UpdateDownloadThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); - context->SetupFilePath = PhGetCacheFileName(PhaFormatString( + context->SetupFilePath = PhCreateCacheFile(PhaFormatString( L"%s.zip", PhGetStringOrEmpty(context->Node->InternalName) )); @@ -563,18 +526,18 @@ NTSTATUS UpdateDownloadThread( { if (NT_SUCCESS(SetupExtractBuild(context))) { - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); - } - else - { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); + updateResult = PH_UPDATESUCCESS; } } - else + + if (context->SetupFilePath) { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); + PhDeleteCacheFile(context->SetupFilePath); + PhDereferenceObject(context->SetupFilePath); } + PostMessage(context->DialogHandle, updateResult, 0, 0); + return STATUS_SUCCESS; } diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 45a2db920356..8c8692b1b0ef 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -291,7 +291,6 @@ NTSTATUS GeoIPUpdateThread( PPH_STRING fwLinkUrl = NULL; PPH_STRING downloadHostPath = NULL; PPH_STRING downloadUrlPath = NULL; - PPH_STRING directoryPath = NULL; URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; @@ -305,7 +304,7 @@ NTSTATUS GeoIPUpdateThread( if (!(fwLinkUrl = QueryFwLinkUrl(context))) goto CleanupExit; - context->SetupFilePath = PhGetCacheFileName(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); + context->SetupFilePath = PhCreateCacheFile(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); if (PhIsNullOrEmptyString(context->SetupFilePath)) goto CleanupExit; @@ -603,15 +602,10 @@ NTSTATUS GeoIPUpdateThread( if (userAgentString) PhDereferenceObject(userAgentString); - if (RtlDoesFileExists_U(PhGetString(context->SetupFilePath))) + if (context->SetupFilePath) { - PhDeleteFileWin32(PhGetString(context->SetupFilePath)); - } - - if (directoryPath) - { - RemoveDirectory(PhGetString(directoryPath)); - PhDereferenceObject(directoryPath); + PhDeleteCacheFile(context->SetupFilePath); + PhDereferenceObject(context->SetupFilePath); } if (success) diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 0b766debcfeb..863578027588 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -152,10 +152,8 @@ VOID ShowLatestVersionDialog( config.pszWindowTitle = L"Process Hacker - Updater"; config.pszMainInstruction = L"You're running the latest version."; config.pszContent = PhaFormatString( - L"Version: v%lu.%lu.%lu\r\nCompiled: %s\r\n\r\nView Changelog", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion, + L"Version: v%s\r\nCompiled: %s\r\n\r\nView Changelog", + PhGetStringOrEmpty(Context->CurrentVersionString), PhaFormatDateTime(&systemTime)->Buffer )->Buffer; @@ -180,10 +178,8 @@ VOID ShowNewerVersionDialog( config.pszWindowTitle = L"Process Hacker - Updater"; config.pszMainInstruction = L"You're running a pre-release build."; config.pszContent = PhaFormatString( - L"Pre-release build: v%lu.%lu.%lu\r\n", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion + L"Pre-release build: v%s\r\n", + PhGetStringOrEmpty(Context->CurrentVersionString) )->Buffer; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 1e5c29dd70f1..16790f4ab3ad 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -37,12 +37,7 @@ PPH_UPDATER_CONTEXT CreateUpdateContext( 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->CurrentVersionString = PhGetPhVersion(); context->StartupCheck = StartupCheck; return context; @@ -62,7 +57,8 @@ VOID FreeUpdateContext( PhClearReference(&Context->SetupFilePath); PhClearReference(&Context->SetupFileDownloadUrl); PhClearReference(&Context->BuildMessage); - + PhClearReference(&Context->CurrentVersionString); + PhDereferenceObject(Context); } @@ -155,18 +151,20 @@ BOOLEAN LastUpdateCheckExpired( 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); - + PPH_STRING currentUpdateTimeString; + + currentUpdateTimeString = PhFormatUInt64(currentUpdateTimeTicks.QuadPart, FALSE); PhSetStringSetting2(SETTING_NAME_LAST_CHECK, ¤tUpdateTimeString->sr); - PhDereferenceObject(currentUpdateTimeString); + PhDereferenceObject(currentUpdateTimeString); + PhDereferenceObject(lastUpdateTimeString); return TRUE; } + PhDereferenceObject(lastUpdateTimeString); return FALSE; } @@ -521,12 +519,7 @@ NTSTATUS UpdateCheckSilentThread( if (!QueryUpdateData(context)) goto CleanupExit; - currentVersion = MAKE_VERSION_ULONGLONG( - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion, - 0 - ); + currentVersion = ParseVersionString(context->CurrentVersionString); #ifdef FORCE_UPDATE_CHECK latestVersion = MAKE_VERSION_ULONGLONG( @@ -542,9 +535,6 @@ NTSTATUS UpdateCheckSilentThread( // 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) { @@ -589,12 +579,7 @@ NTSTATUS UpdateCheckThread( return STATUS_SUCCESS; } - currentVersion = MAKE_VERSION_ULONGLONG( - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion, - 0 - ); + currentVersion = ParseVersionString(context->CurrentVersionString); #ifdef FORCE_UPDATE_CHECK latestVersion = MAKE_VERSION_ULONGLONG( @@ -640,7 +625,7 @@ static PPH_STRING UpdaterParseDownloadFileName( return NULL; downloadFileName = PhCreateString2(&baseNamePart); - filePath = PhGetCacheFileName(downloadFileName); + filePath = PhCreateCacheFile(downloadFileName); PhDereferenceObject(downloadFileName); return filePath; @@ -672,12 +657,9 @@ NTSTATUS UpdateDownloadThread( // Create a user agent string. userAgentString = PhFormatString( - L"PH_%lu.%lu_%lu", - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion + L"PH_%s", + PhGetStringOrEmpty(context->CurrentVersionString) ); - if (PhIsNullOrEmptyString(userAgentString)) goto CleanupExit; @@ -715,13 +697,13 @@ NTSTATUS UpdateDownloadThread( if (PhIsNullOrEmptyString(context->SetupFilePath)) goto CleanupExit; - // Create output file + // Create temporary 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_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) @@ -991,7 +973,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( switch (uMsg) { - case WM_INITDIALOG: + case PH_SHOWDIALOG: { if (IsMinimized(hwndDlg)) ShowWindow(hwndDlg, SW_RESTORE); @@ -1095,6 +1077,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( ) { PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; + UpdateDialogHandle = hwndDlg; switch (uMsg) { @@ -1177,7 +1160,7 @@ VOID ShowUpdateDialog( PhWaitForEvent(&InitializedEvent, NULL); } - PostMessage(UpdateDialogHandle, WM_INITDIALOG, 0, 0); + PostMessage(UpdateDialogHandle, PH_SHOWDIALOG, 0, 0); } VOID StartInitialCheck( diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index d4bf4abda0aa..f0ecd291f096 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -45,6 +45,7 @@ #define PH_UPDATENEWER (WM_APP + 504) #define PH_UPDATESUCCESS (WM_APP + 505) #define PH_UPDATEFAILURE (WM_APP + 506) +#define PH_SHOWDIALOG (WM_APP + 507) #define PLUGIN_NAME L"ProcessHacker.UpdateChecker" #define SETTING_NAME_AUTO_CHECK (PLUGIN_NAME L".PromptStart") @@ -89,9 +90,7 @@ typedef struct _PH_UPDATER_CONTEXT HWND DialogHandle; ULONG ErrorCode; - ULONG CurrentMinorVersion; - ULONG CurrentMajorVersion; - ULONG CurrentRevisionVersion; + PPH_STRING CurrentVersionString; PPH_STRING Version; PPH_STRING RevVersion; PPH_STRING RelDate; diff --git a/plugins/UserNotes/db.c b/plugins/UserNotes/db.c index 3e96290eb91e..6a7b2b9082ac 100644 --- a/plugins/UserNotes/db.c +++ b/plugins/UserNotes/db.c @@ -417,7 +417,7 @@ NTSTATUS SaveDb( if (fullPath = PH_AUTO(PhGetFullPath(ObjectDbPath->Buffer, &indexOfFileName))) { if (indexOfFileName != -1) - SHCreateDirectoryEx(NULL, PhaSubstring(fullPath, 0, indexOfFileName)->Buffer, NULL); + PhCreateDirectory(PhaSubstring(fullPath, 0, indexOfFileName)); } } diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index c94f746e94aa..a4c317b4f8ee 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -406,36 +406,6 @@ 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 ) @@ -521,145 +491,3 @@ BOOLEAN ShutdownProcessHacker(VOID) PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); return TRUE; } - -BOOLEAN CreateDirectoryPath(_In_ PWSTR DirPath) -{ - BOOLEAN success = FALSE; - PPH_STRING dirPathString = NULL; - PWSTR dirPathDup = NULL; - - if (RtlDoesFileExists_U(DirPath)) - return TRUE; - - if ((dirPathDup = PhDuplicateStringZ(DirPath)) == NULL) - goto CleanupExit; - - for (PWSTR path = _wcstok(dirPathDup, L"\\"); path; path = _wcstok(NULL, L"\\")) - { - if (!dirPathString) - dirPathString = PhCreateString(path); - else - { - PPH_STRING tempPathString; - - tempPathString = PhConcatStrings( - 3, - dirPathString->Buffer, - L"\\", - path - ); - - if (!RtlDoesFileExists_U(PhGetString(tempPathString))) - { - if (!CreateDirectory(PhGetString(tempPathString), NULL)) - { - PhDereferenceObject(tempPathString); - goto CleanupExit; - } - } - - PhSwapReference(&dirPathString, tempPathString); - PhDereferenceObject(tempPathString); - } - } - - success = TRUE; - -CleanupExit: - - if (dirPathString) - { - PhDereferenceObject(dirPathString); - } - - if (dirPathDup) - { - PhFree(dirPathDup); - } - - return success; -} - -BOOLEAN RemoveDirectoryPath(_In_ PWSTR DirPath) -{ - HANDLE findHandle; - PPH_STRING findPath; - WIN32_FIND_DATA data = { 0 }; - - findPath = PhConcatStrings2(DirPath, L"\\*"); - - if ((findHandle = FindFirstFile(findPath->Buffer, &data)) == INVALID_HANDLE_VALUE) - { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - { - PhDereferenceObject(findPath); - return TRUE; - } - - PhDereferenceObject(findPath); - return FALSE; - } - - do - { - if (PhEqualStringZ(data.cFileName, L".", TRUE) || PhEqualStringZ(data.cFileName, L"..", TRUE)) - continue; - - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - PPH_STRING dirPath = PhConcatStrings(3, DirPath, L"\\", data.cFileName); - - RemoveDirectoryPath(dirPath->Buffer); - PhDereferenceObject(dirPath); - } - else - { - PPH_STRING filePath = PhConcatStrings(3, DirPath, L"\\", data.cFileName); - - if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - { - _wchmod(filePath->Buffer, _S_IWRITE); - } - - SetupDeleteDirectoryFile(filePath->Buffer); - PhDereferenceObject(filePath); - } - - } while (FindNextFile(findHandle, &data)); - - FindClose(findHandle); - - // Delete the parent directory - RemoveDirectory(DirPath); - - PhDereferenceObject(findPath); - return TRUE; -} - -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/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 0fc0f7fc1aa7..eea47d368e65 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -405,7 +405,7 @@ BOOLEAN UpdateDownloadUpdateData( downloadFileName = PhCreateString2(&baseNamePart); } - Context->FilePath = PhGetCacheFileName(downloadFileName); + Context->FilePath = PhCreateCacheFile(downloadFileName); if (PhIsNullOrEmptyString(Context->FilePath)) goto CleanupExit; diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index a82b278aae02..b6da5136be6d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -177,7 +177,7 @@ BOOLEAN SetupExtractBuild( if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) { - if (!CreateDirectoryPath(PhGetString(directoryPath))) + if (!NT_SUCCESS(PhCreateDirectory(PhGetString(directoryPath)))) { PhDereferenceObject(directoryPath); PhDereferenceObject(fullSetupPath); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h index 5a173e411ec9..8601e3c1cc9e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h @@ -44,10 +44,6 @@ PVOID ExtractResourceToBuffer( _In_ PWSTR Resource ); -VOID SetupDeleteDirectoryFile( - _In_ PWSTR FileName - ); - VOID SetupInitializeFont( _In_ HWND ControlHandle, _In_ LONG Height, @@ -80,13 +76,4 @@ BOOLEAN ShutdownProcessHacker( VOID ); -BOOLEAN CreateDirectoryPath( - _In_ PWSTR DirectoryPath - ); - -BOOLEAN RemoveDirectoryPath( - _In_ PWSTR DirPath - ); - - -#endif \ No newline at end of file +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/install.c b/tools/CustomSetupTool/CustomSetupTool/install.c index c8372ca76d34..69826e9bc9ca 100644 --- a/tools/CustomSetupTool/CustomSetupTool/install.c +++ b/tools/CustomSetupTool/CustomSetupTool/install.c @@ -39,7 +39,7 @@ NTSTATUS SetupProgressThread( goto CleanupExit; // Create the install folder path. - if (!CreateDirectoryPath(PhGetString(Context->SetupInstallPath))) + if (!NT_SUCCESS(PhCreateDirectory(Context->SetupInstallPath))) goto CleanupExit; // Upgrade the 2.x settings file. @@ -47,7 +47,9 @@ NTSTATUS SetupProgressThread( // Remove the previous installation. if (Context->SetupResetSettings) - RemoveDirectoryPath(PhGetString(Context->SetupInstallPath)); + { + PhDeleteDirectory(Context->SetupInstallPath); + } // Create the uninstaller. if (!SetupCreateUninstallFile(Context)) diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 227c2cef77d4..0ef34e6c04b7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -20,34 +20,7 @@ #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 }, -}; - -NTSTATUS CreateSetupMutant( - 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); -} VOID SetupInitializeDpi( VOID @@ -205,10 +178,17 @@ INT WINAPI wWinMain( _In_ INT CmdShow ) { + static 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 }, + }; + HANDLE mutantHandle; PPH_STRING commandLine; - - if (!NT_SUCCESS(CreateSetupMutant())) - return 1; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING mutantName; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); @@ -237,23 +217,35 @@ INT WINAPI wWinMain( PhDereferenceObject(commandLine); - // DEBUG // if (CheckProcessHackerInstalled()) SetupMode = SETUP_COMMAND_UNINSTALL; + RtlInitUnicodeString(&mutantName, L"PhSetupMutant"); + InitializeObjectAttributes( + &oa, + &mutantName, + 0, + PhGetNamespaceHandle(), + NULL + ); - switch (SetupMode) + if (NT_SUCCESS(NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE))) { - case SETUP_COMMAND_INSTALL: - default: - SetupShowInstallDialog(); - break; - case SETUP_COMMAND_UNINSTALL: - SetupShowUninstallDialog(); - break; - case SETUP_COMMAND_UPDATE: - SetupShowUpdateDialog(); - break; - case SETUP_COMMAND_REPAIR: - break; + switch (SetupMode) + { + case SETUP_COMMAND_INSTALL: + default: + SetupShowInstallDialog(); + break; + case SETUP_COMMAND_UNINSTALL: + SetupShowUninstallDialog(); + break; + case SETUP_COMMAND_UPDATE: + SetupShowUpdateDialog(); + break; + case SETUP_COMMAND_REPAIR: + break; + } + + NtClose(mutantHandle); } - return 0; + return ERROR_SUCCESS; } \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 1c8e6e5906f3..b9fbfaab07be 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -131,16 +131,17 @@ PPH_STRING SetupFindInstallDirectory( // Check if the string is valid. 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%"); + static PH_STRINGREF programFiles = PH_STRINGREF_INIT(L"%ProgramFiles%"); + static PH_STRINGREF defaultDirectoryName = PH_STRINGREF_INIT(L"\\Process Hacker\\"); SYSTEM_INFO info; - PPH_STRING expandedString; GetNativeSystemInfo(&info); if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + PPH_STRING expandedString; + if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programW6432))) { setupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); @@ -148,6 +149,8 @@ PPH_STRING SetupFindInstallDirectory( } else { + PPH_STRING expandedString; + if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programFiles))) { setupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); @@ -189,13 +192,13 @@ BOOLEAN SetupCreateUninstallFile( if (RtlDoesFileExists_U(backupFilePath->Buffer)) { - if (!DeleteFile(backupFilePath->Buffer)) + if (!NT_SUCCESS(PhDeleteFileWin32(backupFilePath->Buffer))) { PPH_STRING tempFileName; PPH_STRING tempFilePath; tempFileName = PhCreateString(L"processhacker-setup.old"); - tempFilePath = PhGetCacheFileName(tempFileName); + tempFilePath = PhCreateCacheFile(tempFileName); if (!MoveFile(backupFilePath->Buffer, tempFilePath->Buffer)) { @@ -240,7 +243,7 @@ VOID SetupDeleteUninstallFile( PPH_STRING tempFilePath; tempFileName = PhCreateString(L"processhacker-setup.exe"); - tempFilePath = PhGetCacheFileName(tempFileName); + tempFilePath = PhCreateCacheFile(tempFileName); if (PhIsNullOrEmptyString(tempFilePath)) { @@ -259,6 +262,127 @@ VOID SetupDeleteUninstallFile( PhDereferenceObject(uninstallFilePath); } +VOID SetupStartService( + _In_ PWSTR ServiceName + ) +{ + SC_HANDLE serviceHandle; + + serviceHandle = PhOpenService( + ServiceName, + SERVICE_QUERY_STATUS | SERVICE_START + ); + + if (serviceHandle) + { + 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 (status.dwCurrentState != SERVICE_RUNNING) + { + ULONG attempts = 5; + + 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; + } + } + + Sleep(1000); + + } while (--attempts != 0); + } + } + + CloseServiceHandle(serviceHandle); + } +} + +VOID SetupStopService( + _In_ PWSTR ServiceName + ) +{ + SC_HANDLE serviceHandle; + + serviceHandle = PhOpenService( + ServiceName, + SERVICE_QUERY_STATUS | SERVICE_STOP + ); + + if (serviceHandle) + { + 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 (status.dwCurrentState != SERVICE_STOPPED) + { + ULONG attempts = 5; + + 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; + } + } + + Sleep(1000); + + } while (--attempts != 0); + } + } + + CloseServiceHandle(serviceHandle); + } +} + + VOID SetupStartKph( _In_ PPH_SETUP_CONTEXT Context ) @@ -287,21 +411,7 @@ VOID SetupStartKph( NtClose(processHandle); } - while (retries < 5) - { - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_START)) - { - StartService(serviceHandle, 0, NULL); - CloseServiceHandle(serviceHandle); - break; - } - - Sleep(1000); - retries++; - } + SetupStartService(L"KProcessHacker3"); } PhDereferenceObject(clientPath); @@ -311,43 +421,9 @@ BOOLEAN SetupUninstallKph( _In_ PPH_SETUP_CONTEXT Context ) { - ULONG retries = 0; - - while (retries < 5) - { - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (serviceHandle = PhOpenService(L"KProcessHacker2", SERVICE_STOP | DELETE)) - { - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - DeleteService(serviceHandle); - CloseServiceHandle(serviceHandle); - break; - } - - Sleep(1000); - retries++; - } + SetupStopService(L"KProcessHacker2"); - retries = 0; - - while (retries < 5) - { - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_STOP | DELETE)) - { - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - DeleteService(serviceHandle); - CloseServiceHandle(serviceHandle); - break; - } - - Sleep(1000); - retries++; - } + SetupStopService(L"KProcessHacker3"); return TRUE; } @@ -410,7 +486,7 @@ VOID SetupSetWindowsOptions( { PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); - SetupDeleteDirectoryFile(settingsFileName->Buffer); + PhDeleteFileWin32(settingsFileName->Buffer); PhDereferenceObject(settingsFileName); } @@ -494,25 +570,25 @@ VOID SetupDeleteWindowsOptions( if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) { - SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDeleteFileWin32(PhGetString(startmenuFolderString)); PhDereferenceObject(startmenuFolderString); } if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk")) { - SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDeleteFileWin32(PhGetString(startmenuFolderString)); PhDereferenceObject(startmenuFolderString); } if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) { - SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDeleteFileWin32(PhGetString(startmenuFolderString)); PhDereferenceObject(startmenuFolderString); } if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) { - SetupDeleteDirectoryFile(PhGetString(startmenuFolderString)); + PhDeleteFileWin32(PhGetString(startmenuFolderString)); PhDereferenceObject(startmenuFolderString); } @@ -628,7 +704,6 @@ VOID SetupCreateImageFileExecutionOptions( 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 }, sizeof(ULONG64)); diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index e9c6c8a3ccc1..697576a43c22 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -64,7 +64,7 @@ NTSTATUS SetupUninstallBuild( SetupDeleteUninstallKey(); // Remove the previous installation. - RemoveDirectoryPath(PhGetString(Context->SetupInstallPath)); + PhDeleteDirectory(Context->SetupInstallPath); ShowUninstallCompleteDialog(Context); return STATUS_SUCCESS; diff --git a/tools/peview/main.c b/tools/peview/main.c index 33fa3d33ab96..8823cf300a6f 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -63,6 +63,8 @@ INT WINAPI wWinMain( }; PPH_STRING commandLine; + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (!NT_SUCCESS(PhInitializePhLib())) return 1; @@ -117,7 +119,6 @@ INT WINAPI wWinMain( { PPH_STRING targetFileName; - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); targetFileName = PvResolveShortcutTarget(PvFileName); if (targetFileName) From e253bb07ce558c801dece928d2183d1e32776508 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 07:37:51 +1000 Subject: [PATCH 370/839] Add unused PhInitializeMitigationPolicy --- ProcessHacker/main.c | 62 ++++++++++++++++++++++++++++++++++++++++++ phlib/include/phutil.h | 8 ++++++ phlib/util.c | 28 +++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 0291db3d0c94..498260e9c118 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -517,6 +517,68 @@ VOID PhInitializeFont( } } +VOID PhInitializeMitigationPolicy( + VOID + ) +{ + static PH_STRINGREF policyKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); + static UNICODE_STRING policyKeyValue = RTL_CONSTANT_STRING(L"MitigationOptions"); + BOOLEAN policyKeyValid = FALSE; + HANDLE keyReadHandle; + HANDLE keyWriteHandle; + + if (WindowsVersion < WINDOWS_10 || !PhGetOwnTokenAttributes().Elevated) + return; + +#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) + + if (NT_SUCCESS(PhOpenKey( + &keyReadHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &policyKeyName, + 0 + ))) + { + if (PhQueryRegistryUlong64(keyReadHandle, L"MitigationOptions") == DEFAULT_MITIGATION_POLICY_FLAGS) + policyKeyValid = TRUE; + + NtClose(keyReadHandle); + } + + if (policyKeyValid) + return; + + if (NT_SUCCESS(PhCreateKey( + &keyWriteHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &policyKeyName, + 0, + 0, + NULL + ))) + { + NtSetValueKey( + keyWriteHandle, + &policyKeyValue, + 0, + REG_QWORD, + &(ULONG64) { DEFAULT_MITIGATION_POLICY_FLAGS }, + sizeof(ULONG64) + ); + NtClose(keyWriteHandle); + } +} + NTSTATUS PhpReadSignature( _In_ PWSTR FileName, _Out_ PUCHAR *Signature, diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 0d551968dc7f..ce202be0128b 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -812,6 +812,14 @@ PhQueryRegistryString( _In_opt_ PWSTR ValueName ); +PHLIBAPI +ULONG64 +NTAPI +PhQueryRegistryUlong64( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ); + typedef struct _PH_FLAG_MAPPING { ULONG Flag1; diff --git a/phlib/util.c b/phlib/util.c index 163208755298..5e20010adf6a 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -3522,6 +3522,34 @@ PPH_STRING PhQueryRegistryString( return string; } +ULONG64 PhQueryRegistryUlong64( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ) +{ + ULONG64 ulong64 = 0; + 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_QWORD) + { + if (buffer->DataLength == sizeof(ULONG64)) + ulong64 = *(PULONG64)buffer->Data; + } + + PhFree(buffer); + } + + return ulong64; +} + VOID PhMapFlags1( _Inout_ PULONG Value2, _In_ ULONG Value1, From 45c458699711315c93868b2f3773fbb00650324f Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 08:09:19 +1000 Subject: [PATCH 371/839] Add LocalCachePath setting, Fix build warnings --- ProcessHacker/settings.c | 1 + phlib/native.c | 21 ++++++++++++++----- phlib/util.c | 11 +++++++++- plugins/ExtraPlugins/cloud.c | 4 ++-- .../CustomSetupTool/download.c | 2 +- .../CustomSetupTool/include/setup.h | 2 -- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 28994dd78cbe..9845dd49b0e9 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -80,6 +80,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"IconTogglesVisibility", L"1"); PhpAddStringSetting(L"JobListViewColumns", L""); PhpAddIntegerSetting(L"KphUnloadOnShutdown", L"0"); + PhpAddStringSetting(L"LocalCachePath", L"%LocalAppData%\\Process Hacker\\Cache"); PhpAddIntegerSetting(L"LogEntries", L"200"); // 512 PhpAddStringSetting(L"LogListViewColumns", L""); PhpAddIntegerPairSetting(L"LogWindowPosition", L"300,300"); diff --git a/phlib/native.c b/phlib/native.c index 435c8426825c..aeb0594d69a0 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6299,19 +6299,29 @@ static BOOLEAN PhpDeleteDirectoryCallback( if (NT_SUCCESS(PhCreateFileWin32( &directoryHandle, PhGetString(fullName), - FILE_GENERIC_READ, + FILE_GENERIC_READ | DELETE, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { + IO_STATUS_BLOCK isb; + FILE_DISPOSITION_INFORMATION fileInfo; + PhEnumDirectoryFile(directoryHandle, NULL, PhpDeleteDirectoryCallback, fullName); - NtClose(directoryHandle); + // Delete the directory. + fileInfo.DeleteFile = TRUE; + NtSetInformationFile( + directoryHandle, + &isb, + &fileInfo, + sizeof(FILE_DISPOSITION_INFORMATION), + FileDispositionInformation + ); - // Delete the directory. - RemoveDirectory(PhGetString(fullName)); + NtClose(directoryHandle); } } else @@ -6350,6 +6360,7 @@ static BOOLEAN PhpDeleteDirectoryCallback( } } + // Delete the file. PhDeleteFileWin32(PhGetString(fullName)); } @@ -6373,7 +6384,7 @@ NTSTATUS PhDeleteDirectory( &directoryHandle, PhGetString(DirectoryPath), FILE_GENERIC_READ | DELETE, - 0, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT diff --git a/phlib/util.c b/phlib/util.c index 5e20010adf6a..8f17f76f2309 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "md5.h" #include "sha.h" @@ -5049,7 +5050,15 @@ PPH_STRING PhGetCacheDirectory( VOID ) { - return PhGetKnownLocation(CSIDL_LOCAL_APPDATA, L"\\Process Hacker\\Cache"); + PPH_STRING executeString; + + // Get the default cache directory. + executeString = PhGetStringSetting(L"LocalCachePath"); + + // Expand environment strings. + PhMoveReference(&executeString, PhExpandEnvironmentStrings(&executeString->sr)); + + return executeString; } VOID PhClearCacheDirectory( diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index d2afea082a3f..07a94476fcc9 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -313,7 +313,7 @@ NTSTATUS SetupExtractBuild( extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr); fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName); - PhCreateDirectory(PhGetString(fullSetupPath)); + PhCreateDirectory(fullSetupPath); PhDereferenceObject(fullSetupPath); PhDereferenceObject(extractPath); @@ -340,7 +340,7 @@ NTSTATUS SetupExtractBuild( { if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) { - PhCreateDirectory(PhGetString(directoryPath)); + PhCreateDirectory(directoryPath); PhDereferenceObject(directoryPath); } diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index eea47d368e65..3355a0fc99dc 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -415,7 +415,7 @@ BOOLEAN UpdateDownloadUpdateData( PhGetString(Context->FilePath), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index fa8478a5403f..e63de392ceca 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -64,8 +64,6 @@ #define WM_UPDATE_SETUP (WM_APP + 2) #define WM_END_SETUP (WM_APP + 3) -extern HANDLE MutantHandle; - typedef enum _SETUP_COMMAND_TYPE { SETUP_COMMAND_INSTALL, From 6f9261e3b34597d320ab5d9964921da5d4b6a40e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 08:58:34 +1000 Subject: [PATCH 372/839] Remove CommonUtil plugin, Remove LocalCachePath setting (fix linker issue), Fix build --- ProcessHacker/ProcessHacker.def | 1 + ProcessHacker/plugin.c | 18 ++- ProcessHacker/settings.c | 1 - phlib/include/phutil.h | 7 + phlib/util.c | 28 ++-- plugins/CommonUtil/CHANGELOG.txt | 3 - plugins/CommonUtil/CommonUtil.vcxproj | 87 ------------ plugins/CommonUtil/CommonUtil.vcxproj.filters | 42 ------ plugins/CommonUtil/main.c | 54 -------- plugins/CommonUtil/main.h | 126 ------------------ plugins/CommonUtil/resource.h | 19 --- plugins/CommonUtil/resource.rc | 119 ----------------- .../CommonUtil/resources/active_search.bmp | Bin 1494 -> 0 bytes .../CommonUtil/resources/active_search.png | Bin 569 -> 0 bytes .../CommonUtil/resources/inactive_search.bmp | Bin 1494 -> 0 bytes .../CommonUtil/resources/inactive_search.png | Bin 755 -> 0 bytes plugins/Plugins.sln | 12 +- plugins/include/commonutil.h | 9 -- .../CustomSetupTool/CustomSetupTool/extract.c | 2 +- 19 files changed, 45 insertions(+), 483 deletions(-) delete mode 100644 plugins/CommonUtil/CHANGELOG.txt delete mode 100644 plugins/CommonUtil/CommonUtil.vcxproj delete mode 100644 plugins/CommonUtil/CommonUtil.vcxproj.filters delete mode 100644 plugins/CommonUtil/main.c delete mode 100644 plugins/CommonUtil/main.h delete mode 100644 plugins/CommonUtil/resource.h delete mode 100644 plugins/CommonUtil/resource.rc delete mode 100644 plugins/CommonUtil/resources/active_search.bmp delete mode 100644 plugins/CommonUtil/resources/active_search.png delete mode 100644 plugins/CommonUtil/resources/inactive_search.bmp delete mode 100644 plugins/CommonUtil/resources/inactive_search.png diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index fcec0b535446..d9ec3b6a127b 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -346,6 +346,7 @@ EXPORTS PhGenerateRandomAlphaString PhGetApplicationDirectory PhGetApplicationFileName + PhGetBaseDirectory PhGetBaseName PhGetDllFileName PhGetFileDialogFileName diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 495b5eeab934..c468ffa93131 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -206,16 +206,30 @@ static BOOLEAN EnumPluginsDirectoryCallback( if (PhEndsWithStringRef2(&baseName, L".dll", TRUE)) { - if (!PhIsPluginDisabled(&baseName)) + // Plugin blacklist + if (PhEndsWithStringRef2(&baseName, L"CommonUtil.dll", TRUE)) { 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); + PhDeleteFileWin32(fileName->Buffer); PhDereferenceObject(fileName); } + else + { + 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; diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 9845dd49b0e9..28994dd78cbe 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -80,7 +80,6 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"IconTogglesVisibility", L"1"); PhpAddStringSetting(L"JobListViewColumns", L""); PhpAddIntegerSetting(L"KphUnloadOnShutdown", L"0"); - PhpAddStringSetting(L"LocalCachePath", L"%LocalAppData%\\Process Hacker\\Cache"); PhpAddIntegerSetting(L"LogEntries", L"200"); // 512 PhpAddStringSetting(L"LogListViewColumns", L""); PhpAddIntegerPairSetting(L"LogWindowPosition", L"300,300"); diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index ce202be0128b..9eddea371402 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -576,6 +576,13 @@ PhGetBaseName( _In_ PPH_STRING FileName ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetBaseDirectory( + _In_ PPH_STRING FileName + ); + PHLIBAPI PPH_STRING NTAPI diff --git a/phlib/util.c b/phlib/util.c index 8f17f76f2309..62abe668d064 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1981,6 +1981,24 @@ PPH_STRING PhGetBaseName( 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, '\\', &pathPart, &baseNamePart)) + return NULL; + + return PhCreateString2(&pathPart); +} + /** * Retrieves the system directory path. */ @@ -5050,15 +5068,7 @@ PPH_STRING PhGetCacheDirectory( VOID ) { - PPH_STRING executeString; - - // Get the default cache directory. - executeString = PhGetStringSetting(L"LocalCachePath"); - - // Expand environment strings. - PhMoveReference(&executeString, PhExpandEnvironmentStrings(&executeString->sr)); - - return executeString; + return PhGetKnownLocation(CSIDL_LOCAL_APPDATA, L"\\Process Hacker\\Cache"); } VOID PhClearCacheDirectory( 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.vcxproj b/plugins/CommonUtil/CommonUtil.vcxproj deleted file mode 100644 index aa57362d61bc..000000000000 --- a/plugins/CommonUtil/CommonUtil.vcxproj +++ /dev/null @@ -1,87 +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;uxtheme.lib;%(AdditionalDependencies) - - - - - windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - - - - - windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - - - - - windowscodecs.lib;uxtheme.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - \ 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 6df6a7997e96..000000000000 --- a/plugins/CommonUtil/CommonUtil.vcxproj.filters +++ /dev/null @@ -1,42 +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 - - - - - Header Files - - - Header Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/plugins/CommonUtil/main.c b/plugins/CommonUtil/main.c deleted file mode 100644 index c5e5c664c546..000000000000 --- a/plugins/CommonUtil/main.c +++ /dev/null @@ -1,54 +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; - -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; - } - break; - } - - return TRUE; -} diff --git a/plugins/CommonUtil/main.h b/plugins/CommonUtil/main.h deleted file mode 100644 index 22604b66963c..000000000000 --- a/plugins/CommonUtil/main.h +++ /dev/null @@ -1,126 +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; - -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/resource.h b/plugins/CommonUtil/resource.h deleted file mode 100644 index 72caa7b05f55..000000000000 --- a/plugins/CommonUtil/resource.h +++ /dev/null @@ -1,19 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by resource.rc -// -#define IDB_SEARCH_ACTIVE_BMP 101 -#define IDB_SEARCH_ACTIVE 102 -#define IDB_SEARCH_INACTIVE_BMP 103 -#define IDB_SEARCH_INACTIVE 104 - -// 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 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/plugins/CommonUtil/resource.rc b/plugins/CommonUtil/resource.rc deleted file mode 100644 index fb7b45ceabac..000000000000 --- a/plugins/CommonUtil/resource.rc +++ /dev/null @@ -1,119 +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 - -#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 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", "dmex" - VALUE "FileDescription", "Common plugin for Process Hacker" - VALUE "FileVersion", "1.0" - VALUE "InternalName", "ProcessHacker.CommonUtil" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "CommonUtil.dll" - VALUE "ProductName", "Common plugin for Process Hacker" - VALUE "ProductVersion", "1.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -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" - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/plugins/CommonUtil/resources/active_search.bmp b/plugins/CommonUtil/resources/active_search.bmp deleted file mode 100644 index 61b13de9a1152694de72dd81c01c47282773298e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1494 zcmZ?ry~fG_24+A~1Bk_eSOka}8650^YlUPk4#vGuUW6N6R zWNHFQJcaSx|X%2hHw&(QFzR`wykGxlOd1cH4UFR%aZTy0vQ4UK=!p| e$uJ%|jmI2xrFi+H=HLw;0_g|8USwG+hBE*$K{!VM diff --git a/plugins/CommonUtil/resources/active_search.png b/plugins/CommonUtil/resources/active_search.png deleted file mode 100644 index 1cf29576b0b50ed5ee88c1ea8565fe2e9adb9b69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 569 zcmV-90>=G`P)sM}kty8!R#X_B;6=p#3k05!yiVY9d zlP4*H^pRy55Oc`+@ds70n9+mE9;(NrCYe{xn9wV|9VBsU+d(|PXf{(L44hUrH>@-V-11oc);or!AQP^ zGA=1rIynoT>*g-{55qdy^B+mtM4?zjp=AgQmBSj+v@_=Xhhe3lstAxc%pz)8#;6wA zW~r4l^}SZ|wEr*+mw^5uN{4!`dE+8h9B2g+;~9Laf!o+nq2=@QC~4{5 zzNMIgKr``}1C-rcX?bp5HnP>n`l5h>$RZFnesj((%HG>#cy3wt`|HP^U*3CeUNVq- zZ5@_$h2I>YUDuXnAL}xNqidV67y?vA&>V=f-`}3|{{B)38!JhqIau`~3l2ha0LuUq A!~g&Q diff --git a/plugins/CommonUtil/resources/inactive_search.png b/plugins/CommonUtil/resources/inactive_search.png deleted file mode 100644 index 29eaa91305b3de1ce3854f19d86780a552bff734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 755 zcmV;^k2Oj%`vu0L~h9Ndt{Z?B0N*TK5C5_$jUJj2-f3Mf; z=Q4%>h(lx3%*O9D(}M#pG|-AZmB~Y^5Kn3OGXm_qV;y8!p2Ycr{A~&Vh&eaG$uG|C z6Av0_h0HQDGp8TB=MgF`eoUj*zR2Kp69(V&kV6d|aG`-#)NJOFTW(>}f=4uY!z&#( zy!b0FYT$qi4YZzi z!U4p{@mX8WMy*7RX54w@=Gkk?VfutyFI>F1**tDmn1AarCNdTRhz$lqT^w#vqiIjf z++4ek*{XCNwS|Dwx6J)v#I?d`27J0X0K~|N-h&;@?7SR0m@dn1EiEQs_o;`$Fyb>) z{!a}>%}S)Dt6pr_b2g-Y=aI^?%}2xWn%Jm@kdeTRQJW3s_yq*>%`QLV|Bc0r1Qr_O lJMbXX`SgwQ{nsmReE`jH|7l`4W6l5o002ovPDHLkV1naFZkYf8 diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 15c2c8271d2b..87cbceb668f5 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 15.0.26228.4 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53C3AE07-D96F-4F5C-B407-4195084472CF}" ProjectSection(SolutionItems) = preProject @@ -32,8 +32,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserNotes", "UserNotes\User 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 @@ -132,14 +130,6 @@ Global {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 diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index 70040158bdf9..57d730baee46 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -24,15 +24,6 @@ #ifndef _COMMONUTIL_H #define _COMMONUTIL_H -#define COMMONUTIL_PLUGIN_NAME L"ProcessHacker.CommonUtil" -#define COMMONUTIL_INTERFACE_VERSION 1 - -typedef struct _COMMONUTIL_INTERFACE -{ - ULONG Version; -} COMMONUTIL_INTERFACE, *P_COMMONUTIL_INTERFACE; - - FORCEINLINE HICON CommonBitmapToIcon( diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index b6da5136be6d..14ebc3ff7ddd 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -177,7 +177,7 @@ BOOLEAN SetupExtractBuild( if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) { - if (!NT_SUCCESS(PhCreateDirectory(PhGetString(directoryPath)))) + if (!NT_SUCCESS(PhCreateDirectory(directoryPath))) { PhDereferenceObject(directoryPath); PhDereferenceObject(fullSetupPath); From eccf4de2845a03acb8ca2e2334e7091d404d4951 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 09:25:49 +1000 Subject: [PATCH 373/839] Fix PhDeleteDirectory access --- phlib/native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/native.c b/phlib/native.c index aeb0594d69a0..3af7fe0f68ba 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6333,7 +6333,7 @@ static BOOLEAN PhpDeleteDirectoryCallback( if (NT_SUCCESS(PhCreateFileWin32( &fileHandle, PhGetString(fullName), - FILE_GENERIC_WRITE, + FILE_GENERIC_READ | FILE_WRITE_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, FILE_OPEN, From 6776bd6df18394a340e9d4452ffb562abb861da1 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 10:55:40 +1000 Subject: [PATCH 374/839] Fix typos, Update flags, Tidy up TaskDialog code --- ProcessHacker/main.c | 2 +- ProcessHacker/mainwnd.c | 2 +- phlib/util.c | 11 +-- plugins/ExtraPlugins/plugin.c | 4 - plugins/ExtraPlugins/setup/uninstall.c | 2 +- plugins/ExtraPlugins/setup/updater.c | 129 +++---------------------- plugins/NetworkTools/nettools.h | 10 +- plugins/NetworkTools/pages.c | 13 +-- plugins/NetworkTools/update.c | 54 ++--------- plugins/Updater/page5.c | 16 ++- plugins/Updater/updater.c | 66 ++++--------- plugins/Updater/updater.h | 8 +- 12 files changed, 68 insertions(+), 249 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 498260e9c118..ca0de0fe219f 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -178,7 +178,7 @@ INT WINAPI wWinMain( if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) { - PhShowStatus(NULL, L"Unable to execute the command", status, 0); + PhShowStatus(NULL, L"Unable to execute the command.", status, 0); } RtlExitUserProcess(status); diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 184825f5cf2a..4dbb73e92430 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1867,7 +1867,7 @@ BOOLEAN PhMwpOnNotify( } else { - PhShowStatus(PhMainWndHandle, L"Unable to execute the program", status, 0); + PhShowStatus(PhMainWndHandle, L"Unable to execute the program.", status, 0); *Result = RF_RETRY; } diff --git a/phlib/util.c b/phlib/util.c index 62abe668d064..b72bef57db02 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -3262,13 +3262,13 @@ VOID PhShellExecute( 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()); } } @@ -3301,7 +3301,7 @@ 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; @@ -3388,14 +3388,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()); } } diff --git a/plugins/ExtraPlugins/plugin.c b/plugins/ExtraPlugins/plugin.c index 1bfd3d10c733..d1298c84297f 100644 --- a/plugins/ExtraPlugins/plugin.c +++ b/plugins/ExtraPlugins/plugin.c @@ -308,10 +308,6 @@ VOID EnumerateLoadedPlugins( 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)) diff --git a/plugins/ExtraPlugins/setup/uninstall.c b/plugins/ExtraPlugins/setup/uninstall.c index e7f92310551d..3f6069bae2dc 100644 --- a/plugins/ExtraPlugins/setup/uninstall.c +++ b/plugins/ExtraPlugins/setup/uninstall.c @@ -47,7 +47,7 @@ HRESULT CALLBACK TaskDialogUninstallCallbackProc( PhDereferenceObject(baseNameString); PhDereferenceObject(fileNameString); - PostMessage(context->DialogHandle, PH_UPDATENEWER, 0, 0); + ShowUninstallRestartDialog(context); return S_FALSE; } } diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c index 8a1ce61eb1d3..e6abc1fe748c 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -246,7 +246,7 @@ NTSTATUS UpdateDownloadThread( BOOLEAN downloadSuccess = FALSE; BOOLEAN hashSuccess = FALSE; BOOLEAN signatureSuccess = FALSE; - LONG updateResult = PH_UPDATEISERRORED; + BOOLEAN updateSuccess = FALSE; HANDLE tempFileHandle = NULL; HINTERNET httpSessionHandle = NULL; HINTERNET httpConnectionHandle = NULL; @@ -526,7 +526,7 @@ NTSTATUS UpdateDownloadThread( { if (NT_SUCCESS(SetupExtractBuild(context))) { - updateResult = PH_UPDATESUCCESS; + updateSuccess = TRUE; } } @@ -536,112 +536,16 @@ NTSTATUS UpdateDownloadThread( PhDereferenceObject(context->SetupFilePath); } - PostMessage(context->DialogHandle, updateResult, 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) + if (updateSuccess) { - 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 - // ); - // - // 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; + ShowInstallRestartDialog(context); + } + else + { + ShowUpdateFailedDialog(context, FALSE, FALSE); } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return STATUS_SUCCESS; } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -659,26 +563,19 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( case TDN_CREATED: { context->DialogHandle = hwndDlg; - TaskDialogCreateIcons(context); - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); + TaskDialogCreateIcons(context); switch (context->Action) { case PLUGIN_ACTION_INSTALL: - { - ShowAvailableDialog(context); - } + ShowAvailableDialog(context); break; case PLUGIN_ACTION_UNINSTALL: - { - ShowPluginUninstallDialog(context); - } + ShowPluginUninstallDialog(context); break; case PLUGIN_ACTION_RESTART: - { - ShowUninstallRestartDialog(context); - } + ShowUninstallRestartDialog(context); break; } } diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 8e46673641d2..48d232f8574b 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -106,12 +106,6 @@ typedef enum _PH_NETWORK_ACTION #define WM_TRACERT_COUNTRY (WM_APP + NETWORK_ACTION_TRACEROUTE + 1004) #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 { @@ -305,6 +299,10 @@ VOID ShowInstallRestartDialog( _In_ PPH_UPDATER_CONTEXT Context ); +VOID ShowUpdateFailedDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + // Copied from mstcpip.h due to PH sdk conflicts #define INADDR_ANY (ULONG)0x00000000 #define INADDR_LOOPBACK 0x7f000001 diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index f2bce861c8ca..b379a9213124 100644 --- a/plugins/NetworkTools/pages.c +++ b/plugins/NetworkTools/pages.c @@ -227,8 +227,7 @@ VOID ShowInstallRestartDialog( } VOID ShowUpdateFailedDialog( - _In_ PPH_UPDATER_CONTEXT Context, - _In_ BOOLEAN HashFailed + _In_ PPH_UPDATER_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -245,15 +244,7 @@ VOID ShowUpdateFailedDialog( config.pszWindowTitle = L"Network Tools - GeoIP Updater"; config.pszMainInstruction = L"Error downloading GeoIP database."; - - 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."; - } + 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 diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 8c8692b1b0ef..38924478c8b0 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -608,57 +608,20 @@ NTSTATUS GeoIPUpdateThread( PhDereferenceObject(context->SetupFilePath); } - if (success) + if (context->DialogHandle) { - if (context->DialogHandle) - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); - } - else - { - if (context->DialogHandle) - 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: + if (success) { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_SHOWDIALOG: - { - if (IsMinimized(hwndDlg)) - ShowWindow(hwndDlg, SW_RESTORE); - else - ShowWindow(hwndDlg, SW_SHOW); - - SetForegroundWindow(hwndDlg); + ShowInstallRestartDialog(context); } - break; - case PH_UPDATESUCCESS: + else { - ShowInstallRestartDialog(context); + ShowUpdateFailedDialog(context); } - break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + PhDereferenceObject(context); + return STATUS_SUCCESS; } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -683,9 +646,6 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( // Create the Taskdialog icons TaskDialogCreateIcons(context); - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - ShowCheckForUpdatesDialog(context); } break; diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 863578027588..aac9d196786f 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -70,6 +70,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( info.lpVerb = PhGetOwnTokenAttributes().Elevated ? NULL : L"runas"; info.nShow = SW_SHOW; info.hwnd = hwndDlg; + info.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI; ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); @@ -79,11 +80,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, GetLastError()); + + if (context->StartupCheck) + ShowAvailableDialog(context); + else + ShowCheckForUpdatesDialog(context); + } + return S_FALSE; } } diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 16790f4ab3ad..97fe59b5207a 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -516,6 +516,13 @@ NTSTATUS UpdateCheckSilentThread( if (!LastUpdateCheckExpired()) goto CleanupExit; #endif + + Sleep(5 * 1000); + + // Clear the application cache directory. + PhClearCacheDirectory(); + + // Query latest update information from the server. if (!QueryUpdateData(context)) goto CleanupExit; @@ -573,7 +580,7 @@ NTSTATUS UpdateCheckThread( if (!context->HaveData) { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); + ShowUpdateFailedDialog(context, FALSE, FALSE); PhDereferenceObject(context); return STATUS_SUCCESS; @@ -595,17 +602,17 @@ NTSTATUS UpdateCheckThread( if (currentVersion == latestVersion) { // User is running the latest version - PostMessage(context->DialogHandle, PH_UPDATEISCURRENT, 0, 0); + ShowLatestVersionDialog(context); } else if (currentVersion > latestVersion) { // User is running a newer version - PostMessage(context->DialogHandle, PH_UPDATENEWER, 0, 0); + ShowNewerVersionDialog(context); } else { // User is running an older version - PostMessage(context->DialogHandle, PH_UPDATEAVAILABLE, 0, 0); + ShowAvailableDialog(context); } PhDereferenceObject(context); @@ -944,15 +951,20 @@ NTSTATUS UpdateDownloadThread( { if (downloadSuccess && hashSuccess && signatureSuccess) { - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); + ShowUpdateInstallDialog(context); } else if (downloadSuccess) { - PostMessage(context->DialogHandle, PH_UPDATEFAILURE, signatureSuccess, hashSuccess); + if (signatureSuccess) + ShowUpdateFailedDialog(context, TRUE, FALSE); + else if (hashSuccess) + ShowUpdateFailedDialog(context, FALSE, TRUE); + else + ShowUpdateFailedDialog(context, FALSE, FALSE); } else { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); + ShowUpdateFailedDialog(context, FALSE, FALSE); } } @@ -988,41 +1000,6 @@ LRESULT CALLBACK TaskDialogSubclassProc( RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); } 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) @@ -1077,7 +1054,6 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( ) { PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - UpdateDialogHandle = hwndDlg; switch (uMsg) { @@ -1095,13 +1071,9 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); if (context->StartupCheck) - { ShowAvailableDialog(context); - } else - { ShowCheckForUpdatesDialog(context); - } } break; } diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index f0ecd291f096..d557abf644e9 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -39,13 +39,7 @@ #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 PH_SHOWDIALOG (WM_APP + 507) +#define PH_SHOWDIALOG (WM_APP + 501) #define PLUGIN_NAME L"ProcessHacker.UpdateChecker" #define SETTING_NAME_AUTO_CHECK (PLUGIN_NAME L".PromptStart") From e69d31ebd96c81ea38a1f99777aa3f07bf3f9848 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 11:58:02 +1000 Subject: [PATCH 375/839] ExtraPlugins: Fix crash, Fix warning dialogs --- plugins/ExtraPlugins/main.h | 21 ++----- plugins/ExtraPlugins/setup/page3.c | 2 +- plugins/ExtraPlugins/setup/page4.c | 1 + plugins/ExtraPlugins/setup/uninstall.c | 79 +++++++++++++++++++++++++- plugins/ExtraPlugins/setup/updater.c | 72 ++++++++++++++++------- 5 files changed, 136 insertions(+), 39 deletions(-) diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h index b425a322b6c3..6ccbc8adda4b 100644 --- a/plugins/ExtraPlugins/main.h +++ b/plugins/ExtraPlugins/main.h @@ -42,13 +42,7 @@ #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 WM_ACTION (WM_APP + 601) #define ID_WCTSHOWCONTEXTMENU 19584 #define PLUGIN_NAME L"dmex.ExtraPlugins" @@ -76,7 +70,7 @@ typedef enum _TREE_PLUGIN_STATE PLUGIN_STATE_LOCAL, PLUGIN_STATE_RESTART, PLUGIN_STATE_REMOTE, - PLUGIN_STATE_UPDATE, + PLUGIN_STATE_UPDATE } TREE_PLUGIN_STATE; typedef enum _WCT_TREE_COLUMN_ITEM_NAME @@ -190,7 +184,6 @@ 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, @@ -256,9 +249,6 @@ typedef struct _PH_UPDATER_CONTEXT PLUGIN_ACTION Action; PPLUGIN_NODE Node; - PPH_STRING FileDownloadUrl; - PPH_STRING RevVersion; - PPH_STRING Size; PPH_STRING SetupFilePath; } PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; @@ -274,10 +264,6 @@ BOOLEAN StartInitialCheck( ); // 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); @@ -287,6 +273,9 @@ VOID ShowUpdateFailedDialog( _In_ BOOLEAN HashFailed, _In_ BOOLEAN SignatureFailed ); +VOID ShowPluginUninstallWithoutPrompt( + _In_ PPH_UPDATER_CONTEXT Context + ); // page6.c VOID ShowInstallRestartDialog(_In_ PPH_UPDATER_CONTEXT Context); diff --git a/plugins/ExtraPlugins/setup/page3.c b/plugins/ExtraPlugins/setup/page3.c index 5db142d81c21..d81fe0e169fe 100644 --- a/plugins/ExtraPlugins/setup/page3.c +++ b/plugins/ExtraPlugins/setup/page3.c @@ -83,4 +83,4 @@ VOID ShowAvailableDialog( 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 index 566019d70dec..9f163d8c62cc 100644 --- a/plugins/ExtraPlugins/setup/page4.c +++ b/plugins/ExtraPlugins/setup/page4.c @@ -39,6 +39,7 @@ HRESULT CALLBACK ShowProgressCallbackProc( SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + PhReferenceObject(context); PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UpdateDownloadThread, context); } break; diff --git a/plugins/ExtraPlugins/setup/uninstall.c b/plugins/ExtraPlugins/setup/uninstall.c index 3f6069bae2dc..17da99493fbd 100644 --- a/plugins/ExtraPlugins/setup/uninstall.c +++ b/plugins/ExtraPlugins/setup/uninstall.c @@ -47,7 +47,11 @@ HRESULT CALLBACK TaskDialogUninstallCallbackProc( PhDereferenceObject(baseNameString); PhDereferenceObject(fileNameString); - ShowUninstallRestartDialog(context); + if (PhGetIntegerSetting(L"EnableWarnings")) + ShowUninstallRestartDialog(context); + else + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + return S_FALSE; } } @@ -77,5 +81,78 @@ VOID ShowPluginUninstallDialog( config.pButtons = TaskDialogButtonArray; config.cButtons = ARRAYSIZE(TaskDialogButtonArray); + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + +HRESULT CALLBACK TaskDialogUninstallWithoutPromptCallback( + _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: + { + 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"); + + //PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupExtractBuild, context); + if (RtlDoesFileExists_U(PhGetString(fileNameString))) + { + MoveFileEx(PhGetString(fileNameString), PhGetString(bakNameString), MOVEFILE_REPLACE_EXISTING); + } + + PhDereferenceObject(bakNameString); + PhDereferenceObject(baseNameString); + PhDereferenceObject(fileNameString); + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + ShowUninstallRestartDialog(context); + } + { + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + } + + return S_FALSE; + } + break; + } + + return S_OK; +} + +VOID ShowPluginUninstallWithoutPrompt( + _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"Uninstalling %s...", PhIsNullOrEmptyString(Context->Node->Name) ? PhGetStringOrEmpty(Context->Node->InternalName) : PhGetStringOrEmpty(Context->Node->Name))->Buffer; + config.dwFlags = TDF_USE_HICON_MAIN | TDF_CAN_BE_MINIMIZED | TDF_SHOW_MARQUEE_PROGRESS_BAR; + config.dwCommonButtons = TDCBF_CLOSE_BUTTON; + config.hMainIcon = context->IconLargeHandle; + config.pfCallback = TaskDialogUninstallWithoutPromptCallback; + 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 index e6abc1fe748c..25fbd3ddb1f8 100644 --- a/plugins/ExtraPlugins/setup/updater.c +++ b/plugins/ExtraPlugins/setup/updater.c @@ -43,15 +43,6 @@ 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); } @@ -254,6 +245,7 @@ NTSTATUS UpdateDownloadThread( PPH_STRING downloadHostPath = NULL; PPH_STRING downloadUrlPath = NULL; PPH_STRING userAgentString = NULL; + PPH_STRING fileDownloadUrl = NULL; PUPDATER_HASH_CONTEXT hashContext = NULL; ULONG indexOfFileName = -1; URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; @@ -286,30 +278,46 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; } - context->FileDownloadUrl = PhFormatString( + fileDownloadUrl = PhFormatString( L"https://wj32.org/processhacker/plugins/download.php?id=%s&type=64", PhGetStringOrEmpty(context->Node->Id) ); // Set lengths to non-zero enabling these params to be cracked. - httpParts.dwSchemeLength = (ULONG)-1; - httpParts.dwHostNameLength = (ULONG)-1; - httpParts.dwUrlPathLength = (ULONG)-1; + httpParts.dwSchemeLength = ULONG_MAX; + httpParts.dwHostNameLength = ULONG_MAX; + httpParts.dwUrlPathLength = ULONG_MAX; if (!WinHttpCrackUrl( - PhGetString(context->FileDownloadUrl), + PhGetString(fileDownloadUrl), 0, 0, &httpParts )) { + PhDereferenceObject(fileDownloadUrl); goto CleanupExit; } + PhDereferenceObject(fileDownloadUrl); + // Create the Host string. - downloadHostPath = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); - // Create the Path string. - downloadUrlPath = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); + if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( + httpParts.lpszHostName, + httpParts.dwHostNameLength * sizeof(WCHAR) + ))) + { + goto CleanupExit; + } + + // Create the remote path string. + if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( + httpParts.lpszUrlPath, + httpParts.dwUrlPathLength * sizeof(WCHAR) + ))) + { + goto CleanupExit; + } SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); @@ -538,7 +546,10 @@ NTSTATUS UpdateDownloadThread( if (updateSuccess) { - ShowInstallRestartDialog(context); + if (PhGetIntegerSetting(L"EnableWarnings")) + ShowUninstallRestartDialog(context); + else + SendMessage(context->DialogHandle, WM_CLOSE, 0, 0); } else { @@ -569,13 +580,32 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( switch (context->Action) { case PLUGIN_ACTION_INSTALL: - ShowAvailableDialog(context); + { + if (PhGetIntegerSetting(L"EnableWarnings")) + ShowAvailableDialog(context); + else + ShowProgressDialog(context); + } break; case PLUGIN_ACTION_UNINSTALL: - ShowPluginUninstallDialog(context); + { + if (PhGetIntegerSetting(L"EnableWarnings")) + ShowPluginUninstallDialog(context); + else + ShowPluginUninstallWithoutPrompt(context); + } break; case PLUGIN_ACTION_RESTART: - ShowUninstallRestartDialog(context); + { + if (PhGetIntegerSetting(L"EnableWarnings")) + { + ShowUninstallRestartDialog(context); + } + else + { + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + } + } break; } } From 742c2185b6897a8ea043dac0536b0cac7c498b23 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 11:58:48 +1000 Subject: [PATCH 376/839] Plugins: Tidy up winhttp parsing --- plugins/NetworkTools/update.c | 38 ++++++++++++++++++----------------- plugins/OnlineChecks/upload.c | 6 +++--- plugins/Updater/updater.c | 24 +++++++++++++--------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 38924478c8b0..2e8762359d52 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -291,7 +291,7 @@ NTSTATUS GeoIPUpdateThread( PPH_STRING fwLinkUrl = NULL; PPH_STRING downloadHostPath = NULL; PPH_STRING downloadUrlPath = NULL; - URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; + URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; ULONG64 timeTicks = 0; @@ -325,35 +325,37 @@ 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; + httpParts.dwSchemeLength = ULONG_MAX; + httpParts.dwHostNameLength = ULONG_MAX; + httpParts.dwUrlPathLength = ULONG_MAX; if (!WinHttpCrackUrl( fwLinkUrl->Buffer, 0, 0, - &httpUrlComponents + &httpParts )) { goto CleanupExit; } // Create the Host string. - downloadHostPath = PhCreateStringEx( - httpUrlComponents.lpszHostName, - httpUrlComponents.dwHostNameLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadHostPath)) + if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( + httpParts.lpszHostName, + httpParts.dwHostNameLength * sizeof(WCHAR) + ))) + { goto CleanupExit; + } - // Create the Path string. - downloadUrlPath = PhCreateStringEx( - httpUrlComponents.lpszUrlPath, - httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadUrlPath)) + // Create the remote path string. + if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( + httpParts.lpszUrlPath, + httpParts.dwUrlPathLength * sizeof(WCHAR) + ))) + { goto CleanupExit; + } if (!(httpSessionHandle = WinHttpOpen( PhGetString(userAgentString), @@ -379,7 +381,7 @@ NTSTATUS GeoIPUpdateThread( if (!(httpConnectionHandle = WinHttpConnect( httpSessionHandle, PhGetString(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, + httpParts.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, 0 ))) { @@ -393,7 +395,7 @@ NTSTATUS GeoIPUpdateThread( NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) + WINHTTP_FLAG_REFRESH | (httpParts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) ))) { goto CleanupExit; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 4d060c290ff0..8646b1d98eae 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -467,9 +467,9 @@ NTSTATUS UploadFileThreadStart( 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; + httpComponents.dwSchemeLength = ULONG_MAX; + httpComponents.dwHostNameLength = ULONG_MAX; + httpComponents.dwUrlPathLength = ULONG_MAX; if (!WinHttpCrackUrl( PhGetString(context->UploadUrl), diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 97fe59b5207a..abb34f2a49e7 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -671,9 +671,9 @@ NTSTATUS UpdateDownloadThread( goto CleanupExit; // Set lengths to non-zero enabling these params to be cracked. - httpParts.dwSchemeLength = (ULONG)-1; - httpParts.dwHostNameLength = (ULONG)-1; - httpParts.dwUrlPathLength = (ULONG)-1; + httpParts.dwSchemeLength = ULONG_MAX; + httpParts.dwHostNameLength = ULONG_MAX; + httpParts.dwUrlPathLength = ULONG_MAX; if (!WinHttpCrackUrl( PhGetStringOrEmpty(context->SetupFileDownloadUrl), @@ -687,16 +687,22 @@ NTSTATUS UpdateDownloadThread( } // Create the Host string. - downloadHostPath = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); - - if (PhIsNullOrEmptyString(downloadHostPath)) + if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( + httpParts.lpszHostName, + httpParts.dwHostNameLength * sizeof(WCHAR) + ))) + { goto CleanupExit; + } // Create the remote path string. - downloadUrlPath = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); - - if (PhIsNullOrEmptyString(downloadUrlPath)) + if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( + httpParts.lpszUrlPath, + httpParts.dwUrlPathLength * sizeof(WCHAR) + ))) + { goto CleanupExit; + } // Create the local path string. context->SetupFilePath = UpdaterParseDownloadFileName(downloadUrlPath); From 5985b12758561a2702829f4ccaffe03fb4a60666 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 11 Aug 2017 13:51:59 +1000 Subject: [PATCH 377/839] Update process module tab options, Update headers --- ProcessHacker/include/modlist.h | 12 +++++++- ProcessHacker/modlist.c | 22 +++++++++++--- ProcessHacker/modprv.c | 11 +++---- ProcessHacker/prpgmod.c | 51 ++++++++++++++++++++++++++------- phnt/include/ntwow64.h | 1 + plugins/ExtraPlugins/main.c | 2 +- 6 files changed, 77 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/include/modlist.h b/ProcessHacker/include/modlist.h index 1e9edb4150f1..1faef45ef001 100644 --- a/ProcessHacker/include/modlist.h +++ b/ProcessHacker/include/modlist.h @@ -60,6 +60,12 @@ typedef struct _PH_MODULE_NODE #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 typedef struct _PH_MODULE_LIST_CONTEXT { @@ -80,7 +86,11 @@ typedef struct _PH_MODULE_LIST_CONTEXT ULONG HideMappedModules : 1; ULONG HideSignedModules : 1; ULONG HideStaticModules : 1; - ULONG Spare : 27; + ULONG HighlightUntrustedModules : 1; + ULONG HighlightDotNetModules : 1; + ULONG HighlightImmersiveModules : 1; + ULONG HighlightRelocatedModules : 1; + ULONG Spare : 23; }; }; diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 1e77f5273cd8..2b4285549d63 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -219,6 +219,18 @@ VOID PhSetOptionsModuleList( 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; } } @@ -870,14 +882,16 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( if (!moduleItem) ; // Dummy - else if (PhCsUseColorDotNet && (moduleItem->Flags & LDRP_COR_IMAGE)) + else if (context->HighlightUntrustedModules && moduleItem->VerifyResult != VrTrusted) + getNodeColor->BackColor = PhCsColorPacked; + else if (context->HighlightDotNetModules && (moduleItem->Flags & LDRP_COR_IMAGE)) getNodeColor->BackColor = PhCsColorDotNet; - else if (PhCsUseColorImmersiveProcesses && (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER)) + else if (context->HighlightImmersiveModules && (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER)) getNodeColor->BackColor = PhCsColorImmersiveProcesses; - else if (PhCsUseColorRelocatedModules && (moduleItem->Flags & LDRP_IMAGE_NOT_AT_BASE)) + else if (context->HighlightRelocatedModules && (moduleItem->Flags & LDRP_IMAGE_NOT_AT_BASE)) getNodeColor->BackColor = PhCsColorRelocatedModules; - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + getNodeColor->Flags = TN_AUTO_FORECOLOR; } return TRUE; case TreeNewGetNodeFont: diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 9f5f1f623ba9..659774544dbd 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -457,21 +457,22 @@ VOID PhModuleProviderUpdate( moduleItem = PhCreateModuleItem(); + PhReferenceObject(module->Name); + PhReferenceObject(module->FileName); + moduleItem->BaseAddress = module->BaseAddress; - PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); moduleItem->EntryPoint = module->EntryPoint; - PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->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; - PhReferenceObject(moduleItem->Name); moduleItem->FileName = module->FileName; - PhReferenceObject(moduleItem->FileName); + + PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); + PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->EntryPoint); PhInitializeImageVersionInfo(&moduleItem->VersionInfo, moduleItem->FileName->Buffer); diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index c803d20f97b7..9ab53e012387 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -670,29 +670,49 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PPH_EMENU_ITEM mappedItem; PPH_EMENU_ITEM staticItem; PPH_EMENU_ITEM verifiedItem; + PPH_EMENU_ITEM untrustedItem; + PPH_EMENU_ITEM dotnetItem; + PPH_EMENU_ITEM immersiveItem; + PPH_EMENU_ITEM relocatedItem; + PPH_EMENU_ITEM stringsItem; 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), -1); PhInsertEMenuItem(menu, mappedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MAPPED_OPTION, L"Hide mapped", NULL, NULL), -1); PhInsertEMenuItem(menu, staticItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_STATIC_OPTION, L"Hide static", NULL, NULL), -1); PhInsertEMenuItem(menu, verifiedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_SIGNED_OPTION, L"Hide verified", NULL, NULL), -1); - + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, dotnetItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION, L"Highlight .NET modules", NULL, NULL), -1); + PhInsertEMenuItem(menu, immersiveItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION, L"Highlight immersive modules", NULL, NULL), -1); + PhInsertEMenuItem(menu, relocatedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION, L"Highlight relocated modules", NULL, NULL), -1); + PhInsertEMenuItem(menu, untrustedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION, L"Highlight untrusted modules", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MODULE_FLAGS_LOAD_MODULE_OPTION, L"Load module", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, stringsItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MODULE_STRINGS_OPTION, L"Strings...", NULL, NULL), -1); + 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.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; + + stringsItem->Flags |= PH_EMENU_DISABLED; + selectedItem = PhShowEMenu( menu, hwndDlg, @@ -704,11 +724,20 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( if (selectedItem && selectedItem->Id) { - PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); - - PhSaveSettingsModuleList(&modulesContext->ListContext); - - PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + if (selectedItem->Id == PH_MODULE_FLAGS_LOAD_MODULE_OPTION) + { + PhReferenceObject(processItem); + PhUiInjectDllProcess(hwndDlg, processItem); + PhDereferenceObject(processItem); + } + else + { + PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); + + PhSaveSettingsModuleList(&modulesContext->ListContext); + + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + } } PhDestroyEMenu(menu); diff --git a/phnt/include/ntwow64.h b/phnt/include/ntwow64.h index 16948f927aae..4317ab0fbe9a 100644 --- a/phnt/include/ntwow64.h +++ b/phnt/include/ntwow64.h @@ -167,6 +167,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 diff --git a/plugins/ExtraPlugins/main.c b/plugins/ExtraPlugins/main.c index ed2be884c1f1..e9a8aff8c18c 100644 --- a/plugins/ExtraPlugins/main.c +++ b/plugins/ExtraPlugins/main.c @@ -91,7 +91,7 @@ BOOLEAN PluginsCleanupDirectoryCallback( FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { - UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*"); + static UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*"); PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, PluginsCleanupDirectoryCallback, PluginsDirectoryPath); NtClose(pluginsDirectoryHandle); From 4b5ee673b8d7b5e638a5732eb3e44c4a8e6f0d84 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 12 Aug 2017 18:07:56 +1000 Subject: [PATCH 378/839] Fix empty process handle tab when running non-official builds --- ProcessHacker/hndlprv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 8b7707c0f154..4afd571c33c9 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -306,7 +306,7 @@ NTSTATUS PhEnumHandlesGeneric( // * Otherwise, NtQuerySystemInformation with SystemHandleInformation // can be used. - if (KphIsConnected()) + if (KphIsConnected() && KphIsVerified()) { PKPH_PROCESS_HANDLE_INFORMATION handles; PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; From d356b10289791bb5472291ae2eff50baa2d3f4b4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 12 Aug 2017 18:45:23 +1000 Subject: [PATCH 379/839] Convert main Users menu to emenu --- ProcessHacker/mainwnd.c | 90 ++++++++++------------------------------- 1 file changed, 21 insertions(+), 69 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 4dbb73e92430..29785212bb3c 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -89,7 +89,6 @@ 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; @@ -319,11 +318,6 @@ LRESULT CALLBACK PhMwpWndProc( return result; } break; - case WM_WTSSESSION_CHANGE: - { - PhMwpOnWtsSessionChange((ULONG)wParam, (ULONG)lParam); - } - break; } if (uMsg >= WM_PH_FIRST && uMsg <= WM_PH_LAST) @@ -485,9 +479,6 @@ 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. @@ -1666,18 +1657,6 @@ VOID PhMwpOnInitMenuPopup( 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)) ; @@ -1693,9 +1672,16 @@ VOID PhMwpOnInitMenuPopup( SetMenuInfo(Menu, &menuInfo); menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND), Index); - PhMwpInitializeSubMenu(menu, Index); + if (Index == 3) // Special case for Users menu. + { + PhMwpUpdateUsersMenu(menu); + } + else + { + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND), Index); + PhMwpInitializeSubMenu(menu, Index); + } if (PhPluginsEnabled) { @@ -1878,20 +1864,6 @@ BOOLEAN PhMwpOnNotify( 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, @@ -2557,7 +2529,14 @@ VOID PhMwpDispatchMenuCommand( case ID_USER_SENDMESSAGE: case ID_USER_PROPERTIES: { - SelectedUserSessionId = (ULONG)ItemData; + PPH_EMENU_ITEM menuItem; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + if (menuItem && menuItem->Parent) + { + SelectedUserSessionId = PtrToUlong(menuItem->Parent->Context); + } } break; } @@ -3561,31 +3540,22 @@ BOOLEAN PhMwpPluginNotifyEvent( } VOID PhMwpUpdateUsersMenu( - VOID + _In_ PPH_EMENU UsersMenu ) { - 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_EMENU_ITEM userMenu; PPH_STRING menuText; PPH_STRING escapedMenuText; WINSTATIONINFORMATION winStationInfo; ULONG returnLength; - ULONG numberOfItems; if (!WinStationQueryInformationW( NULL, @@ -3615,30 +3585,12 @@ VOID PhMwpUpdateUsersMenu( 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 - ); + PhInsertEMenuItem(UsersMenu, userMenu = PhCreateEMenuItem(0, IDR_USER, escapedMenuText->Buffer, NULL, UlongToPtr(sessions[i].SessionId)), -1); + PhLoadResourceEMenuItem(userMenu, PhInstanceHandle, MAKEINTRESOURCE(IDR_USER), 0); 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); } From 568d992142f351112d33394ddd81cf8fc99611ce Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 12 Aug 2017 18:46:55 +1000 Subject: [PATCH 380/839] Add missing file from previous commit --- ProcessHacker/include/mainwndp.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index ef1b7c8ea106..b2a82e0a7e77 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -119,11 +119,6 @@ BOOLEAN PhMwpOnNotify( _Out_ LRESULT *Result ); -VOID PhMwpOnWtsSessionChange( - _In_ ULONG Reason, - _In_ ULONG SessionId - ); - ULONG_PTR PhMwpOnUserMessage( _In_ ULONG Message, _In_ ULONG_PTR WParam, @@ -506,7 +501,7 @@ VOID PhMwpOnNetworkItemsUpdated( // Users VOID PhMwpUpdateUsersMenu( - VOID + _In_ PPH_EMENU UsersMenu ); #endif From 8003e87b27e98ac13bc86f9ef37e989ebe14f7e4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 12 Aug 2017 21:41:53 +1000 Subject: [PATCH 381/839] Fix showing thread stack window after an error --- ProcessHacker/thrdstk.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 9674e6531cce..b63a2f5c2619 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -807,7 +807,11 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( if (status == STATUS_ABANDONED) EndDialog(hwndDlg, IDCANCEL); else if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); + { + // 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: @@ -850,7 +854,7 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( if (!NT_SUCCESS(status = PhpRefreshThreadStack(hwndDlg, context))) { - PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); + PhShowStatus(hwndDlg, L"Unable to refresh the stack.", status, 0); } } break; From 73adea9a09928457970deee990344d65e8230506 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 13 Aug 2017 13:38:08 +1000 Subject: [PATCH 382/839] Add PhGetExpandStringSetting macro --- phlib/include/settings.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/phlib/include/settings.h b/phlib/include/settings.h index 5fadd004d4b8..7cd07eb07b1d 100644 --- a/phlib/include/settings.h +++ b/phlib/include/settings.h @@ -95,6 +95,20 @@ 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 From 1eeabea3000b0a61e12522af0dcadfa962da96bc Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 13 Aug 2017 15:32:48 +1000 Subject: [PATCH 383/839] NetworkTools: Add setting for the geoip database path --- plugins/NetworkTools/country.c | 11 ++++++++--- plugins/NetworkTools/main.c | 1 + plugins/NetworkTools/nettools.h | 2 +- plugins/NetworkTools/update.c | 15 ++++++++++----- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/plugins/NetworkTools/country.c b/plugins/NetworkTools/country.c index f4d0ff2e3078..9ab6f6ea8203 100644 --- a/plugins/NetworkTools/country.c +++ b/plugins/NetworkTools/country.c @@ -31,11 +31,14 @@ VOID LoadGeoLiteDb( VOID ) { - PPH_STRING path; + PPH_STRING dbpath; - path = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); + dbpath = PhGetExpandStringSetting(SETTING_NAME_DB_LOCATION); - if (MMDB_open(PhGetString(path), MMDB_MODE_MMAP, &GeoDb) == MMDB_SUCCESS) + if (PhIsNullOrEmptyString(dbpath)) + PhMoveReference(&dbpath, PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); + + if (MMDB_open(PhGetString(dbpath), MMDB_MODE_MMAP, &GeoDb) == MMDB_SUCCESS) { time_t systemTime; @@ -55,6 +58,8 @@ VOID LoadGeoLiteDb( GeoDbLoaded = TRUE; } + + PhDereferenceObject(dbpath); } VOID FreeGeoLiteDb( diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 1542ea7612e5..4c75f6c05769 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -597,6 +597,7 @@ LOGICAL DllMain( { 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" }, }; PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 48d232f8574b..cc52b6e22c4c 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -45,7 +45,7 @@ #include "resource.h" #define PLUGIN_NAME L"ProcessHacker.NetworkTools" -#define SETTING_NAME_DB_TYPE (PLUGIN_NAME L".GeoIpType") +#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") diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 2e8762359d52..cce08c067af9 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -520,16 +520,21 @@ NTSTATUS GeoIPUpdateThread( } { - PPH_STRING gzpath; + PPH_STRING dbpath; PPH_BYTES mmdbGzPath; gzFile gzfile; - gzpath = PH_AUTO(PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); + dbpath = PhGetExpandStringSetting(SETTING_NAME_DB_LOCATION); + + if (PhIsNullOrEmptyString(dbpath)) + PhMoveReference(&dbpath, PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); + + PhAutoDereferenceObject(dbpath); mmdbGzPath = PH_AUTO(PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath))); - if (RtlDoesFileExists_U(PhGetString(gzpath))) + if (RtlDoesFileExists_U(PhGetString(dbpath))) { - if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(gzpath)))) + if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(dbpath)))) goto CleanupExit; } @@ -539,7 +544,7 @@ NTSTATUS GeoIPUpdateThread( if (NT_SUCCESS(PhCreateFileWin32( &mmdbFileHandle, - PhGetStringOrEmpty(gzpath), + PhGetStringOrEmpty(dbpath), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, From f31c0d9063b206d114672d99e4537e39a6d5047f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 13 Aug 2017 15:33:12 +1000 Subject: [PATCH 384/839] Remove unused export --- ProcessHacker/ProcessHacker.def | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index d9ec3b6a127b..3cd1060436f0 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -639,7 +639,6 @@ EXPORTS PhGetJsonObjectAsArrayList ; cache - PhGetCacheDirectory PhClearCacheDirectory PhCreateCacheFile PhDeleteCacheFile From b198b9d80434a4847ec4d97c79cf8375cec7794f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 13 Aug 2017 20:12:45 +1000 Subject: [PATCH 385/839] Fix network tab sorting --- ProcessHacker/netlist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index 6c416a97f978..8845df4fcb40 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -454,15 +454,15 @@ BOOLEAN NTAPI PhpNetworkTreeNewCallback( { 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) + SORT_FUNCTION(TimeStamp), + SORT_FUNCTION(LocalHostname), + SORT_FUNCTION(RemoteHostname) }; int (__cdecl *sortFunction)(const void *, const void *); From 93ffa22de3e7364f4925ba68b33950d96d33fce9 Mon Sep 17 00:00:00 2001 From: Koby Kahane Date: Sun, 13 Aug 2017 18:08:17 +0300 Subject: [PATCH 386/839] Show restricting SIDs when displaying the access token property page. (#167) --- ProcessHacker/include/phapp.h | 3 +- ProcessHacker/tokprp.c | 126 +++++++++++++++++++++------------- phlib/include/phnative.h | 8 +++ phlib/native.c | 19 +++++ 4 files changed, 109 insertions(+), 47 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 82099e4e0225..aaf3ed2325b6 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -789,7 +789,8 @@ VOID PhShowThreadStackDialog( // tokprp PPH_STRING PhGetGroupAttributesString( - _In_ ULONG Attributes + _In_ ULONG Attributes, + _In_ BOOLEAN Restricted ); PWSTR PhGetPrivilegeAttributesString( diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 24cb7c718740..e3a06197efca 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -54,6 +54,7 @@ typedef struct _TOKEN_PAGE_CONTEXT HWND PrivilegesListViewHandle; PTOKEN_GROUPS Groups; + PTOKEN_GROUPS RestrictedSids; PTOKEN_PRIVILEGES Privileges; PTOKEN_GROUPS Capabilities; @@ -204,7 +205,8 @@ INT CALLBACK PhpTokenPropPageProc( } PPH_STRING PhGetGroupAttributesString( - _In_ ULONG Attributes + _In_ ULONG Attributes, + _In_ BOOLEAN Restricted ) { PWSTR baseString; @@ -213,40 +215,51 @@ PPH_STRING PhGetGroupAttributesString( if (Attributes & SE_GROUP_INTEGRITY) { if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - return PhCreateString(L"Integrity"); + string = PhCreateString(L"Integrity"); else - return PhCreateString(L"Integrity (disabled)"); + string = 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(); + 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 - return PhCreateString(L"Disabled"); + baseString = NULL; + + if (!baseString) + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + string = PhCreateString(L"Default enabled"); + else if (Attributes & SE_GROUP_ENABLED) + string = PhReferenceEmptyString(); + else + string = PhCreateString(L"Disabled"); + } + else + { + 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)"); + } } - 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)"); + if (Restricted) + { + PPH_STRING prefixString = string; + string = PhConcatStrings2(prefixString->Buffer, L" (restricted)"); + PhDereferenceObject(prefixString); + } return string; } @@ -332,47 +345,68 @@ PWSTR PhGetElevationTypeString( } } -BOOLEAN PhpUpdateTokenGroups( - _In_ HWND hwndDlg, - _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, +VOID PhpUpdateSidsFromTokenGroups( _In_ HWND GroupsLv, - _In_ HANDLE TokenHandle + _In_ PTOKEN_GROUPS Groups, + _In_ BOOLEAN Restricted ) { - 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++) + 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 = 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); + lvItemIndex = PhAddListViewItem(GroupsLv, MAXINT, fullName->Buffer, &Groups->Groups[i]); + attributesString = PhGetGroupAttributesString(Groups->Groups[i].Attributes, Restricted); PhSetListViewSubItem(GroupsLv, lvItemIndex, 1, attributesString->Buffer); PhDereferenceObject(attributesString); PhDereferenceObject(fullName); } } +} + +BOOLEAN PhpUpdateTokenGroups( + _In_ HWND hwndDlg, + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND GroupsLv, + _In_ HANDLE TokenHandle + ) +{ + PTOKEN_GROUPS groups; + PTOKEN_GROUPS restrictedSIDs = NULL; + + if (!NT_SUCCESS(PhGetTokenGroups(TokenHandle, &groups))) + return FALSE; + + ExtendedListView_SetRedraw(GroupsLv, FALSE); + + ListView_DeleteAllItems(GroupsLv); + + PhpUpdateSidsFromTokenGroups(GroupsLv, groups, FALSE); + + if (NT_SUCCESS(PhGetTokenRestrictedSids(TokenHandle, &restrictedSIDs))) + { + PhpUpdateSidsFromTokenGroups(GroupsLv, restrictedSIDs, TRUE); + } ExtendedListView_SortItems(GroupsLv); ExtendedListView_SetRedraw(GroupsLv, TRUE); + if (TokenPageContext->RestrictedSids) + PhFree(TokenPageContext->RestrictedSids); + + TokenPageContext->RestrictedSids = restrictedSIDs; + if (TokenPageContext->Groups) PhFree(TokenPageContext->Groups); @@ -1389,7 +1423,7 @@ INT_PTR CALLBACK PhpTokenCapabilitiesPageProc( lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, &tokenPageContext->Capabilities->Groups[i]); attributesString = PhGetGroupAttributesString( - tokenPageContext->Capabilities->Groups[i].Attributes); + tokenPageContext->Capabilities->Groups[i].Attributes, FALSE); PhSetListViewSubItem(lvHandle, lvItemIndex, 1, attributesString->Buffer); PhDereferenceObject(attributesString); diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index b9dd90ab0716..4e752ae3a347 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -347,6 +347,14 @@ PhGetTokenGroups( _Out_ PTOKEN_GROUPS *Groups ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenRestrictedSids( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_GROUPS* RestrictedSids + ); + PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/native.c b/phlib/native.c index 3af7fe0f68ba..443d259ebf02 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1868,6 +1868,25 @@ NTSTATUS PhGetTokenGroups( ); } +/** + * 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. * From cee832a3873f2c3e3135c335426f2fdc4490ee6e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 14 Aug 2017 01:30:37 +1000 Subject: [PATCH 387/839] Fix setup error during kph update --- .../CustomSetupTool/download.c | 28 +++++++++++++------ tools/CustomSetupTool/CustomSetupTool/setup.c | 5 ++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 3355a0fc99dc..d76238c99456 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -174,12 +174,10 @@ BOOLEAN SetupQueryUpdateData( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption( httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, + &(ULONG) { WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, sizeof(ULONG) ); } @@ -211,8 +209,12 @@ BOOLEAN SetupQueryUpdateData( if (WindowsVersion >= WINDOWS_7) { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG) { WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); } if (versionHeader) @@ -445,8 +447,12 @@ BOOLEAN UpdateDownloadUpdateData( if (WindowsVersion >= WINDOWS_8_1) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG) { WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); } if (!(httpConnectionHandle = WinHttpConnect( @@ -476,8 +482,12 @@ BOOLEAN UpdateDownloadUpdateData( if (WindowsVersion >= WINDOWS_7) { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG) { WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); } if (!WinHttpSendRequest( diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index b9fbfaab07be..810433cc85dc 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -328,7 +328,7 @@ VOID SetupStopService( serviceHandle = PhOpenService( ServiceName, - SERVICE_QUERY_STATUS | SERVICE_STOP + SERVICE_QUERY_STATUS | SERVICE_STOP | DELETE ); if (serviceHandle) @@ -378,6 +378,8 @@ VOID SetupStopService( } } + DeleteService(serviceHandle); + CloseServiceHandle(serviceHandle); } } @@ -422,7 +424,6 @@ BOOLEAN SetupUninstallKph( ) { SetupStopService(L"KProcessHacker2"); - SetupStopService(L"KProcessHacker3"); return TRUE; From be69ab36cacba81699dc8e02c62bf0259d1b038a Mon Sep 17 00:00:00 2001 From: Koby Kahane Date: Sun, 13 Aug 2017 19:49:58 +0300 Subject: [PATCH 388/839] Free token restricted SIDs when destroying the token property page. (#169) --- ProcessHacker/tokprp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index e3a06197efca..97852c2c9925 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -624,6 +624,7 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSaveListViewColumnsToSetting(L"TokenPrivilegesListViewColumns", tokenPageContext->PrivilegesListViewHandle); if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); + if (tokenPageContext->RestrictedSids) PhFree(tokenPageContext->RestrictedSids); if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); } break; From fcd79e479ee5d7e3cfa76a8a19ba8900ccc868fe Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 15 Aug 2017 20:52:09 +1000 Subject: [PATCH 389/839] NetworkTools: Fix crash sorting tracert window, Fix saving tracert column and sort settings --- plugins/NetworkTools/main.c | 3 ++- plugins/NetworkTools/nettools.h | 3 ++- plugins/NetworkTools/tracetree.c | 38 +++++++++++++++++++++++++++++--- plugins/NetworkTools/update.c | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 4c75f6c05769..fa5dc23efcc1 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -593,7 +593,8 @@ LOGICAL DllMain( { 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_LIST_COLUMNS, L"" }, + { 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" }, diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index cc52b6e22c4c..b636a0bc7091 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -53,7 +53,8 @@ #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_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") diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 1cf94b79f2ac..9327858f7434 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -72,15 +72,37 @@ BEGIN_SORT_FUNCTION(Ping4) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(Country) +{ + sortResult = PhCompareStringWithNull(node1->CountryString, node2->CountryString, TRUE); +} +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 + 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 +110,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( @@ -268,6 +298,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 *); @@ -549,7 +582,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); diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index cce08c067af9..b88d78143f68 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -675,7 +675,7 @@ NTSTATUS GeoIPUpdateDialogThread( context = (PPH_UPDATER_CONTEXT)PhCreateAlloc(sizeof(PH_UPDATER_CONTEXT)); memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); - 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; From 76a4dac2e62f6e748bb95b7c3780ffc730056acb Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 15 Aug 2017 23:07:30 +1000 Subject: [PATCH 390/839] Add name column to process threads tab (e.g. show Chrome thread names) --- ProcessHacker/include/thrdlist.h | 3 +- ProcessHacker/include/thrdprv.h | 2 ++ ProcessHacker/thrdlist.c | 20 ++++++++++++- ProcessHacker/thrdprv.c | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/include/thrdlist.h b/ProcessHacker/include/thrdlist.h index f1132eb4549b..c17f97f69e73 100644 --- a/ProcessHacker/include/thrdlist.h +++ b/ProcessHacker/include/thrdlist.h @@ -12,8 +12,9 @@ #define PHTHTLC_STARTADDRESS 3 #define PHTHTLC_PRIORITY 4 #define PHTHTLC_SERVICE 5 +#define PHTHTLC_NAME 6 -#define PHTHTLC_MAXIMUM 6 +#define PHTHTLC_MAXIMUM 7 // begin_phapppub typedef struct _PH_THREAD_NODE diff --git a/ProcessHacker/include/thrdprv.h b/ProcessHacker/include/thrdprv.h index ef6fe42f6cfa..971f06779e3d 100644 --- a/ProcessHacker/include/thrdprv.h +++ b/ProcessHacker/include/thrdprv.h @@ -36,6 +36,8 @@ typedef struct _PH_THREAD_ITEM BOOLEAN JustResolved; WCHAR ThreadIdString[PH_INT32_STR_LEN_1]; + + PPH_STRING ThreadName; } PH_THREAD_ITEM, *PPH_THREAD_ITEM; typedef enum _PH_KNOWN_PROCESS_TYPE PH_KNOWN_PROCESS_TYPE; diff --git a/ProcessHacker/thrdlist.c b/ProcessHacker/thrdlist.c index 37ad2e9c7698..7d3bcd1d128e 100644 --- a/ProcessHacker/thrdlist.c +++ b/ProcessHacker/thrdlist.c @@ -101,6 +101,7 @@ VOID PhInitializeThreadList( 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); + PhAddTreeNewColumn(hwnd, PHTHTLC_NAME, FALSE, L"Name", 100, PH_ALIGN_LEFT, -1, 0); TreeNew_SetRedraw(hwnd, TRUE); @@ -166,6 +167,13 @@ VOID PhLoadSettingsThreadList( TreeNew_SetColumn(Context->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column); } + if (WindowsVersion >= WINDOWS_10_RS1) + { + column.Id = PHTHTLC_NAME; + 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); @@ -417,6 +425,12 @@ BEGIN_SORT_FUNCTION(Service) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareStringWithNull(threadItem1->ThreadName, threadItem2->ThreadName, TRUE); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpThreadTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -448,7 +462,8 @@ BOOLEAN NTAPI PhpThreadTreeNewCallback( SORT_FUNCTION(CyclesDelta), SORT_FUNCTION(StartAddress), SORT_FUNCTION(Priority), - SORT_FUNCTION(Service) + SORT_FUNCTION(Service), + SORT_FUNCTION(Name) }; int (__cdecl *sortFunction)(void *, const void *, const void *); @@ -564,6 +579,9 @@ BOOLEAN NTAPI PhpThreadTreeNewCallback( case PHTHTLC_SERVICE: getCellText->Text = PhGetStringRef(threadItem->ServiceName); break; + case PHTHTLC_NAME: + getCellText->Text = PhGetStringRef(threadItem->ThreadName); + break; default: return FALSE; } diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 3be1e25a73e2..6eb750985a73 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -49,6 +49,7 @@ typedef struct _PH_THREAD_QUERY_DATA PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel; PPH_STRING ServiceName; + PPH_STRING ThreadName; } PH_THREAD_QUERY_DATA, *PPH_THREAD_QUERY_DATA; typedef struct _PH_THREAD_SYMBOL_LOAD_CONTEXT @@ -198,6 +199,7 @@ VOID PhpThreadProviderDeleteProcedure( PhClearReference(&data->StartAddressString); PhClearReference(&data->ServiceName); + PhClearReference(&data->ThreadName); PhDereferenceObject(data->ThreadItem); PhFree(data); } @@ -412,6 +414,7 @@ VOID PhpThreadItemDeleteProcedure( if (threadItem->StartAddressString) PhDereferenceObject(threadItem->StartAddressString); if (threadItem->StartAddressFileName) PhDereferenceObject(threadItem->StartAddressFileName); if (threadItem->ServiceName) PhDereferenceObject(threadItem->ServiceName); + if (threadItem->ThreadName) PhDereferenceObject(threadItem->ThreadName); } BOOLEAN PhpThreadHashtableEqualFunction( @@ -577,6 +580,52 @@ NTSTATUS PhpThreadQueryWorker( } } + // Get the thread name (Windows 10 only). + + if (data->ThreadItem->ThreadHandle && WindowsVersion >= WINDOWS_10_RS1) + { + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG returnLength; + PUNICODE_STRING unicodeString; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationThread( + data->ThreadItem->ThreadHandle, + ThreadNameInformation, + buffer, + bufferSize, + &returnLength + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationThread( + data->ThreadItem->ThreadHandle, + ThreadNameInformation, + buffer, + bufferSize, + &returnLength + ); + } + + if (NT_SUCCESS(status)) + { + unicodeString = (PUNICODE_STRING)buffer; + + data->ThreadName = PhCreateStringFromUnicodeString(unicodeString); + } + + PhFree(buffer); + } + Done: RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry); PhDereferenceObject(data->ThreadProvider); @@ -826,6 +875,7 @@ VOID PhpThreadProviderUpdate( } PhMoveReference(&data->ThreadItem->ServiceName, data->ServiceName); + PhMoveReference(&data->ThreadItem->ThreadName, data->ThreadName); data->ThreadItem->JustResolved = TRUE; From 9523abccf351d4ddbfd6e6972c1d0508d79bd733 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 00:15:47 +1000 Subject: [PATCH 391/839] Add verification/signer columns to services tab --- ProcessHacker/srvlist.c | 30 +++++++++++- ProcessHacker/srvprv.c | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index eb10d23e993a..4f85a62bda1d 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -138,6 +139,8 @@ VOID PhInitializeServiceTreeList( 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); + PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, -1, 0); TreeNew_SetRedraw(hwnd, TRUE); @@ -528,6 +531,22 @@ BEGIN_SORT_FUNCTION(KeyModifiedTime) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(VerificationStatus) +{ + sortResult = intcmp(serviceItem1->VerifyResult, serviceItem2->VerifyResult); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerifiedSigner) +{ + sortResult = PhCompareStringWithNull( + serviceItem1->VerifySignerName, + serviceItem2->VerifySignerName, + TRUE + ); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpServiceTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -561,7 +580,9 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( SORT_FUNCTION(ErrorControl), SORT_FUNCTION(Group), SORT_FUNCTION(Description), - SORT_FUNCTION(KeyModifiedTime) + SORT_FUNCTION(KeyModifiedTime), + SORT_FUNCTION(VerificationStatus), + SORT_FUNCTION(VerifiedSigner) }; int (__cdecl *sortFunction)(const void *, const void *); @@ -674,6 +695,13 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( 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; default: return FALSE; } diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 70dc41e6b684..3e638fb589d7 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -3,6 +3,7 @@ * service provider * * Copyright (C) 2009-2015 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -22,6 +23,7 @@ #include #include +#include #include #include @@ -99,6 +101,16 @@ static HANDLE PhpNonPollEventHandle; static PH_QUEUED_LOCK PhpNonPollServiceListLock = PH_QUEUED_LOCK_INIT; static LIST_ENTRY PhpNonPollServiceListHead; static LIST_ENTRY PhpNonPollServicePendingListHead; +static SLIST_HEADER PhpServiceQueryListHead; + +typedef struct _PH_SERVICE_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + PPH_SERVICE_ITEM ServiceItem; + + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; +} PH_SERVICE_QUERY_DATA, *PPH_SERVICE_QUERY_DATA; BOOLEAN PhServiceProviderInitialization( VOID @@ -112,6 +124,8 @@ BOOLEAN PhServiceProviderInitialization( 40 ); + RtlInitializeSListHead(&PhpServiceQueryListHead); + return TRUE; } @@ -441,6 +455,60 @@ static ULONG PhpHashServiceNameEntry( return PhHashStringRef(&Value->Name, TRUE); } +NTSTATUS PhpServiceQueryWorker( + _In_ PVOID Parameter + ) +{ + PPH_SERVICE_QUERY_DATA data = (PPH_SERVICE_QUERY_DATA)Parameter; + SC_HANDLE serviceHandle; + PPH_STRING serviceFileName; + + if (serviceHandle = PhOpenService(data->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + serviceFileName = PhGetServiceRelevantFileName(&data->ServiceItem->Name->sr, serviceHandle); + CloseServiceHandle(serviceHandle); + } + + if (serviceFileName) + { + data->VerifyResult = PhVerifyFileCached( + serviceFileName, + NULL, + &data->VerifySignerName, + FALSE + ); + PhDereferenceObject(serviceFileName); + } + + RtlInterlockedPushEntrySList(&PhpServiceQueryListHead, &data->ListEntry); + + return STATUS_SUCCESS; +} + +VOID PhpQueueServiceQuery( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PPH_SERVICE_QUERY_DATA data; + PH_WORK_QUEUE_ENVIRONMENT environment; + + if (!PhEnableProcessQueryStage2) // Reuse the same setting for convenience. + return; + + data = PhAllocate(sizeof(PH_SERVICE_QUERY_DATA)); + memset(data, 0, sizeof(PH_SERVICE_QUERY_DATA)); + data->ServiceItem = ServiceItem; + + PhReferenceObject(ServiceItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + environment.IoPriority = IoPriorityLow; + environment.PagePriority = MEMORY_PRIORITY_LOW; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryWorker, data, NULL, &environment); +} + VOID PhServiceProviderUpdate( _In_ PVOID Object ) @@ -523,6 +591,27 @@ VOID PhServiceProviderUpdate( ); } + // Go through the queued services query data. + { + PSLIST_ENTRY entry; + PPH_SERVICE_QUERY_DATA data; + + entry = RtlInterlockedFlushSList(&PhpServiceQueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_SERVICE_QUERY_DATA, ListEntry); + entry = entry->Next; + + data->ServiceItem->VerifyResult = data->VerifyResult; + data->ServiceItem->VerifySignerName = data->VerifySignerName; + data->ServiceItem->JustProcessed = TRUE; + + PhDereferenceObject(data->ServiceItem); + PhFree(data); + } + } + // Look for dead services. { PPH_LIST servicesToRemove = NULL; @@ -644,6 +733,9 @@ VOID PhServiceProviderUpdate( } } + // Queue for verification. + PhpQueueServiceQuery(serviceItem); + // Add the service item to the hashtable. PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); PhAddEntryHashtable(PhServiceHashtable, &serviceItem); @@ -654,6 +746,8 @@ VOID PhServiceProviderUpdate( } else { + BOOLEAN modified = FALSE; + if (WindowsVersion >= WINDOWS_10_RS2) { // https://github.com/processhacker2/processhacker/issues/120 @@ -663,7 +757,13 @@ VOID PhServiceProviderUpdate( serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; } + if (serviceItem->JustProcessed) + modified = TRUE; + + serviceItem->JustProcessed = FALSE; + if ( + modified || serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || serviceItem->ControlsAccepted != serviceEntry->ServiceStatusProcess.dwControlsAccepted || From 7879df7be87857aa1d25a3d66e9739809b682bae Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 00:16:42 +1000 Subject: [PATCH 392/839] Add missing files from previous commit --- ProcessHacker/include/srvlist.h | 4 +++- ProcessHacker/include/srvprv.h | 23 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/include/srvlist.h b/ProcessHacker/include/srvlist.h index 499823274b3b..ea73a2824be3 100644 --- a/ProcessHacker/include/srvlist.h +++ b/ProcessHacker/include/srvlist.h @@ -17,8 +17,10 @@ #define PHSVTLC_GROUP 8 #define PHSVTLC_DESCRIPTION 9 #define PHSVTLC_KEYMODIFIEDTIME 10 +#define PHSVTLC_VERIFICATIONSTATUS 11 +#define PHSVTLC_VERIFIEDSIGNER 12 -#define PHSVTLC_MAXIMUM 11 +#define PHSVTLC_MAXIMUM 13 #define PHSN_CONFIG 0x1 #define PHSN_DESCRIPTION 0x2 diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index a01349d8005e..a30b1b6f148f 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -11,6 +11,8 @@ 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 @@ -28,13 +30,26 @@ 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; + }; + }; + + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; WCHAR ProcessIdString[PH_INT32_STR_LEN_1]; + // begin_phapppub } PH_SERVICE_ITEM, *PPH_SERVICE_ITEM; // end_phapppub From fb9156a70498ca228ace92cf35b0d412ad726af9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 00:46:51 +1000 Subject: [PATCH 393/839] Add winsta.dll to linker delayload --- ProcessHacker/ProcessHacker.vcxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 0be944a9791b..1b6219b26070 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -102,7 +102,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -133,7 +133,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -173,7 +173,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -211,7 +211,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) From 24855cd80a933ddf40095e3dd7d8de2e5b082b60 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 00:54:35 +1000 Subject: [PATCH 394/839] NetworkTools: Patch manminddb.c and disable unused winsock functionality --- plugins/NetworkTools/maxminddb/maxminddb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/NetworkTools/maxminddb/maxminddb.c b/plugins/NetworkTools/maxminddb/maxminddb.c index f5e8d66cb850..24979674a53f 100644 --- a/plugins/NetworkTools/maxminddb/maxminddb.c +++ b/plugins/NetworkTools/maxminddb/maxminddb.c @@ -248,8 +248,8 @@ int MMDB_open(const wchar_t* const filename, uint32_t flags, MMDB_s *const mmdb) } #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; @@ -1845,7 +1845,7 @@ LOCAL void free_mmdb_struct(MMDB_s *const mmdb) UnmapViewOfFile(mmdb->file_content); /* Winsock is only initialized if open was successful so we only have * to cleanup then. */ - WSACleanup(); + //WSACleanup(); // dmex: disabled since hostname lookup is not used. #else munmap((void *)mmdb->file_content, mmdb->file_size); #endif From 54e8457208ddd62047f6c324520ab1d4f45e3b4a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 00:55:26 +1000 Subject: [PATCH 395/839] Fix typo --- phlib/native.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 443d259ebf02..4b99fe5466c5 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1108,10 +1108,7 @@ NTSTATUS PhGetProcessMappedFileName( } unicodeString = (PUNICODE_STRING)buffer; - *FileName = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); + *FileName = PhCreateStringFromUnicodeString(unicodeString); PhFree(buffer); return status; @@ -2703,10 +2700,7 @@ NTSTATUS PhGetDriverName( ))) return status; - *Name = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); + *Name = PhCreateStringFromUnicodeString(unicodeString); PhFree(unicodeString); return status; @@ -2736,10 +2730,7 @@ NTSTATUS PhGetDriverServiceKeyName( ))) return status; - *ServiceKeyName = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); + *ServiceKeyName = PhCreateStringFromUnicodeString(unicodeString); PhFree(unicodeString); return status; From 900980413e46baa4700ac7092269bcb36a7d5067 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 01:43:03 +1000 Subject: [PATCH 396/839] NetworkTools: Fix tracert crash --- plugins/NetworkTools/tracert.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index eb5d39c5a0fc..75f8e2b0d609 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -178,8 +178,13 @@ VOID TracertQueueHostLookup( &remoteCountryName )) { - PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); - PhMoveReference(&Node->RemoteCountryName, remoteCountryName); + if (Node->RemoteCountryCode) + PhDereferenceObject(Node->RemoteCountryCode); + if (Node->RemoteCountryName) + PhDereferenceObject(Node->RemoteCountryName); + + Node->RemoteCountryCode = remoteCountryCode; + Node->RemoteCountryName = remoteCountryName; } } else if (Context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) @@ -229,8 +234,13 @@ VOID TracertQueueHostLookup( &remoteCountryName )) { - PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); - PhMoveReference(&Node->RemoteCountryName, remoteCountryName); + if (Node->RemoteCountryCode) + PhDereferenceObject(Node->RemoteCountryCode); + if (Node->RemoteCountryName) + PhDereferenceObject(Node->RemoteCountryName); + + Node->RemoteCountryCode = remoteCountryCode; + Node->RemoteCountryName = remoteCountryName; } } } @@ -650,7 +660,11 @@ INT_PTR CALLBACK TracertDlgProc( switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: - DestroyWindow(hwndDlg); + { + context->Cancel = TRUE; + + DestroyWindow(hwndDlg); + } break; case IDC_REFRESH: { From e9355ac68493d80e46f2c66601b82ebcd082b551 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 01:44:06 +1000 Subject: [PATCH 397/839] Fix service verification column crash --- ProcessHacker/srvprv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 3e638fb589d7..38bb93373374 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -461,7 +461,7 @@ NTSTATUS PhpServiceQueryWorker( { PPH_SERVICE_QUERY_DATA data = (PPH_SERVICE_QUERY_DATA)Parameter; SC_HANDLE serviceHandle; - PPH_STRING serviceFileName; + PPH_STRING serviceFileName = NULL; if (serviceHandle = PhOpenService(data->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) { From 3391c028b54dec9dc25b5772c3094e37a4b7b829 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 01:44:42 +1000 Subject: [PATCH 398/839] Fix typo --- ProcessHacker/thrdprv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 6eb750985a73..a1eb226728a6 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -588,7 +588,7 @@ NTSTATUS PhpThreadQueryWorker( PVOID buffer; ULONG bufferSize; ULONG returnLength; - PUNICODE_STRING unicodeString; + PTHREAD_NAME_INFORMATION threadNameInfo; bufferSize = 0x100; buffer = PhAllocate(bufferSize); @@ -618,9 +618,9 @@ NTSTATUS PhpThreadQueryWorker( if (NT_SUCCESS(status)) { - unicodeString = (PUNICODE_STRING)buffer; + threadNameInfo = (PTHREAD_NAME_INFORMATION)buffer; - data->ThreadName = PhCreateStringFromUnicodeString(unicodeString); + data->ThreadName = PhCreateStringFromUnicodeString(&threadNameInfo->ThreadName); } PhFree(buffer); From 5b6a2407fa99f6436d430ed7bef345f3c72dda25 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 04:33:10 +1000 Subject: [PATCH 399/839] Add MainWindowTabRestoreEnabled option --- ProcessHacker/mainwnd.c | 7 +++++++ ProcessHacker/settings.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 29785212bb3c..c8648aa0997e 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -227,6 +227,11 @@ BOOLEAN PhMainWndInitialization( if (page) PhMwpSelectPage(page->Index); } + else + { + if (PhGetIntegerSetting(L"MainWindowTabRestoreEnabled")) + PhMwpSelectPage(PhGetIntegerSetting(L"MainWindowTabRestoreIndex")); + } if (PhStartupParameters.SysInfo) PhShowSystemInformationDialog(PhStartupParameters.SysInfo->Buffer); @@ -536,6 +541,8 @@ VOID PhMwpOnDestroy( VOID ) { + PhSetIntegerSetting(L"MainWindowTabRestoreIndex", TabCtrl_GetCurSel(TabControlHandle)); + // Notify pages and plugins that we are shutting down. PhMwpNotifyAllPages(MainTabPageDestroy, NULL, NULL); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 28994dd78cbe..86a97ee41548 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -90,6 +90,8 @@ VOID PhAddDefaultSettings( 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""); From 91bd4bede0e0ed57887565183d132cf29743da1b Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 05:23:36 +1000 Subject: [PATCH 400/839] Fix service verification status column not updating the verification state --- ProcessHacker/include/srvprv.h | 2 +- ProcessHacker/srvprv.c | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index a30b1b6f148f..08d55c57457c 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -40,7 +40,7 @@ typedef struct _PH_SERVICE_ITEM BOOLEAN HasTriggers : 1; BOOLEAN PendingProcess : 1; BOOLEAN NeedsConfigUpdate : 1; - BOOLEAN JustProcessed : 1; + BOOLEAN NeedsVerifyUpdate : 1; BOOLEAN Spare : 3; }; }; diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 38bb93373374..826bdfc48796 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -256,6 +256,14 @@ VOID PhMarkNeedsConfigUpdateServiceItem( PhpNonPollGate = 1; } +VOID PhpResetServiceNonPollGate( + VOID + ) +{ + if (PhEnableServiceNonPoll) + PhpNonPollGate = 1; +} + VOID PhpRemoveServiceItem( _In_ PPH_SERVICE_ITEM ServiceItem ) @@ -477,9 +485,12 @@ NTSTATUS PhpServiceQueryWorker( &data->VerifySignerName, FALSE ); + PhDereferenceObject(serviceFileName); } + PhpResetServiceNonPollGate(); // HACK + RtlInterlockedPushEntrySList(&PhpServiceQueryListHead, &data->ListEntry); return STATUS_SUCCESS; @@ -492,7 +503,7 @@ VOID PhpQueueServiceQuery( PPH_SERVICE_QUERY_DATA data; PH_WORK_QUEUE_ENVIRONMENT environment; - if (!PhEnableProcessQueryStage2) // Reuse the same setting for convenience. + if (!PhEnableProcessQueryStage2) return; data = PhAllocate(sizeof(PH_SERVICE_QUERY_DATA)); @@ -605,7 +616,7 @@ VOID PhServiceProviderUpdate( data->ServiceItem->VerifyResult = data->VerifyResult; data->ServiceItem->VerifySignerName = data->VerifySignerName; - data->ServiceItem->JustProcessed = TRUE; + data->ServiceItem->NeedsVerifyUpdate = TRUE; PhDereferenceObject(data->ServiceItem); PhFree(data); @@ -746,8 +757,6 @@ VOID PhServiceProviderUpdate( } else { - BOOLEAN modified = FALSE; - if (WindowsVersion >= WINDOWS_10_RS2) { // https://github.com/processhacker2/processhacker/issues/120 @@ -757,18 +766,13 @@ VOID PhServiceProviderUpdate( serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; } - if (serviceItem->JustProcessed) - modified = TRUE; - - serviceItem->JustProcessed = FALSE; - if ( - modified || serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || serviceItem->ControlsAccepted != serviceEntry->ServiceStatusProcess.dwControlsAccepted || serviceItem->ProcessId != UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId) || - serviceItem->NeedsConfigUpdate + serviceItem->NeedsConfigUpdate || + serviceItem->NeedsVerifyUpdate ) { PH_SERVICE_MODIFIED_DATA serviceModifiedData; @@ -862,6 +866,9 @@ VOID PhServiceProviderUpdate( serviceItem->NeedsConfigUpdate = FALSE; } + if (serviceItem->NeedsVerifyUpdate) // HACK + serviceItem->NeedsVerifyUpdate = FALSE; + // Raise the service modified event. PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); } From 99be9c0401880c4147f09b1d691f4b71f794adcb Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 17 Aug 2017 05:54:53 +1000 Subject: [PATCH 401/839] Update begin_phapppub --- ProcessHacker/include/srvprv.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index 08d55c57457c..ea4ca015cc4e 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -44,13 +44,11 @@ typedef struct _PH_SERVICE_ITEM 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 From 398040837b840beea801099a28511ef1e0979871 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 18 Aug 2017 00:58:02 +1000 Subject: [PATCH 402/839] Fix log window showing incorrect exit status for linux processes --- ProcessHacker/include/phapp.h | 9 +++++++++ ProcessHacker/log.c | 19 +++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index aaf3ed2325b6..9c050d433bad 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -198,6 +198,15 @@ typedef struct _PH_LOG_ENTRY HANDLE ParentProcessId; PPH_STRING ParentName; NTSTATUS ExitStatus; + union + { + BOOLEAN Flags; + struct + { + BOOLEAN IsSubsystemProcess : 1; + BOOLEAN Spare : 7; + }; + }; } Process; struct { diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index a57862b1c614..e6188ca145d6 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -102,11 +102,12 @@ PPH_LOG_ENTRY PhpCreateProcessLogEntry( if (QueryHandle && entry->Type == PH_LOG_ENTRY_PROCESS_DELETE) { - PROCESS_BASIC_INFORMATION basicInfo; + PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; - if (NT_SUCCESS(PhGetProcessBasicInformation(QueryHandle, &basicInfo))) + if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(QueryHandle, &basicInfo))) { - entry->Process.ExitStatus = basicInfo.ExitStatus; + entry->Process.ExitStatus = basicInfo.BasicInfo.ExitStatus; + entry->Process.IsSubsystemProcess = !!basicInfo.IsSubsystemProcess; } } @@ -218,7 +219,17 @@ PPH_STRING PhFormatLogEntry( 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); + { + ULONG exitstatus = 0; + + // The exit code for Linux processes is located in the lower 8-bits. + if (Entry->Process.IsSubsystemProcess) + exitstatus = Entry->Process.ExitStatus >> 8; + else + exitstatus = Entry->Process.ExitStatus; + + return PhFormatString(L"Process terminated: %s (%u); exit status 0x%x", Entry->Process.Name->Buffer, HandleToUlong(Entry->Process.ProcessId), 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: From de5cac49c3a5ae9643acde1d684bbe5448fe801e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 18 Aug 2017 01:07:40 +1000 Subject: [PATCH 403/839] ToolStatus: Update search for service verification columns --- plugins/ToolStatus/filter.c | 56 +++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/plugins/ToolStatus/filter.c b/plugins/ToolStatus/filter.c index cd8a92ccd31c..8e0002aa3c85 100644 --- a/plugins/ToolStatus/filter.c +++ b/plugins/ToolStatus/filter.c @@ -332,11 +332,7 @@ BOOLEAN ProcessTreeFilterCallback( if (serviceItem->ProcessId) { - WCHAR processIdString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(processIdString, HandleToUlong(serviceItem->ProcessId)); - - if (WordMatchStringZ(processIdString)) + if (WordMatchStringZ(serviceItem->ProcessIdString)) { matched = TRUE; break; @@ -423,11 +419,8 @@ BOOLEAN ServiceTreeFilterCallback( if (serviceNode->ServiceItem->ProcessId) { PPH_PROCESS_NODE processNode; - WCHAR processIdString[PH_INT32_STR_LEN_1]; - PhPrintUInt32(processIdString, HandleToUlong(serviceNode->ServiceItem->ProcessId)); - - if (WordMatchStringZ(processIdString)) + if (WordMatchStringZ(serviceNode->ServiceItem->ProcessIdString)) return TRUE; // Search the process node @@ -438,6 +431,51 @@ BOOLEAN ServiceTreeFilterCallback( } } + 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, From ede96965ff07a58695a2efc341e4e1142a03684f Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 18 Aug 2017 03:44:42 +1000 Subject: [PATCH 404/839] Setup: Fix reset settings option --- tools/CustomSetupTool/CustomSetupTool/resource.h | 4 ++-- tools/CustomSetupTool/CustomSetupTool/resource.rc | 4 ++-- tools/CustomSetupTool/CustomSetupTool/setup.c | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.h b/tools/CustomSetupTool/CustomSetupTool/resource.h index 5a7083fb3d6e..b27ddfceb9ec 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.h +++ b/tools/CustomSetupTool/CustomSetupTool/resource.h @@ -29,8 +29,8 @@ #define IDC_INSTALL_STATUS 1064 #define IDC_INSTALL_DIRECTORY 1065 #define IDC_INSTALL_SUBSTATUS 1065 -#define IDC_RESET_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 diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index e8ea912af48c..0989de87920b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -105,10 +105,10 @@ BEGIN "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,190,168,10 - CONTROL "Install latest nightly build (not recommended)",IDC_RESET_CHECK, + 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_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,176,173,10 + 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,190,138,10 diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 810433cc85dc..9933ad96a515 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -384,7 +384,6 @@ VOID SetupStopService( } } - VOID SetupStartKph( _In_ PPH_SETUP_CONTEXT Context ) From a9e1af86262f51724f6c5317353020af3c3ff20b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 18 Aug 2017 04:20:05 +1000 Subject: [PATCH 405/839] Improve commit 398040837b840beea801099a28511ef1e0979871 --- ProcessHacker/include/phapp.h | 9 --------- ProcessHacker/log.c | 19 ++++++------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 9c050d433bad..aaf3ed2325b6 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -198,15 +198,6 @@ typedef struct _PH_LOG_ENTRY HANDLE ParentProcessId; PPH_STRING ParentName; NTSTATUS ExitStatus; - union - { - BOOLEAN Flags; - struct - { - BOOLEAN IsSubsystemProcess : 1; - BOOLEAN Spare : 7; - }; - }; } Process; struct { diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index e6188ca145d6..36fc243b5307 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -106,8 +106,11 @@ PPH_LOG_ENTRY PhpCreateProcessLogEntry( if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(QueryHandle, &basicInfo))) { - entry->Process.ExitStatus = basicInfo.BasicInfo.ExitStatus; - entry->Process.IsSubsystemProcess = !!basicInfo.IsSubsystemProcess; + // The exit code for Linux processes is located in the lower 8-bits. + if (basicInfo.IsSubsystemProcess) + entry->Process.ExitStatus = basicInfo.BasicInfo.ExitStatus >> 8; + else + entry->Process.ExitStatus = basicInfo.BasicInfo.ExitStatus; } } @@ -219,17 +222,7 @@ PPH_STRING PhFormatLogEntry( HandleToUlong(Entry->Process.ParentProcessId) ); case PH_LOG_ENTRY_PROCESS_DELETE: - { - ULONG exitstatus = 0; - - // The exit code for Linux processes is located in the lower 8-bits. - if (Entry->Process.IsSubsystemProcess) - exitstatus = Entry->Process.ExitStatus >> 8; - else - exitstatus = Entry->Process.ExitStatus; - - return PhFormatString(L"Process terminated: %s (%u); exit status 0x%x", Entry->Process.Name->Buffer, HandleToUlong(Entry->Process.ProcessId), exitstatus); - } + 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: From 6d21ed170538f82f7803386708369260c8cfa10f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 13:53:15 +1000 Subject: [PATCH 406/839] Add service tab highlighting, Add UseColorServiceStop/UseColorUnknown settings for service highlighting --- ProcessHacker/include/phsettings.h | 7 +++++++ ProcessHacker/include/procprv.h | 1 - ProcessHacker/mainwnd.c | 1 - ProcessHacker/modlist.c | 4 ++-- ProcessHacker/modprv.c | 1 + ProcessHacker/procprv.c | 1 - ProcessHacker/settings.c | 19 ++++++++++++++++--- ProcessHacker/srvlist.c | 21 +++++++++++++++++++++ ProcessHacker/srvprv.c | 1 + 9 files changed, 48 insertions(+), 8 deletions(-) diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h index 4f79cc7a69eb..653bab459e98 100644 --- a/ProcessHacker/include/phsettings.h +++ b/ProcessHacker/include/phsettings.h @@ -11,6 +11,8 @@ #define EXT extern #endif +EXT BOOLEAN PhEnableProcessQueryStage2; + EXT ULONG PhCsCollapseServicesOnStart; EXT ULONG PhCsForceNoParent; EXT ULONG PhCsHighlightingDuration; @@ -63,6 +65,11 @@ EXT ULONG PhCsColorIoWrite; EXT ULONG PhCsColorPrivate; EXT ULONG PhCsColorPhysical; +EXT ULONG PhCsUseColorUnknown; +EXT ULONG PhCsColorUnknown; +EXT ULONG PhCsUseColorServiceStop; +EXT ULONG PhCsColorServiceStop; + #define PH_SET_INTEGER_CACHED_SETTING(Name, Value) (PhSetIntegerSetting(L#Name, PhCs##Name = (Value))) #endif diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index f79546f534fd..c3d6b7dd7130 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -15,7 +15,6 @@ extern PPH_LIST PhProcessRecordList; extern PH_QUEUED_LOCK PhProcessRecordListLock; extern ULONG PhStatisticsSampleCount; -extern BOOLEAN PhEnableProcessQueryStage2; extern BOOLEAN PhEnablePurgeProcessRecords; extern BOOLEAN PhEnableCycleCpuUsage; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index c8648aa0997e..a9469a78eeb7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2248,7 +2248,6 @@ VOID PhMwpLoadSettings( PhSetWindowOpacity(PhMainWndHandle, opacity); PhStatisticsSampleCount = PhGetIntegerSetting(L"SampleCount"); - PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); PhEnablePurgeProcessRecords = !PhGetIntegerSetting(L"NoPurgeProcessRecords"); PhEnableCycleCpuUsage = !!PhGetIntegerSetting(L"EnableCycleCpuUsage"); PhEnableServiceNonPoll = !!PhGetIntegerSetting(L"EnableServiceNonPoll"); diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 2b4285549d63..a72a167d1f60 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -882,8 +882,8 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( if (!moduleItem) ; // Dummy - else if (context->HighlightUntrustedModules && moduleItem->VerifyResult != VrTrusted) - getNodeColor->BackColor = PhCsColorPacked; + else if (PhEnableProcessQueryStage2 && context->HighlightUntrustedModules && moduleItem->VerifyResult != VrTrusted) + getNodeColor->BackColor = PhCsColorUnknown; else if (context->HighlightDotNetModules && (moduleItem->Flags & LDRP_COR_IMAGE)) getNodeColor->BackColor = PhCsColorDotNet; else if (context->HighlightImmersiveModules && (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER)) diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 659774544dbd..0a507b872a4f 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -29,6 +29,7 @@ #include #include +#include typedef struct _PH_MODULE_QUERY_DATA { diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index c30b5412d012..e4d2bdb48893 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -189,7 +189,6 @@ PPH_LIST PhProcessRecordList; PH_QUEUED_LOCK PhProcessRecordListLock = PH_QUEUED_LOCK_INIT; ULONG PhStatisticsSampleCount = 512; -BOOLEAN PhEnableProcessQueryStage2 = FALSE; BOOLEAN PhEnablePurgeProcessRecords = TRUE; BOOLEAN PhEnableCycleCpuUsage = TRUE; diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 86a97ee41548..51439a7eecf7 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -26,6 +26,9 @@ #define PH_SETTINGS_PRIVATE #include +#define PH_UPDATE_SETTING(Name) \ + (PhCs##Name = PhGetIntegerSetting(L#Name)) + VOID PhAddDefaultSettings( VOID ) @@ -202,14 +205,19 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ColorIoWrite", L"ff0077"); PhpAddIntegerSetting(L"ColorPrivate", L"0077ff"); PhpAddIntegerSetting(L"ColorPhysical", L"ffff00"); + + PhpAddIntegerSetting(L"UseColorServiceStop", L"1"); + PhpAddIntegerSetting(L"ColorServiceStop", L"6d6d6d"); // Dark grey + PhpAddIntegerSetting(L"UseColorUnknown", L"1"); + PhpAddIntegerSetting(L"ColorUnknown", L"507fff"); // Deep Pink } VOID PhUpdateCachedSettings( VOID ) { - #define PH_UPDATE_SETTING(Name) (PhCs##Name = PhGetIntegerSetting(L#Name)) - + PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); + PH_UPDATE_SETTING(CollapseServicesOnStart); PH_UPDATE_SETTING(ForceNoParent); PH_UPDATE_SETTING(HighlightingDuration); @@ -260,4 +268,9 @@ VOID PhUpdateCachedSettings( PH_UPDATE_SETTING(ColorIoWrite); PH_UPDATE_SETTING(ColorPrivate); PH_UPDATE_SETTING(ColorPhysical); -} \ No newline at end of file + + PH_UPDATE_SETTING(UseColorServiceStop); + PH_UPDATE_SETTING(ColorServiceStop); + PH_UPDATE_SETTING(UseColorUnknown); + PH_UPDATE_SETTING(ColorUnknown); +} diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 4f85a62bda1d..2bf336cb4a26 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -743,6 +743,27 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( 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 (PhEnableProcessQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) + { + getNodeColor->Flags = TN_AUTO_FORECOLOR; + getNodeColor->BackColor = PhCsColorUnknown; + } + else if (PhCsUseColorServiceStop && serviceItem->State == SERVICE_STOPPED) + { + getNodeColor->ForeColor = PhCsColorServiceStop; + } + } + return TRUE; case TreeNewGetCellTooltip: { PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 826bdfc48796..c4f1238460cf 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -30,6 +30,7 @@ #include #include +#include typedef struct _PHP_SERVICE_NAME_ENTRY { From 0a9e0bbf770fc614dfcc397fa99f1ccbefe0f0ef Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 14:33:28 +1000 Subject: [PATCH 407/839] Add filename column to process properties service tab --- ProcessHacker/srvctl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index a53d1f769498..8b122289a3ff 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -242,17 +242,32 @@ INT_PTR CALLBACK PhpServicesPageProc( 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"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 220, L"File name"); PhSetExtendedListView(lvHandle); for (i = 0; i < servicesContext->NumberOfServices; i++) { + SC_HANDLE serviceHandle; 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); + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + PPH_STRING fileName; + + if (fileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle)) + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PhGetStringOrEmpty(fileName)); + PhDereferenceObject(fileName); + } + + CloseServiceHandle(serviceHandle); + } } ExtendedListView_SortItems(lvHandle); From 77a2f2441bf21e6038e9ff04c999c5f449f86d1f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 15:14:02 +1000 Subject: [PATCH 408/839] Add options window callback --- ProcessHacker/include/phplug.h | 1 + ProcessHacker/options.c | 17 +++++++++- plugins/ExtendedTools/ExtendedTools.rc | 1 - plugins/ExtendedTools/exttools.h | 7 ++-- plugins/ExtendedTools/main.c | 18 +++++++++-- plugins/ExtendedTools/options.c | 44 +++----------------------- 6 files changed, 41 insertions(+), 47 deletions(-) diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 68ea5602ede1..22746056c982 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -44,6 +44,7 @@ typedef enum _PH_GENERAL_CALLBACK 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, // PPH_PLUGIN_OBJECT_PROPERTIES Data [main thread] GeneralCallbackMaximum } PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 0c5ea7c7f833..8eebf0135ca6 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -114,7 +115,7 @@ VOID PhShowOptionsDialog( { PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[5]; + HPROPSHEETPAGE pages[30]; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | @@ -182,6 +183,20 @@ VOID PhShowOptionsDialog( pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); } + if (PhPluginsEnabled) + { + PH_PLUGIN_OBJECT_PROPERTIES objectProperties; + + //objectProperties.Parameter = RestartRequired; + objectProperties.NumberOfPages = propSheetHeader.nPages; + objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); + objectProperties.Pages = pages; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &objectProperties); + + propSheetHeader.nPages = objectProperties.NumberOfPages; + } + PageInit = FALSE; PressedOk = FALSE; RestartRequired = FALSE; diff --git a/plugins/ExtendedTools/ExtendedTools.rc b/plugins/ExtendedTools/ExtendedTools.rc index 6535eaeda856..ca97a242e353 100644 --- a/plugins/ExtendedTools/ExtendedTools.rc +++ b/plugins/ExtendedTools/ExtendedTools.rc @@ -158,7 +158,6 @@ BEGIN 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 diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index c5201b3fabc5..410615b3d068 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -524,8 +524,11 @@ VOID EtHandlePropertiesInitializing( // options -VOID EtShowOptionsDialog( - _In_ HWND ParentWindowHandle +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); // thrdact diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 22c279729158..69de3fdacb2f 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -72,7 +72,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - EtShowOptionsDialog((HWND)Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"ExtendedTools"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } VOID NTAPI MenuItemCallback( @@ -470,7 +483,6 @@ LOGICAL DllMain( 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), @@ -485,7 +497,7 @@ LOGICAL DllMain( &PluginUnloadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration diff --git a/plugins/ExtendedTools/options.c b/plugins/ExtendedTools/options.c index cd4c21e63b7d..0b9c5b36b0fa 100644 --- a/plugins/ExtendedTools/options.c +++ b/plugins/ExtendedTools/options.c @@ -22,25 +22,6 @@ #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, @@ -52,33 +33,16 @@ INT_PTR CALLBACK OptionsDlgProc( { 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: + case WM_DESTROY: { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - 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; - } + 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; } From 464a628d086dce5783ab7195837fb9880b69cc14 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 15:26:12 +1000 Subject: [PATCH 409/839] Move (some) plugin dialogs to options window (also add missing files from previous commit) --- plugins/ExtendedServices/ExtendedServices.rc | 7 ++- plugins/ExtendedServices/extsrv.h | 7 ++- plugins/ExtendedServices/main.c | 18 +++++-- plugins/ExtendedServices/options.c | 31 +---------- plugins/NetworkTools/main.c | 15 +++++- plugins/NetworkTools/nettools.h | 7 ++- plugins/NetworkTools/options.c | 32 ++--------- plugins/OnlineChecks/OnlineChecks.rc | 5 +- plugins/OnlineChecks/main.c | 18 +++++-- plugins/OnlineChecks/onlnchk.h | 9 +++- plugins/OnlineChecks/options.c | 29 ++-------- plugins/ToolStatus/ToolStatus.rc | 5 +- plugins/ToolStatus/main.c | 18 +++++-- plugins/ToolStatus/options.c | 56 ++++++-------------- plugins/ToolStatus/toolstatus.h | 7 ++- plugins/Updater/Updater.rc | 7 ++- plugins/Updater/main.c | 18 +++++-- plugins/Updater/options.c | 16 +----- plugins/Updater/updater.h | 7 ++- 19 files changed, 135 insertions(+), 177 deletions(-) diff --git a/plugins/ExtendedServices/ExtendedServices.rc b/plugins/ExtendedServices/ExtendedServices.rc index 82d6e9477509..8c2bd9083285 100644 --- a/plugins/ExtendedServices/ExtendedServices.rc +++ b/plugins/ExtendedServices/ExtendedServices.rc @@ -177,14 +177,13 @@ BEGIN PUSHBUTTON "Remove",IDC_REMOVE,225,171,50,14 END -IDD_OPTIONS DIALOGEX 0, 0, 215, 54 +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 - PUSHBUTTON "Close",IDOK,158,33,50,14 END IDD_SRVTRIGGER DIALOGEX 0, 0, 330, 196 @@ -290,9 +289,9 @@ BEGIN IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 208 + RIGHTMARGIN, 160 TOPMARGIN, 7 - BOTTOMMARGIN, 47 + BOTTOMMARGIN, 26 END IDD_SRVTRIGGER, DIALOG diff --git a/plugins/ExtendedServices/extsrv.h b/plugins/ExtendedServices/extsrv.h index 2a15f62d44fe..6459073e14c5 100644 --- a/plugins/ExtendedServices/extsrv.h +++ b/plugins/ExtendedServices/extsrv.h @@ -40,8 +40,11 @@ INT_PTR CALLBACK EspServiceDependentsDlgProc( // options -VOID EsShowOptionsDialog( - _In_ HWND ParentWindowHandle +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); // other diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index af0e8f012988..7bac8fd61502 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -43,7 +43,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - EsShowOptionsDialog((HWND)Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"ExtendedServices"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } VOID NTAPI MenuItemCallback( @@ -432,7 +445,6 @@ LOGICAL DllMain( 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), @@ -441,7 +453,7 @@ LOGICAL DllMain( &PluginLoadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration diff --git a/plugins/ExtendedServices/options.c b/plugins/ExtendedServices/options.c index 019bfbd32dff..c9bf6e7027cd 100644 --- a/plugins/ExtendedServices/options.c +++ b/plugins/ExtendedServices/options.c @@ -33,42 +33,15 @@ INT_PTR CALLBACK OptionsDlgProc( { 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: + case WM_DESTROY: { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - 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; - } + PhSetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLESERVICESMENU)) == BST_CHECKED); } 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/NetworkTools/main.c b/plugins/NetworkTools/main.c index fa5dc23efcc1..dbe747ba1e35 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -47,7 +47,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - ShowOptionsDialog((HWND)Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"NetworkTools"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } static BOOLEAN ValidAddressInfo( diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index b636a0bc7091..45e04b181b29 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -201,8 +201,11 @@ VOID ShowTracertWindowFromAddress( // options.c -VOID ShowOptionsDialog( - _In_opt_ HWND Parent +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); // country.c diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index caa320ccf068..accb0a4ece4d 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -34,43 +34,17 @@ INT_PTR CALLBACK OptionsDlgProc( { 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: + case WM_DESTROY: { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - 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: - ShowGeoIPUpdateDialog(NULL); - break; - } + PhSetIntegerSetting(SETTING_NAME_PING_SIZE, GetDlgItemInt(hwndDlg, IDC_PINGPACKETLENGTH, NULL, FALSE)); + PhSetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS, GetDlgItemInt(hwndDlg, IDC_MAXHOPS, NULL, FALSE)); } 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/OnlineChecks.rc b/plugins/OnlineChecks/OnlineChecks.rc index 650d108605a9..0cdef0b36dac 100644 --- a/plugins/OnlineChecks/OnlineChecks.rc +++ b/plugins/OnlineChecks/OnlineChecks.rc @@ -87,7 +87,7 @@ END // Dialog // -IDD_OPTIONS DIALOGEX 0, 0, 185, 103 +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 @@ -96,7 +96,6 @@ BEGIN "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 @@ -118,7 +117,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 179 TOPMARGIN, 7 - BOTTOMMARGIN, 96 + BOTTOMMARGIN, 80 END END #endif // APSTUDIO_INVOKED diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 502d5cf853f8..166785835989 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -150,7 +150,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - ShowOptionsDialog((HWND)Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"OnlineChecks"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } VOID NTAPI MenuItemCallback( @@ -724,7 +737,6 @@ LOGICAL DllMain( 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), @@ -733,7 +745,7 @@ LOGICAL DllMain( &PluginLoadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index c7a30d3097ad..808f1282d8b2 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -143,8 +143,13 @@ typedef struct _UPLOAD_CONTEXT PPH_STRING LastAnalysisAgo; } UPLOAD_CONTEXT, *PUPLOAD_CONTEXT; -VOID ShowOptionsDialog( - _In_opt_ HWND Parent +// options.c + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); NTSTATUS UploadFileThreadStart( diff --git a/plugins/OnlineChecks/options.c b/plugins/OnlineChecks/options.c index c7f27a4acc97..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 (GET_WM_COMMAND_ID(wParam, lParam)) - { - 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/ToolStatus/ToolStatus.rc b/plugins/ToolStatus/ToolStatus.rc index b0862532cf6f..70c45351bb40 100644 --- a/plugins/ToolStatus/ToolStatus.rc +++ b/plugins/ToolStatus/ToolStatus.rc @@ -87,7 +87,7 @@ END // Dialog // -IDD_OPTIONS DIALOGEX 0, 0, 191, 103 +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 @@ -98,7 +98,6 @@ BEGIN "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 @@ -157,7 +156,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 184 TOPMARGIN, 7 - BOTTOMMARGIN, 96 + BOTTOMMARGIN, 77 END IDD_CUSTOMIZE_TB, DIALOG diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index d358c8a64d7d..008f0979259b 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -1353,7 +1353,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - ShowOptionsDialog(Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"ToolStatus"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } LOGICAL DllMain( @@ -1387,7 +1400,6 @@ LOGICAL DllMain( 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( @@ -1397,7 +1409,7 @@ LOGICAL DllMain( &PluginLoadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration diff --git a/plugins/ToolStatus/options.c b/plugins/ToolStatus/options.c index f46e5b5564ac..1ab5ac891182 100644 --- a/plugins/ToolStatus/options.c +++ b/plugins/ToolStatus/options.c @@ -34,8 +34,6 @@ INT_PTR CALLBACK OptionsDlgProc( { 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), @@ -46,37 +44,25 @@ INT_PTR CALLBACK OptionsDlgProc( ToolStatusConfig.AutoHideMenu ? BST_CHECKED : BST_UNCHECKED); } break; - case WM_COMMAND: + case WM_DESTROY: { - 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); + 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; - ToolbarLoadSettings(); + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - if (ToolStatusConfig.AutoHideMenu) - { - SetMenu(PhMainWndHandle, NULL); - } - else - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - } + ToolbarLoadSettings(); - EndDialog(hwndDlg, IDOK); - } - break; + if (ToolStatusConfig.AutoHideMenu) + { + SetMenu(PhMainWndHandle, NULL); + } + else + { + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); } } break; @@ -84,15 +70,3 @@ INT_PTR CALLBACK OptionsDlgProc( return FALSE; } - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - Parent, - OptionsDlgProc - ); -} \ No newline at end of file diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 04751703fb12..202cc23ced73 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -223,8 +223,11 @@ VOID ShowCustomizeMenu( // options.c -VOID ShowOptionsDialog( - _In_opt_ HWND Parent +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); // filter.c diff --git a/plugins/Updater/Updater.rc b/plugins/Updater/Updater.rc index 42c50c87d468..2f67ad44e332 100644 --- a/plugins/Updater/Updater.rc +++ b/plugins/Updater/Updater.rc @@ -89,14 +89,13 @@ END // Dialog // -IDD_OPTIONS DIALOGEX 0, 0, 215, 54 +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 - DEFPUSHBUTTON "Close",IDCANCEL,158,33,50,14 END IDD_TEXT DIALOGEX 0, 0, 309, 176 @@ -120,9 +119,9 @@ BEGIN IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 208 + RIGHTMARGIN, 150 TOPMARGIN, 7 - BOTTOMMARGIN, 47 + BOTTOMMARGIN, 27 END IDD_TEXT, DIALOG diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index a6eb9efcde40..4f27d6b7795f 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -73,7 +73,20 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - ShowOptionsDialog((HWND)Parameter); + 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_OPTIONS); + propSheetPage.pszTitle = L"Updater"; + propSheetPage.pfnDlgProc = OptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } LOGICAL DllMain( @@ -104,7 +117,6 @@ LOGICAL DllMain( 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), @@ -125,7 +137,7 @@ LOGICAL DllMain( &PluginMenuItemCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index de429ec1e990..94879202df92 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -43,12 +43,10 @@ INT_PTR CALLBACK OptionsDlgProc( { switch (GET_WM_COMMAND_ID(wParam, lParam)) { - case IDCANCEL: + case IDC_AUTOCHECKBOX: { PhSetIntegerSetting(SETTING_NAME_AUTO_CHECK, Button_GetCheck(GetDlgItem(hwndDlg, IDC_AUTOCHECKBOX)) == BST_CHECKED); - - EndDialog(hwndDlg, IDCANCEL); } break; } @@ -59,18 +57,6 @@ INT_PTR CALLBACK OptionsDlgProc( return FALSE; } -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - Parent, - OptionsDlgProc - ); -} - INT_PTR CALLBACK TextDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index d557abf644e9..604bf3280b79 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -171,8 +171,11 @@ ULONG64 ParseVersionString( // options.c -VOID ShowOptionsDialog( - _In_opt_ HWND Parent +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam ); INT_PTR CALLBACK TextDlgProc( From b522353ee79fe9eafc7d62e78ac6bcb18c0fd8b1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 15:29:36 +1000 Subject: [PATCH 410/839] NetworkTools: Add missing file from previous commit --- plugins/NetworkTools/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index dbe747ba1e35..a4ea7b2402a5 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -632,7 +632,7 @@ LOGICAL DllMain( &PluginLoadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration From 1f887c7a579e19032cd7edf915a22bc16faa54d2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 15:34:14 +1000 Subject: [PATCH 411/839] NetworkTools: Fix options layout --- plugins/NetworkTools/NetworkTools.rc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/NetworkTools/NetworkTools.rc b/plugins/NetworkTools/NetworkTools.rc index 7f639e93aebd..66fdaa394f83 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -96,17 +96,15 @@ BEGIN CONTROL "",IDC_NETOUTPUTEDIT,"RICHEDIT50W",WS_VSCROLL | WS_TABSTOP | 0x4,2,2,385,210 END -IDD_OPTIONS DIALOGEX 0, 0, 201, 57 +IDD_OPTIONS DIALOGEX 0, 0, 201, 43 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 @@ -660,7 +658,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 194 TOPMARGIN, 7 - BOTTOMMARGIN, 50 + BOTTOMMARGIN, 36 END IDD_PING, DIALOG From e4394490fedc708f38577898fe99c7edcfc215ff Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 20 Aug 2017 15:38:49 +1000 Subject: [PATCH 412/839] NetworkTools: Add missing file --- plugins/NetworkTools/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index a4ea7b2402a5..df2b30a5674f 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -623,7 +623,6 @@ LOGICAL DllMain( 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), From 0c8a5726eba5508b44fea2ea5da261c835e101c8 Mon Sep 17 00:00:00 2001 From: Starsam80 Date: Fri, 25 Aug 2017 00:16:44 -0600 Subject: [PATCH 413/839] Minor High-DPI fix (#173) --- plugins/ToolStatus/toolbar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index d5521bcdce06..838032616bbc 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -109,8 +109,8 @@ VOID RebarLoadSettings( { if (ToolStatusConfig.ToolBarEnabled && !ToolBarImageList) { - ToolBarImageSize.cx = PH_SCALE_DPI(GetSystemMetrics(SM_CXSMICON)); - ToolBarImageSize.cy = PH_SCALE_DPI(GetSystemMetrics(SM_CYSMICON)); + ToolBarImageSize.cx = GetSystemMetrics(SM_CXSMICON); + ToolBarImageSize.cy = GetSystemMetrics(SM_CYSMICON); ToolBarImageList = ImageList_Create(ToolBarImageSize.cx, ToolBarImageSize.cy, ILC_COLOR32, 0, 0); } From 08c864e1e6151f0f859037d117b8e73f6d393ba9 Mon Sep 17 00:00:00 2001 From: diversenok <30962924+diversenok@users.noreply.github.com> Date: Sat, 26 Aug 2017 14:25:21 +0300 Subject: [PATCH 414/839] Lost *.com file extension (#174) --- tools/peview/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/peview/main.c b/tools/peview/main.c index 8823cf300a6f..87404e5258c6 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -94,7 +94,7 @@ INT WINAPI wWinMain( { static PH_FILETYPE_FILTER filters[] = { - { L"Supported files (*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb)", L"*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb" }, + { 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; From fec584f19ab0be9ea64813ca930863b7a5c97f3d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 31 Aug 2017 14:54:37 +1000 Subject: [PATCH 415/839] Disable service verification by default, Add EnableServiceStage2 setting --- ProcessHacker/include/phsettings.h | 1 + ProcessHacker/settings.c | 2 ++ ProcessHacker/srvlist.c | 2 +- ProcessHacker/srvprv.c | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h index 653bab459e98..c74d741f692b 100644 --- a/ProcessHacker/include/phsettings.h +++ b/ProcessHacker/include/phsettings.h @@ -12,6 +12,7 @@ #endif EXT BOOLEAN PhEnableProcessQueryStage2; +EXT BOOLEAN PhEnableServiceQueryStage2; EXT ULONG PhCsCollapseServicesOnStart; EXT ULONG PhCsForceNoParent; diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 51439a7eecf7..933d27733ad9 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -48,6 +48,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"1"); PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); + PhpAddIntegerSetting(L"EnableServiceStage2", L"0"); PhpAddIntegerSetting(L"EnableStage2", L"1"); PhpAddIntegerSetting(L"EnableWarnings", L"1"); PhpAddIntegerSetting(L"EnableWindowText", L"1"); @@ -217,6 +218,7 @@ VOID PhUpdateCachedSettings( ) { PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); + PhEnableServiceQueryStage2 = !!PhGetIntegerSetting(L"EnableServiceStage2"); PH_UPDATE_SETTING(CollapseServicesOnStart); PH_UPDATE_SETTING(ForceNoParent); diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index 2bf336cb4a26..e04ff39f66f8 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -753,7 +753,7 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( if (!serviceItem) ; // Dummy - else if (PhEnableProcessQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) + else if (PhEnableServiceQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) { getNodeColor->Flags = TN_AUTO_FORECOLOR; getNodeColor->BackColor = PhCsColorUnknown; diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index c4f1238460cf..1b8ea63d5b2e 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -504,7 +504,7 @@ VOID PhpQueueServiceQuery( PPH_SERVICE_QUERY_DATA data; PH_WORK_QUEUE_ENVIRONMENT environment; - if (!PhEnableProcessQueryStage2) + if (!PhEnableServiceQueryStage2) return; data = PhAllocate(sizeof(PH_SERVICE_QUERY_DATA)); From 27cbc8ccbe56e88875b3b0b296788016a95154ad Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 31 Aug 2017 19:43:52 +1000 Subject: [PATCH 416/839] Fix issue inspecting/browsing executables running from faulty ramdisk software --- ProcessHacker/prpggen.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 925398bfa7eb..38327b0653e7 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -484,13 +484,20 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { if (processItem->FileName) { + PPH_STRING fileName; + + // Note: Workaround for buggy RamDisk software. + fileName = PhGetFileName(processItem->FileName); + PhShellExecuteUserString( hwndDlg, L"ProgramInspectExecutables", - processItem->FileName->Buffer, + fileName->Buffer, FALSE, L"Make sure the PE Viewer executable file is present." ); + + PhDereferenceObject(fileName); } } break; @@ -498,13 +505,20 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { if (processItem->FileName) { + PPH_STRING fileName; + + // Note: Workaround for buggy RamDisk software. + fileName = PhGetFileName(processItem->FileName); + PhShellExecuteUserString( hwndDlg, L"FileBrowseExecutable", - processItem->FileName->Buffer, + fileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." ); + + PhDereferenceObject(fileName); } } break; From bd438f985b476d27894cafaf235f924ebda90892 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 31 Aug 2017 20:04:26 +1000 Subject: [PATCH 417/839] Revert "Fix issue inspecting/browsing executables running from faulty ramdisk software" This reverts commit 27cbc8ccbe56e88875b3b0b296788016a95154ad. --- ProcessHacker/prpggen.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 38327b0653e7..925398bfa7eb 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -484,20 +484,13 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { if (processItem->FileName) { - PPH_STRING fileName; - - // Note: Workaround for buggy RamDisk software. - fileName = PhGetFileName(processItem->FileName); - PhShellExecuteUserString( hwndDlg, L"ProgramInspectExecutables", - fileName->Buffer, + processItem->FileName->Buffer, FALSE, L"Make sure the PE Viewer executable file is present." ); - - PhDereferenceObject(fileName); } } break; @@ -505,20 +498,13 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { if (processItem->FileName) { - PPH_STRING fileName; - - // Note: Workaround for buggy RamDisk software. - fileName = PhGetFileName(processItem->FileName); - PhShellExecuteUserString( hwndDlg, L"FileBrowseExecutable", - fileName->Buffer, + processItem->FileName->Buffer, FALSE, L"Make sure the Explorer executable file is present." ); - - PhDereferenceObject(fileName); } } break; From 5248f9b232624ab7581a9d08bc7d9a90575e9b6e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 31 Aug 2017 20:43:25 +1000 Subject: [PATCH 418/839] Add workaround for inspecting/browsing executables running from faulty ramdisk software --- ProcessHacker/appsup.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 8517f5c049ee..e72d2cf1e37c 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -945,7 +945,6 @@ VOID PhShellExecuteUserString( PPH_STRING executeString; PH_STRINGREF stringBefore; - PH_STRINGREF stringMiddle; PH_STRINGREF stringAfter; PPH_STRING ntMessage; @@ -995,8 +994,20 @@ VOID PhShellExecuteUserString( // 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)); + 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) From 947bacf796d58cc172ae3db858a0a75931f67225 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 1 Sep 2017 11:24:41 +1000 Subject: [PATCH 419/839] BuildTools: Fix pinvoke signature, Fix stream dispose issues --- tools/CustomBuildTool/Source Files/Utils.cs | 23 ++++++++++-------- tools/CustomBuildTool/Source Files/Zip.cs | 6 ++--- .../bin/Release/CustomBuildTool.exe | Bin 161792 -> 161792 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index ba180c1fc213..7fd432512415 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -108,12 +108,12 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) } public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - public static readonly IntPtr STD_OUTPUT_HANDLE = new IntPtr(-11); - public static readonly IntPtr STD_INPUT_HANDLE = new IntPtr(-10); - public static readonly IntPtr STD_ERROR_HANDLE = new IntPtr(-12); + 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(IntPtr StdHandle); + 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)] @@ -160,10 +160,11 @@ private static Rijndael GetRijndael(string secret) public static void Encrypt(string fileName, string outFileName, string secret) { + FileStream fileOutStream = File.Create(outFileName); + using (Rijndael rijndael = GetRijndael(secret)) using (FileStream fileStream = File.OpenRead(fileName)) - using (FileStream fileStream2 = File.Create(outFileName)) - using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateEncryptor(), CryptoStreamMode.Write)) + using (CryptoStream cryptoStream = new CryptoStream(fileOutStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write)) { fileStream.CopyTo(cryptoStream); } @@ -171,10 +172,11 @@ public static void Encrypt(string fileName, string outFileName, string secret) public static void Decrypt(string FileName, string outFileName, string secret) { + FileStream fileOutStream = File.Create(outFileName); + using (Rijndael rijndael = GetRijndael(secret)) using (FileStream fileStream = File.OpenRead(FileName)) - using (FileStream fileStream2 = File.Create(outFileName)) - using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) + using (CryptoStream cryptoStream = new CryptoStream(fileOutStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) { fileStream.CopyTo(cryptoStream); } @@ -189,8 +191,9 @@ public static string HashFile(string FileName) // return BitConverter.ToString(hashBytes).Replace("-", String.Empty); //} - using (FileStream stream = File.OpenRead(FileName)) - using (BufferedStream bufferedStream = new BufferedStream(stream, 0x1000)) + FileStream fileInStream = File.OpenRead(FileName); + + using (BufferedStream bufferedStream = new BufferedStream(fileInStream, 0x1000)) { SHA256Managed sha = new SHA256Managed(); byte[] checksum = sha.ComputeHash(bufferedStream); diff --git a/tools/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs index 826659de53f8..f239475ac93e 100644 --- a/tools/CustomBuildTool/Source Files/Zip.cs +++ b/tools/CustomBuildTool/Source Files/Zip.cs @@ -59,7 +59,7 @@ public static void CreateCompressedFolder(string sourceDirectoryName, string des File.Delete(destinationArchiveFileName); using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) { for (int i = 0; i < filesToAdd.Length; i++) { @@ -93,7 +93,7 @@ public static void CreateCompressedSdkFromFolder(string sourceDirectoryName, str File.Delete(destinationArchiveFileName); using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) { for (int i = 0; i < filesToAdd.Length; i++) { @@ -111,7 +111,7 @@ public static void CreateCompressedPdbFromFolder(string sourceDirectoryName, str File.Delete(destinationArchiveFileName); using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) { for (int i = 0; i < filesToAdd.Length; i++) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 9571ac2a843046720c3a6d2246eed1203b5e0b9f..3a5ceddc828f66058aaf9a8013e749e4dd747693 100644 GIT binary patch delta 5183 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 delta 9679 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 Date: Fri, 1 Sep 2017 11:42:55 +1000 Subject: [PATCH 420/839] HardwareDevices: Move plugin settings to options window making kylo singing great again --- plugins/HardwareDevices/main.c | 53 +++++++++++++++------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index 9b2e7c4a39bd..df1e73ab889e 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -67,36 +67,32 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; 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); + 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_DISKDRIVE_OPTIONS); + propSheetPage.pszTitle = L"Disk Drives"; + propSheetPage.pfnDlgProc = DiskDriveOptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } - // 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); + 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_NETADAPTER_OPTIONS); + propSheetPage.pszTitle = L"Network Adapters"; + propSheetPage.pfnDlgProc = NetworkAdapterOptionsDlgProc; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } } VOID NTAPI MainWindowShowingCallback( @@ -336,7 +332,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 +346,7 @@ LOGICAL DllMain( &PluginUnloadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration From baecf675a542a553ecb808b3579b335b1ab46624 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 1 Sep 2017 12:33:06 +1000 Subject: [PATCH 421/839] NetworkTools: Fix retry button, Fix error creating DB directory path --- plugins/NetworkTools/nettools.h | 12 +++++------- plugins/NetworkTools/pages.c | 32 ++++++++++++++++++-------------- plugins/NetworkTools/update.c | 19 ++++++++++++++++--- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 45e04b181b29..b00ba77ec247 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -288,22 +288,20 @@ VOID ShowGeoIPUpdateDialog( _In_opt_ HWND Parent ); -// page1.c -VOID ShowCheckForUpdatesDialog( +// pages.c +VOID ShowDbCheckForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ); -// page2.c -VOID ShowCheckingForUpdatesDialog( +VOID ShowDbCheckingForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ); -// page5.c -VOID ShowInstallRestartDialog( +VOID ShowDbInstallRestartDialog( _In_ PPH_UPDATER_CONTEXT Context ); -VOID ShowUpdateFailedDialog( +VOID ShowDbUpdateFailedDialog( _In_ PPH_UPDATER_CONTEXT Context ); diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index b379a9213124..d5fb5a254762 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, @@ -51,7 +51,7 @@ HRESULT CALLBACK CheckForUpdatesCallbackProc( { if ((INT)wParam == IDOK) { - ShowCheckingForUpdatesDialog(context); + ShowDbCheckingForUpdatesDialog(context); return S_FALSE; } } @@ -66,7 +66,7 @@ HRESULT CALLBACK CheckForUpdatesCallbackProc( return S_OK; } -HRESULT CALLBACK CheckingForUpdatesCallbackProc( +HRESULT CALLBACK CheckingForUpdatesDbCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -91,7 +91,7 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( return S_OK; } -HRESULT CALLBACK RestartTaskDialogCallbackProc( +HRESULT CALLBACK RestartDbTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -126,7 +126,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( return S_OK; } -HRESULT CALLBACK FinalTaskDialogCallbackProc( +HRESULT CALLBACK FinalDbTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -148,7 +148,11 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( break; case TDN_BUTTON_CLICKED: { - + if ((INT)wParam == IDRETRY) + { + ShowDbCheckForUpdatesDialog(context); + return S_FALSE; + } } break; } @@ -156,7 +160,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( return S_OK; } -VOID ShowCheckForUpdatesDialog( +VOID ShowDbCheckForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -170,7 +174,7 @@ 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"; @@ -180,7 +184,7 @@ VOID ShowCheckForUpdatesDialog( SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } -VOID ShowCheckingForUpdatesDialog( +VOID ShowDbCheckingForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -192,7 +196,7 @@ 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"; @@ -202,7 +206,7 @@ VOID ShowCheckingForUpdatesDialog( SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } -VOID ShowInstallRestartDialog( +VOID ShowDbInstallRestartDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -214,7 +218,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); @@ -226,7 +230,7 @@ VOID ShowInstallRestartDialog( SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } -VOID ShowUpdateFailedDialog( +VOID ShowDbUpdateFailedDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -239,7 +243,7 @@ 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"; diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index b88d78143f68..e53977a5e2b8 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -537,6 +537,19 @@ NTSTATUS GeoIPUpdateThread( if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(dbpath)))) goto CleanupExit; } + else + { + // Create the directory if it does not exist. + + PPH_STRING fullPath; + ULONG indexOfFileName; + + if (fullPath = PH_AUTO(PhGetFullPath(dbpath->Buffer, &indexOfFileName))) + { + if (indexOfFileName != -1) + PhCreateDirectory(PhaSubstring(fullPath, 0, indexOfFileName)); + } + } if (gzfile = gzopen(mmdbGzPath->Buffer, "rb")) { @@ -619,11 +632,11 @@ NTSTATUS GeoIPUpdateThread( { if (success) { - ShowInstallRestartDialog(context); + ShowDbInstallRestartDialog(context); } else { - ShowUpdateFailedDialog(context); + ShowDbUpdateFailedDialog(context); } } @@ -653,7 +666,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( // Create the Taskdialog icons TaskDialogCreateIcons(context); - ShowCheckForUpdatesDialog(context); + ShowDbCheckForUpdatesDialog(context); } break; } From d3679a7c7492157b468cf3826549b4280b204c5a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 1 Sep 2017 16:06:55 +1000 Subject: [PATCH 422/839] Remove legacy plugin menu support --- ProcessHacker/include/mainwnd.h | 15 +------ ProcessHacker/include/mainwndp.h | 4 -- ProcessHacker/include/phplug.h | 17 ------- ProcessHacker/mainwnd.c | 76 -------------------------------- ProcessHacker/plugin.c | 54 ----------------------- 5 files changed, 1 insertion(+), 165 deletions(-) diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index 12cfb2551d49..de4d3b04f33a 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -47,7 +47,7 @@ extern BOOLEAN PhMainWndExiting; #define WM_PH_GET_FONT (WM_APP + 137) // 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) @@ -87,8 +87,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) \ @@ -126,17 +124,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 { diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index b2a82e0a7e77..70f85d4f1569 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -206,10 +206,6 @@ VOID PhMwpDispatchMenuCommand( _In_ ULONG_PTR ItemData ); -ULONG_PTR PhMwpLegacyAddPluginMenuItem( - _In_ PPH_ADD_MENU_ITEM AddMenuItem - ); - HBITMAP PhMwpGetShieldBitmap( VOID ); diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 22746056c982..29aca4ac6cc5 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -502,23 +502,6 @@ typedef struct _PH_PLUGIN_MENU_ITEM #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; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index a9469a78eeb7..415a0165d274 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -88,7 +88,6 @@ static HFONT CurrentCustomFont; static HMENU SubMenuHandles[5]; static PPH_EMENU SubMenuObjects[5]; -static PPH_LIST LegacyAddMenuItemList; static PH_CALLBACK_REGISTRATION SymInitRegistration; @@ -2072,13 +2071,6 @@ ULONG_PTR PhMwpOnUserMessage( 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); @@ -2550,30 +2542,6 @@ VOID PhMwpDispatchMenuCommand( 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 ) @@ -2764,50 +2732,6 @@ VOID PhMwpInitializeSubMenu( } } } - - 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( diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index c468ffa93131..3fd0cf20ffb6 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -627,60 +627,6 @@ ULONG PhPluginReserveIds( 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. */ From ceddb0c7a59e896ccd02e6ece02141a5a5e7fee4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 1 Sep 2017 16:07:26 +1000 Subject: [PATCH 423/839] Fix users mainmenu regression --- ProcessHacker/mainwnd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 415a0165d274..73b7be8fccd5 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -3518,7 +3518,7 @@ VOID PhMwpUpdateUsersMenu( PhInsertEMenuItem(UsersMenu, userMenu = PhCreateEMenuItem(0, IDR_USER, escapedMenuText->Buffer, NULL, UlongToPtr(sessions[i].SessionId)), -1); PhLoadResourceEMenuItem(userMenu, PhInstanceHandle, MAKEINTRESOURCE(IDR_USER), 0); - PhDereferenceObject(escapedMenuText); + PhAutoDereferenceObject(escapedMenuText); } WinStationFreeMemory(sessions); From d74b74a5c5dada1c1ae3e4d45cd2188fe8dac897 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 9 Sep 2017 19:41:39 +1000 Subject: [PATCH 424/839] Add new options window UI --- ProcessHacker/ProcessHacker.rc | 20 +- ProcessHacker/include/phplug.h | 44 + ProcessHacker/options.c | 903 +++++++++++------- ProcessHacker/resource.h | 7 +- .../ExtendedNotifications.rc | 10 +- plugins/ExtendedNotifications/main.c | 250 +++-- plugins/ExtendedNotifications/resource.h | 5 +- plugins/ExtendedServices/main.c | 23 +- plugins/ExtendedTools/main.c | 23 +- plugins/HardwareDevices/devices.h | 6 +- plugins/HardwareDevices/diskoptions.c | 17 +- plugins/HardwareDevices/main.c | 41 +- plugins/HardwareDevices/netoptions.c | 11 + plugins/NetworkTools/main.c | 23 +- plugins/OnlineChecks/main.c | 23 +- plugins/ToolStatus/main.c | 23 +- plugins/Updater/main.c | 23 +- plugins/UserNotes/UserNotes.rc | 12 +- plugins/UserNotes/main.c | 46 +- 19 files changed, 878 insertions(+), 632 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 6ab929d692fc..a2d7926d6373 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1192,7 +1192,7 @@ BEGIN 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 + 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 @@ -1870,6 +1870,18 @@ BEGIN 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 +EXSTYLE WS_EX_APPWINDOW +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,228 + 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,229 + PUSHBUTTON "Reset",IDC_RESET,319,231,50,14 +END ///////////////////////////////////////////////////////////////////////////// // @@ -2402,6 +2414,12 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 170 END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 2 + BOTTOMMARGIN, 245 + END END #endif // APSTUDIO_INVOKED diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 29aca4ac6cc5..18ae65492051 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -325,7 +325,51 @@ typedef struct _PH_PLUGIN_MINIINFO_POINTERS PPH_MINIINFO_FIND_SECTION FindSection; PPH_MINIINFO_CREATE_LIST_SECTION CreateListSection; } PH_PLUGIN_MINIINFO_POINTERS, *PPH_PLUGIN_MINIINFO_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; + // 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_ 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; diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 8eebf0135ca6..b442a115de0b 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include @@ -33,25 +35,9 @@ #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, @@ -87,10 +73,70 @@ INT_PTR CALLBACK PhpOptionsGraphsDlgProc( _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 *DeferHandle, + _Inout_ HDWP *ContainerDeferHandle + ); + +VOID PhOptionsCreateSectionDialog( + _In_ PPH_OPTIONS_SECTION Section + ); + +PPH_OPTIONS_SECTION PhOptionsFindSection( + _In_ PPH_STRINGREF Name + ); + +VOID PhOptionsOnSize( + VOID + ); + +PPH_OPTIONS_SECTION PhOptionsCreateInternalSection( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ); + +PPH_OPTIONS_SECTION PhOptionsCreateSection( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ); + +static HWND PhOptionsWindowHandle = NULL; +static PPH_LIST PhOptionsDialogList = 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; + // All -static BOOLEAN PageInit; -static BOOLEAN PressedOk; -static BOOLEAN RestartRequired; +static BOOLEAN RestartRequired = FALSE; static POINT StartLocation; // General @@ -113,241 +159,198 @@ VOID PhShowOptionsDialog( _In_ HWND ParentWindowHandle ) { - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[30]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_USECALLBACK | - PSH_USEPSTARTPAGE; - propSheetHeader.hInstance = PhInstanceHandle; - 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.hInstance = PhInstanceHandle; - 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.hInstance = PhInstanceHandle; - propSheetPage.pfnDlgProc = PhpOptionsAdvancedDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + ParentWindowHandle, + PhOptionsDialogProc + ); if (!PhStartupParameters.ShowOptions) { - // Symbols page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTSYMBOLS); - propSheetPage.hInstance = PhInstanceHandle; - propSheetPage.pfnDlgProc = PhpOptionsSymbolsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - } + PhUpdateCachedSettings(); + ProcessHacker_SaveAllSettings(PhMainWndHandle); + PhInvalidateAllProcessNodes(); + PhReloadSettingsProcessTreeList(); + PhSiNotifyChangeSettings(); - if (!PhStartupParameters.ShowOptions) - { - // Highlighting page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING); - propSheetPage.hInstance = PhInstanceHandle; - propSheetPage.pfnDlgProc = PhpOptionsHighlightingDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + if (RestartRequired) + { + if (PhShowMessage2( + PhMainWndHandle, + 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); + } + } } - - if (!PhStartupParameters.ShowOptions) + else { - // Graphs page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTGRAPHS); - propSheetPage.hInstance = PhInstanceHandle; - propSheetPage.pfnDlgProc = PhpOptionsGraphsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + // Main window not available. + if (PhSettingsFileName) + PhSaveSettings(PhSettingsFileName->Buffer); } +} - if (PhPluginsEnabled) - { - PH_PLUGIN_OBJECT_PROPERTIES objectProperties; - - //objectProperties.Parameter = RestartRequired; - objectProperties.NumberOfPages = propSheetHeader.nPages; - objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); - objectProperties.Pages = pages; +static HTREEITEM PhOptionsTreeViewAddItem( + _In_ PWSTR Text, + _In_ PVOID Context + ) +{ + TV_INSERTSTRUCT insert; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &objectProperties); + memset(&insert, 0, sizeof(TV_INSERTSTRUCT)); - propSheetHeader.nPages = objectProperties.NumberOfPages; - } + insert.item.mask = TVIF_TEXT | TVIF_PARAM; + insert.hInsertAfter = TVI_LAST; + insert.hParent = TVI_ROOT; + insert.item.pszText = Text; + insert.item.lParam = (LPARAM)Context; - PageInit = FALSE; - PressedOk = FALSE; - RestartRequired = FALSE; + return TreeView_InsertItem(OptionsTreeControl, &insert); +} - if (PhStartupParameters.ShowOptions) - StartLocation = PhStartupParameters.Point; - else - StartLocation.x = MINLONG; +static PPH_OPTIONS_SECTION GetSelectedSectionTreeView( + _In_ HTREEITEM SelectedTreeItem + ) +{ + TVITEM item; - OldTaskMgrDebugger = NULL; + if (!SelectedTreeItem) + return NULL; - PhModalPropertySheet(&propSheetHeader); + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = SelectedTreeItem; - if (PressedOk) - { - if (!PhStartupParameters.ShowOptions) - { - PhUpdateCachedSettings(); - ProcessHacker_SaveAllSettings(PhMainWndHandle); - PhInvalidateAllProcessNodes(); - PhReloadSettingsProcessTreeList(); - PhSiNotifyChangeSettings(); + if (!TreeView_GetItem(OptionsTreeControl, &item)) + return NULL; - if (RestartRequired) - { - if (PhShowMessage2( - PhMainWndHandle, - 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); - } - } - } - else - { - // Main window not available. - if (PhSettingsFileName) - PhSaveSettings(PhSettingsFileName->Buffer); - } - } + return (PPH_OPTIONS_SECTION)item.lParam; } -INT CALLBACK PhpOptionsPropSheetProc( +INT_PTR CALLBACK PhOptionsDialogProc( _In_ HWND hwndDlg, _In_ UINT uMsg, + _In_ WPARAM wParam, _In_ LPARAM lParam ) { switch (uMsg) { - case PSCB_BUTTONPRESSED: + case WM_INITDIALOG: { - if (lParam == PSBTN_OK) + PhOptionsWindowHandle = hwndDlg; + + SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, NULL); + + OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); + ContainerControl = GetDlgItem(PhOptionsWindowHandle, 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, ImageList_Create(2, 22, ILC_COLOR32, 1, 1), TVSIL_NORMAL); // leak + TreeView_SetBkColor(OptionsTreeControl, GetSysColor(COLOR_3DFACE)); + + PhInitializeLayoutManager(&WindowLayoutManager, PhOptionsWindowHandle); + + 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(PhOptionsWindowHandle, IDC_RESET), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); + { - PressedOk = TRUE; + PPH_OPTIONS_SECTION section; + + SectionList = PhCreateList(8); + CurrentSection = NULL; + + if (PhStartupParameters.ShowOptions) + { + // Disable all pages other than Advanced. + section = PhOptionsCreateInternalSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + } + else + { + section = PhOptionsCreateInternalSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); + PhOptionsCreateInternalSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + PhOptionsCreateInternalSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, NULL); + PhOptionsCreateInternalSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); + PhOptionsCreateInternalSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); + + if (PhPluginsEnabled) + { + PH_PLUGIN_OPTIONS_POINTERS pointers; + + pointers.WindowHandle = PhOptionsWindowHandle; + pointers.CreateSection = PhOptionsCreateSection; + pointers.FindSection = PhOptionsFindSection; + pointers.EnterSectionView = PhOptionsEnterSectionView; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &pointers); + } + } + + PhOptionsEnterSectionView(section); + PhOptionsOnSize(); } } break; - } - - return 0; -} + case WM_NCDESTROY: + { + ULONG i; + PPH_OPTIONS_SECTION section; -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, - WC_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); + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + PhOptionsDestroySection(section); + } - if (PhStartupParameters.ShowOptions) - ShowWindow(resetButton, SW_HIDE); + PhDereferenceObject(SectionList); + SectionList = NULL; - // Set the location of the options window. - if (StartLocation.x == MINLONG) - { - PhCenterWindow(optionsWindow, GetParent(optionsWindow)); + PhDeleteLayoutManager(&WindowLayoutManager); } - else + break; + case WM_SIZE: { - SetWindowPos(optionsWindow, NULL, StartLocation.x, StartLocation.y, 0, 0, - SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER); + PhOptionsOnSize(); } - - 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)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; case IDC_RESET: { if (PhShowMessage2( - hwnd, + hwndDlg, TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, TD_WARNING_ICON, L"Do you want to reset all settings and restart Process Hacker?", @@ -377,9 +380,254 @@ LRESULT CALLBACK PhpOptionsWndProc( } } break; + case WM_DRAWITEM: + { + PDRAWITEMSTRUCT drawInfo = (PDRAWITEMSTRUCT)lParam; + + if (drawInfo->CtlID == IDC_SEPARATOR) + { + RECT rect; + + rect = drawInfo->rcItem; + rect.right = 2; + 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 = GetSelectedSectionTreeView(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 DefSubclassProc(hwnd, uMsg, wParam, lParam); + 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_ PVOID Parameter + ) +{ + PPH_OPTIONS_SECTION section; + + section = PhAllocate(sizeof(PH_OPTIONS_SECTION)); + memset(section, 0, sizeof(PH_OPTIONS_SECTION)); + + PhInitializeStringRefLongHint(§ion->Name, Name); + + section->Instance = Instance; + section->Template = Template; + section->DialogProc = DialogProc; + section->Parameter = Parameter; + + PhAddItemList(SectionList, section); + + PhOptionsTreeViewAddItem(Name, section); + + return section; +} + +PPH_OPTIONS_SECTION PhOptionsCreateInternalSection( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ) +{ + PPH_OPTIONS_SECTION section; + + section = PhOptionsCreateSection( + Name, + Instance, + Template, + DialogProc, + Parameter + ); + + section->DialogHandle = PhCreateDialogFromTemplate( + ContainerControl, + DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, + Instance, + Template, + DialogProc, + Parameter + ); + + 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 deferHandle; + HDWP containerDeferHandle; + + if (CurrentSection == NewSection) + return; + + oldSection = CurrentSection; + CurrentSection = NewSection; + + deferHandle = BeginDeferWindowPos(SectionList->Count); + containerDeferHandle = BeginDeferWindowPos(SectionList->Count); + + PhOptionsEnterSectionViewInner(NewSection, &deferHandle, &containerDeferHandle); + PhOptionsLayoutSectionView(); + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (section != NewSection) + PhOptionsEnterSectionViewInner(section, &deferHandle, &containerDeferHandle); + } + + EndDeferWindowPos(deferHandle); + 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 *DeferHandle, + _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 + ); } #define SetDlgItemCheckForSetting(hwndDlg, Id, Name) \ @@ -394,11 +642,10 @@ LRESULT CALLBACK PhpOptionsWndProc( 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; @@ -500,7 +747,6 @@ static VOID WriteCurrentUserRun( } } - INT_PTR CALLBACK PhpOptionsGeneralDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -516,8 +762,6 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( ULONG i; LOGFONT font; - PhpPageInit(hwndDlg); - comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); for (i = 0; i < sizeof(PhSizeUnitNames) / sizeof(PWSTR); i++) @@ -567,6 +811,31 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( break; case WM_DESTROY: { + 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); + } + if (CurrentFontInstance) DeleteObject(CurrentFontInstance); @@ -575,7 +844,7 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_STARTATLOGON: { @@ -618,45 +887,6 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( } } 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; @@ -876,7 +1106,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( { case WM_INITDIALOG: { - PhpPageInit(hwndDlg); PhpAdvancedPageLoad(hwndDlg); if (PhStartupParameters.ShowOptions) @@ -898,6 +1127,8 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( break; case WM_DESTROY: { + PhpAdvancedPageSave(hwndDlg); + PhClearReference(&OldTaskMgrDebugger); } break; @@ -915,7 +1146,7 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( GetWindowRect(GetParent(hwndDlg), &windowRect); WindowHandleForElevate = hwndDlg; - + PhCreateThread2(PhpElevateAdvancedThreadStart, PhFormatString( L"-showoptions -hwnd %Ix -point %u,%u", (ULONG_PTR)GetParent(hwndDlg), @@ -932,21 +1163,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( } } 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); @@ -964,16 +1180,21 @@ INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + 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"); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_BROWSE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPSEARCHPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); } break; case WM_COMMAND: @@ -1008,27 +1229,23 @@ INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( } } break; - case WM_NOTIFY: + case WM_SIZE: { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PPH_STRING dbgHelpPath = PhaGetDlgItemText(hwndDlg, IDC_DBGHELPPATH); + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_DESTROY: + { + PPH_STRING dbgHelpPath = PhaGetDlgItemText(hwndDlg, IDC_DBGHELPPATH); - if (!PhEqualString(dbgHelpPath, PhaGetStringSetting(L"DbgHelpPath"), TRUE)) - RestartRequired = TRUE; + 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"); + 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; - } + PhDeleteLayoutManager(&LayoutManager); } break; } @@ -1092,14 +1309,12 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + switch (uMsg) { case WM_INITDIALOG: { - ULONG i; - - PhpPageInit(hwndDlg); - // Highlighting Duration SetDlgItemInt(hwndDlg, IDC_HIGHLIGHTINGDURATION, PhCsHighlightingDuration, FALSE); @@ -1117,7 +1332,7 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( PhSetExtendedListView(HighlightingListViewHandle); ExtendedListView_SetItemColorFunction(HighlightingListViewHandle, PhpColorItemColorFunction); - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) + for (ULONG i = 0; i < ARRAYSIZE(ColorItems); i++) { INT lvItemIndex; @@ -1126,25 +1341,50 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( 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, 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 (ULONG i = 0; i < ARRAYSIZE(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 (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_ENABLEALL: { - ULONG i; - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) + for (ULONG i = 0; i < ARRAYSIZE(ColorItems); i++) ListView_SetCheckState(HighlightingListViewHandle, i, TRUE); } break; case IDC_DISABLEALL: { - ULONG i; - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) + for (ULONG i = 0; i < ARRAYSIZE(ColorItems); i++) ListView_SetCheckState(HighlightingListViewHandle, i, FALSE); } break; @@ -1157,24 +1397,6 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( 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) @@ -1183,7 +1405,7 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( if (item = PhGetSelectedListViewItemParam(HighlightingListViewHandle)) { - CHOOSECOLOR chooseColor = { sizeof(chooseColor) }; + CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; COLORREF customColors[16] = { 0 }; chooseColor.hwndOwner = hwndDlg; @@ -1233,8 +1455,6 @@ INT_PTR CALLBACK PhpOptionsGraphsDlgProc( { case WM_INITDIALOG: { - PhpPageInit(hwndDlg); - // Show Text SetDlgItemCheckForSetting(hwndDlg, IDC_SHOWTEXT, L"GraphShowText"); SetDlgItemCheckForSetting(hwndDlg, IDC_USEOLDCOLORS, L"GraphColorMode"); @@ -1248,28 +1468,17 @@ INT_PTR CALLBACK PhpOptionsGraphsDlgProc( ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_PHYSICAL), PhCsColorPhysical); } break; - case WM_NOTIFY: + case WM_DESTROY: { - 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; - } + 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; } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 00012e75a9d9..7ab4883d8fa3 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -124,6 +124,7 @@ #define IDB_SEARCH_INACTIVE 224 #define IDB_SEARCH_ACTIVE_BMP 225 #define IDB_SEARCH_INACTIVE_BMP 226 +#define IDD_OPTIONS 227 #define IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 @@ -535,6 +536,8 @@ #define IDC_FILTEROPTIONS 1389 #define IDC_FILTERTYPE 1390 #define IDC_TREELIST 1391 +#define IDC_SECTIONTREE 1393 +#define IDC_INFO 1396 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -740,9 +743,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 233 +#define _APS_NEXT_RESOURCE_VALUE 238 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1392 +#define _APS_NEXT_CONTROL_VALUE 1397 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif diff --git a/plugins/ExtendedNotifications/ExtendedNotifications.rc b/plugins/ExtendedNotifications/ExtendedNotifications.rc index ce5be6f4bd35..0c7e73170777 100644 --- a/plugins/ExtendedNotifications/ExtendedNotifications.rc +++ b/plugins/ExtendedNotifications/ExtendedNotifications.rc @@ -101,7 +101,7 @@ BEGIN 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 + 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 @@ -118,15 +118,15 @@ BEGIN 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 + LTEXT "Examples:\nWdi*\nseclogon",IDC_INFO,7,186,241,36 END -IDD_LOGGING DIALOGEX 0, 0, 255, 229 +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_STATIC,7,7,241,53 + 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 @@ -173,7 +173,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 248 TOPMARGIN, 7 - BOTTOMMARGIN, 222 + BOTTOMMARGIN, 62 END IDD_GROWL, DIALOG diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index b272d100f136..1ec31acc37d7 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -3,6 +3,7 @@ * main program * * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -122,7 +123,6 @@ LOGICAL DllMain( 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), @@ -131,7 +131,7 @@ LOGICAL DllMain( &PluginLoadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration @@ -339,53 +339,36 @@ VOID NTAPI ShowOptionsCallback( _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); + 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( @@ -875,6 +858,7 @@ INT_PTR CALLBACK ProcessesDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; INT_PTR result; if (result = HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, @@ -885,42 +869,44 @@ INT_PTR CALLBACK ProcessesDlgProc( { case WM_INITDIALOG: { - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - 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_NOTIFY: + case WM_SIZE: { - 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; - } + PhLayoutManagerLayout(&LayoutManager); } break; } @@ -935,9 +921,12 @@ INT_PTR CALLBACK ServicesDlgProc( _In_ LPARAM lParam ) { - if (HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, + static PH_LAYOUT_MANAGER LayoutManager; + INT_PTR result; + + if (result = HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, GetDlgItem(hwndDlg, IDC_LIST), EditingServiceFilterList)) - return FALSE; + return result; switch (uMsg) { @@ -946,37 +935,41 @@ INT_PTR CALLBACK ServicesDlgProc( 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_NOTIFY: + case WM_SIZE: { - 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; - } + PhLayoutManagerLayout(&LayoutManager); } break; } @@ -991,11 +984,30 @@ INT_PTR CALLBACK LoggingDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + switch (uMsg) { case WM_INITDIALOG: { - SetDlgItemText(hwndDlg, IDC_LOGFILENAME, ((PPH_STRING)PH_AUTO(PhGetStringSetting(SETTING_NAME_LOG_FILENAME)))->Buffer); + SetDlgItemText(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: @@ -1030,22 +1042,6 @@ INT_PTR CALLBACK LoggingDlgProc( } } 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; @@ -1058,6 +1054,8 @@ INT_PTR CALLBACK GrowlDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + switch (uMsg) { case WM_INITDIALOG: @@ -1065,30 +1063,24 @@ INT_PTR CALLBACK GrowlDlgProc( 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); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LICENSE), NULL, PH_ANCHOR_ALL); } break; - case WM_NOTIFY: + case WM_DESTROY: { - 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); + PhSetIntegerSetting(SETTING_NAME_ENABLE_GROWL, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGROWL)) == BST_CHECKED); - if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) - RegisterGrowl(FALSE); + if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) + RegisterGrowl(FALSE); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); } break; } diff --git a/plugins/ExtendedNotifications/resource.h b/plugins/ExtendedNotifications/resource.h index 7222e3a05259..10606d451840 100644 --- a/plugins/ExtendedNotifications/resource.h +++ b/plugins/ExtendedNotifications/resource.h @@ -20,14 +20,15 @@ #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 108 +#define _APS_NEXT_RESOURCE_VALUE 109 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1017 +#define _APS_NEXT_CONTROL_VALUE 1018 #define _APS_NEXT_SYMED_VALUE 102 #endif #endif diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index 7bac8fd61502..401b27a1629a 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -43,20 +43,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"ExtendedServices"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"ExtendedServices", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } VOID NTAPI MenuItemCallback( diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 69de3fdacb2f..fe84e010489f 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -72,20 +72,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"ExtendedTools"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"ExtendedTools", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } VOID NTAPI MenuItemCallback( diff --git a/plugins/HardwareDevices/devices.h b/plugins/HardwareDevices/devices.h index e6b70f33d775..1c415a429d92 100644 --- a/plugins/HardwareDevices/devices.h +++ b/plugins/HardwareDevices/devices.h @@ -182,16 +182,13 @@ typedef struct _DV_NETADAPTER_CONTEXT //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( @@ -443,6 +440,7 @@ typedef struct _DV_DISK_OPTIONS_CONTEXT HWND ListViewHandle; //HIMAGELIST ImageList; BOOLEAN OptionsChanged; + PH_LAYOUT_MANAGER LayoutManager; } DV_DISK_OPTIONS_CONTEXT, *PDV_DISK_OPTIONS_CONTEXT; VOID DiskDrivesInitialize(VOID); diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index 42a404f99c63..bd252a5171ce 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -606,6 +606,8 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( if (uMsg == WM_DESTROY) { + PhDeleteLayoutManager(&context->LayoutManager); + if (context->OptionsChanged) DiskDrivesSaveList(); @@ -623,13 +625,6 @@ 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); @@ -641,6 +636,9 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( AddListViewGroup(context->ListViewHandle, 0, L"Connected"); AddListViewGroup(context->ListViewHandle, 1, L"Disconnected"); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); FindDiskDrives(context); @@ -648,6 +646,11 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( context->OptionsChanged = FALSE; } break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index df1e73ab889e..19366f4c7515 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -67,32 +67,23 @@ VOID NTAPI ShowOptionsCallback( _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_DISKDRIVE_OPTIONS); - propSheetPage.pszTitle = L"Disk Drives"; - propSheetPage.pfnDlgProc = DiskDriveOptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = 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 + ); - 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_NETADAPTER_OPTIONS); - propSheetPage.pszTitle = L"Network Adapters"; - propSheetPage.pfnDlgProc = NetworkAdapterOptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + optionsEntry->CreateSection( + L"Network Adapters", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_NETADAPTER_OPTIONS), + NetworkAdapterOptionsDlgProc, + NULL + ); } VOID NTAPI MainWindowShowingCallback( diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index 0807ba9a9e3b..0d5a25d4005c 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -705,6 +705,8 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( if (uMsg == WM_DESTROY) { + PhDeleteLayoutManager(&context->LayoutManager); + if (context->OptionsChanged) NetAdaptersSaveList(); @@ -733,11 +735,20 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( AddListViewGroup(context->ListViewHandle, 0, L"Connected"); AddListViewGroup(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); + } + break; case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index df2b30a5674f..b034b73ca90f 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -47,20 +47,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"NetworkTools"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"NetworkTools", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } static BOOLEAN ValidAddressInfo( diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 166785835989..6f9677e1b99c 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -150,20 +150,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"OnlineChecks"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"OnlineChecks", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } VOID NTAPI MenuItemCallback( diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 008f0979259b..ae24130f3e41 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -1353,20 +1353,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"ToolStatus"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"ToolStatus", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } LOGICAL DllMain( diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index 4f27d6b7795f..9d9e795e2607 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -73,20 +73,15 @@ VOID NTAPI ShowOptionsCallback( _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_OPTIONS); - propSheetPage.pszTitle = L"Updater"; - propSheetPage.pfnDlgProc = OptionsDlgProc; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"Updater", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); } LOGICAL DllMain( diff --git a/plugins/UserNotes/UserNotes.rc b/plugins/UserNotes/UserNotes.rc index 5510b557d445..a1b019805eb3 100644 --- a/plugins/UserNotes/UserNotes.rc +++ b/plugins/UserNotes/UserNotes.rc @@ -48,17 +48,15 @@ END // Dialog // -IDD_OPTIONS DIALOGEX 0, 0, 316, 71 +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,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 + 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 @@ -92,9 +90,9 @@ BEGIN IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 309 + RIGHTMARGIN, 310 TOPMARGIN, 7 - BOTTOMMARGIN, 64 + BOTTOMMARGIN, 47 END IDD_PROCCOMMENT, DIALOG diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 6f2dde717b1c..55bfc0d4b5ff 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -3,7 +3,7 @@ * main program * * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2017 dmex * * This file is part of Process Hacker. * @@ -314,11 +314,14 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - DialogBox( + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"UserNotes", PluginInstance->DllBase, MAKEINTRESOURCE(IDD_OPTIONS), - (HWND)Parameter, - OptionsDlgProc + OptionsDlgProc, + NULL ); } @@ -1344,7 +1347,6 @@ LOGICAL DllMain( 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(); @@ -1361,7 +1363,7 @@ LOGICAL DllMain( &PluginUnloadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration @@ -1479,32 +1481,38 @@ INT_PTR CALLBACK OptionsDlgProc( _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + switch (uMsg) { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetDlgItemText(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); + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); + + } + 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 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[] = From 818bb1b7b301381d7cd4600e68a9e6000e741a96 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 9 Sep 2017 20:01:36 +1000 Subject: [PATCH 425/839] Fix options window start location regression from previous commit --- ProcessHacker/options.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index b442a115de0b..17bef613611a 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -159,6 +159,11 @@ VOID PhShowOptionsDialog( _In_ HWND ParentWindowHandle ) { + if (PhStartupParameters.ShowOptions) + StartLocation = PhStartupParameters.Point; + else + StartLocation.x = MINLONG; + DialogBox( PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTIONS), @@ -258,7 +263,16 @@ INT_PTR CALLBACK PhOptionsDialogProc( SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - PhCenterWindow(hwndDlg, NULL); + // Set the location of the options window. + if (StartLocation.x == MINLONG) + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + } + else + { + SetWindowPos(hwndDlg, NULL, StartLocation.x, StartLocation.y, 0, 0, + SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER); + } OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); ContainerControl = GetDlgItem(PhOptionsWindowHandle, IDD_CONTAINER); @@ -1144,12 +1158,12 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( // WM_PH_CHILD_EXIT gets sent. PhpAdvancedPageSave(hwndDlg); - GetWindowRect(GetParent(hwndDlg), &windowRect); + GetWindowRect(GetParent(GetParent(hwndDlg)), &windowRect); WindowHandleForElevate = hwndDlg; PhCreateThread2(PhpElevateAdvancedThreadStart, PhFormatString( L"-showoptions -hwnd %Ix -point %u,%u", - (ULONG_PTR)GetParent(hwndDlg), + (ULONG_PTR)GetParent(GetParent(hwndDlg)), windowRect.left + 20, windowRect.top + 20 )); From 814450d9a752a128bab20a7dd96a31c75e8234ac Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 14 Sep 2017 16:45:20 +1000 Subject: [PATCH 426/839] Add cleanup button to options window, Fix options window handle leak --- ProcessHacker/ProcessHacker.rc | 11 ++-- ProcessHacker/options.c | 95 +++++++++++++--------------------- ProcessHacker/resource.h | 1 + 3 files changed, 45 insertions(+), 62 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index a2d7926d6373..4e3105150b98 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1877,12 +1877,15 @@ 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,228 + 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,229 - PUSHBUTTON "Reset",IDC_RESET,319,231,50,14 + 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 + ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO @@ -2414,7 +2417,7 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 170 END - + IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 2 diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 17bef613611a..d57347a8497a 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -94,7 +94,6 @@ VOID PhOptionsLayoutSectionView( VOID PhOptionsEnterSectionViewInner( _In_ PPH_OPTIONS_SECTION Section, - _Inout_ HDWP *DeferHandle, _Inout_ HDWP *ContainerDeferHandle ); @@ -110,14 +109,6 @@ VOID PhOptionsOnSize( VOID ); -PPH_OPTIONS_SECTION PhOptionsCreateInternalSection( - _In_ PWSTR Name, - _In_ PVOID Instance, - _In_ PWSTR Template, - _In_ DLGPROC DialogProc, - _In_ PVOID Parameter - ); - PPH_OPTIONS_SECTION PhOptionsCreateSection( _In_ PWSTR Name, _In_ PVOID Instance, @@ -134,6 +125,7 @@ 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; @@ -211,7 +203,7 @@ VOID PhShowOptionsDialog( } } -static HTREEITEM PhOptionsTreeViewAddItem( +static HTREEITEM PhpOptionsTreeViewAddItem( _In_ PWSTR Text, _In_ PVOID Context ) @@ -229,7 +221,7 @@ static HTREEITEM PhOptionsTreeViewAddItem( return TreeView_InsertItem(OptionsTreeControl, &insert); } -static PPH_OPTIONS_SECTION GetSelectedSectionTreeView( +static PPH_OPTIONS_SECTION PhpTreeViewGetSelectedSection( _In_ HTREEITEM SelectedTreeItem ) { @@ -274,6 +266,7 @@ INT_PTR CALLBACK PhOptionsDialogProc( SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER); } + OptionsTreeImageList = ImageList_Create(2, 22, ILC_COLOR, 1, 1); OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); ContainerControl = GetDlgItem(PhOptionsWindowHandle, IDD_CONTAINER); @@ -281,15 +274,16 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhSetControlTheme(OptionsTreeControl, L"explorer"); TreeView_SetExtendedStyle(OptionsTreeControl, TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); - TreeView_SetImageList(OptionsTreeControl, ImageList_Create(2, 22, ILC_COLOR32, 1, 1), TVSIL_NORMAL); // leak + TreeView_SetImageList(OptionsTreeControl, OptionsTreeImageList, TVSIL_NORMAL); TreeView_SetBkColor(OptionsTreeControl, GetSysColor(COLOR_3DFACE)); PhInitializeLayoutManager(&WindowLayoutManager, PhOptionsWindowHandle); - 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(PhOptionsWindowHandle, IDC_RESET), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_RESET), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_CLEANUP), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + //PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_APPLY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); @@ -303,15 +297,15 @@ INT_PTR CALLBACK PhOptionsDialogProc( if (PhStartupParameters.ShowOptions) { // Disable all pages other than Advanced. - section = PhOptionsCreateInternalSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + section = PhOptionsCreateSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); } else { - section = PhOptionsCreateInternalSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); - PhOptionsCreateInternalSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); - PhOptionsCreateInternalSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, NULL); - PhOptionsCreateInternalSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); - PhOptionsCreateInternalSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); + section = PhOptionsCreateSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); + PhOptionsCreateSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + PhOptionsCreateSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, NULL); + PhOptionsCreateSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); + PhOptionsCreateSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); if (PhPluginsEnabled) { @@ -345,6 +339,8 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhDereferenceObject(SectionList); SectionList = NULL; + ImageList_Destroy(OptionsTreeImageList); + PhDeleteLayoutManager(&WindowLayoutManager); } break; @@ -391,6 +387,20 @@ INT_PTR CALLBACK PhOptionsDialogProc( } } 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; @@ -422,7 +432,7 @@ INT_PTR CALLBACK PhOptionsDialogProc( LPNMTREEVIEW treeview = (LPNMTREEVIEW)lParam; PPH_OPTIONS_SECTION section; - if (section = GetSelectedSectionTreeView(treeview->itemNew.hItem)) + if (section = PhpTreeViewGetSelectedSection(treeview->itemNew.hItem)) { PhOptionsEnterSectionView(section); } @@ -493,37 +503,7 @@ PPH_OPTIONS_SECTION PhOptionsCreateSection( PhAddItemList(SectionList, section); - PhOptionsTreeViewAddItem(Name, section); - - return section; -} - -PPH_OPTIONS_SECTION PhOptionsCreateInternalSection( - _In_ PWSTR Name, - _In_ PVOID Instance, - _In_ PWSTR Template, - _In_ DLGPROC DialogProc, - _In_ PVOID Parameter - ) -{ - PPH_OPTIONS_SECTION section; - - section = PhOptionsCreateSection( - Name, - Instance, - Template, - DialogProc, - Parameter - ); - - section->DialogHandle = PhCreateDialogFromTemplate( - ContainerControl, - DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, - Instance, - Template, - DialogProc, - Parameter - ); + PhpOptionsTreeViewAddItem(Name, section); return section; } @@ -582,7 +562,6 @@ VOID PhOptionsEnterSectionView( ULONG i; PPH_OPTIONS_SECTION section; PPH_OPTIONS_SECTION oldSection; - HDWP deferHandle; HDWP containerDeferHandle; if (CurrentSection == NewSection) @@ -591,10 +570,9 @@ VOID PhOptionsEnterSectionView( oldSection = CurrentSection; CurrentSection = NewSection; - deferHandle = BeginDeferWindowPos(SectionList->Count); containerDeferHandle = BeginDeferWindowPos(SectionList->Count); - PhOptionsEnterSectionViewInner(NewSection, &deferHandle, &containerDeferHandle); + PhOptionsEnterSectionViewInner(NewSection, &containerDeferHandle); PhOptionsLayoutSectionView(); for (i = 0; i < SectionList->Count; i++) @@ -602,10 +580,9 @@ VOID PhOptionsEnterSectionView( section = SectionList->Items[i]; if (section != NewSection) - PhOptionsEnterSectionViewInner(section, &deferHandle, &containerDeferHandle); + PhOptionsEnterSectionViewInner(section, &containerDeferHandle); } - EndDeferWindowPos(deferHandle); EndDeferWindowPos(containerDeferHandle); if (NewSection->DialogHandle) @@ -614,7 +591,6 @@ VOID PhOptionsEnterSectionView( VOID PhOptionsEnterSectionViewInner( _In_ PPH_OPTIONS_SECTION Section, - _Inout_ HDWP *DeferHandle, _Inout_ HDWP *ContainerDeferHandle ) { @@ -1136,6 +1112,9 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTLABEL), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC), FALSE); + + EnableWindow(GetDlgItem(PhOptionsWindowHandle, IDC_RESET), FALSE); + EnableWindow(GetDlgItem(PhOptionsWindowHandle, IDC_CLEANUP), FALSE); } } break; diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 7ab4883d8fa3..80b0825bde89 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -225,6 +225,7 @@ #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 From 4a78a1b027bffbed9b0b1c7ee104f987b73ccbc3 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 14 Sep 2017 16:59:04 +1000 Subject: [PATCH 427/839] Fix format specifiers --- ProcessHacker/dbgcon.c | 18 +++++++++--------- phlib/settings.c | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index c441891d4979..d9a5c25e57d7 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -240,16 +240,16 @@ static VOID PhpDumpObjectInfo( __try { wprintf(L"Type: %s\n", objectType->Name); - wprintf(L"Reference count: %d\n", ObjectHeader->RefCount); + 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: %u\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects); + 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: %u\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count); + wprintf(L"Free list count: %lu\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count); } else if (objectType == PhStringType) { @@ -1329,12 +1329,12 @@ NTSTATUS PhpDebugConsoleThreadStart( 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"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: %u\n", workQueue->CurrentThreads); - wprintf(L"Busy count: %u\n", workQueue->BusyCount); + wprintf(L"Current threads: %lu\n", workQueue->CurrentThreads); + wprintf(L"Busy count: %lu\n", workQueue->BusyCount); PhAcquireQueuedLockExclusive(&workQueue->QueueLock); @@ -1388,7 +1388,7 @@ NTSTATUS PhpDebugConsoleThreadStart( do { - wprintf(L"\tRecord at %Ix: %s (%u) (refs: %d)\n", (ULONG_PTR)record, record->ProcessName->Buffer, HandleToUlong(record->ProcessId), record->RefCount); + 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); diff --git a/phlib/settings.c b/phlib/settings.c index 173afb1cbe91..4f38546f7393 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -165,7 +165,7 @@ static PPH_STRING PhpSettingToString( { PPH_INTEGER_PAIR integerPair = &Setting->u.IntegerPair; - return PhFormatString(L"%d,%d", integerPair->X, integerPair->Y); + return PhFormatString(L"%ld,%ld", integerPair->X, integerPair->Y); } case ScalableIntegerPairSettingType: { @@ -174,7 +174,7 @@ static PPH_STRING PhpSettingToString( if (!scalableIntegerPair) return PhReferenceEmptyString(); - return PhFormatString(L"@%u|%d,%d", scalableIntegerPair->Scale, scalableIntegerPair->X, scalableIntegerPair->Y); + return PhFormatString(L"@%lu|%ld,%ld", scalableIntegerPair->Scale, scalableIntegerPair->X, scalableIntegerPair->Y); } } From 9128580e2ac275b1d4130b2565662765b1c51a29 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 24 Sep 2017 15:34:32 +1000 Subject: [PATCH 428/839] UserNotes: Check current directory for portable database file --- plugins/UserNotes/main.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 55bfc0d4b5ff..e3c8e0deb96f 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -273,7 +273,9 @@ VOID NTAPI LoadCallback( _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)) @@ -284,21 +286,29 @@ VOID NTAPI LoadCallback( ToolStatusInterface = NULL; } - path = PhaGetStringSetting(SETTING_NAME_DATABASE_PATH); - path = PH_AUTO(PhExpandEnvironmentStrings(&path->sr)); + directory = PH_AUTO(PhGetApplicationDirectory()); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &databaseFile)); - LoadCustomColors(); - - if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) + if (RtlDoesFileExists_U(path->Buffer)) + { + SetDbPath(path); + } + else { - PPH_STRING directory; + path = PhaGetStringSetting(SETTING_NAME_DATABASE_PATH); + path = PH_AUTO(PhExpandEnvironmentStrings(&path->sr)); - directory = PH_AUTO(PhGetApplicationDirectory()); - path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); + if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) + { + directory = PH_AUTO(PhGetApplicationDirectory()); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); + } + + SetDbPath(path); } - SetDbPath(path); LoadDb(); + LoadCustomColors(); } VOID NTAPI UnloadCallback( From adce5c621482b71a298000e12e46741504073ba4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 24 Sep 2017 17:26:25 +1000 Subject: [PATCH 429/839] SetupTool: Ignore portable config files --- tools/CustomSetupTool/CustomSetupTool/extract.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 14ebc3ff7ddd..af1efe92e6ae 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -96,6 +96,11 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; + if (!strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) + continue; + if (!strstr(zipFileStat.m_filename, "usernotesdb.xml")) + continue; + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (!strncmp(zipFileStat.m_filename, "x32\\", 4)) @@ -130,6 +135,11 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; + if (!strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) + continue; + if (!strstr(zipFileStat.m_filename, "usernotesdb.xml")) + continue; + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (!strncmp(zipFileStat.m_filename, "x32\\", 4)) @@ -166,7 +176,12 @@ BOOLEAN SetupExtractBuild( if ((zipFileCrc32 = mz_crc32(zipFileCrc32, buffer, bufferLength)) != zipFileStat.m_crc32) goto CleanupExit; - extractPath = PhConcatStrings(3, PhGetString(Context->SetupInstallPath), L"\\", PhGetString(fileName)); + extractPath = PhConcatStrings( + 3, + PhGetString(Context->SetupInstallPath), + L"\\", + PhGetString(fileName) + ); if (fullSetupPath = PhGetFullPath(extractPath->Buffer, &indexOfFileName)) { From 5f3cc85417372b5e91325103584c4f16e7e5d572 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 24 Sep 2017 17:29:17 +1000 Subject: [PATCH 430/839] BuildTools: Include config files for portable release --- tools/CustomBuildTool/Source Files/Build.cs | 28 ++++++++++++++++-- .../bin/Release/CustomBuildTool.exe | Bin 161792 -> 162304 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index c6a02b535668..73d01c1bc7b4 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -758,9 +758,30 @@ public static bool BuildBinZip() try { - if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) - File.Delete(BuildOutputFolder + "\\processhacker-build-bin.zip"); + 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")) @@ -769,6 +790,9 @@ public static bool BuildBinZip() 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"); Directory.Move("bin\\x32", "bin\\Release32"); diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 3a5ceddc828f66058aaf9a8013e749e4dd747693..eccc44aa16cc4def7600af91ef53d0824753c23d 100644 GIT binary patch delta 11562 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 X-sjKeeJ-8dB+qDkHZW2CoAiGG%Nn|_ delta 11166 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 YYjcl3l6zeGa=m=%z(<}bmOhjI8vsptC;$Ke diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 6456725c97ff4e77184007b52e406f3d2a3c0e45..f3ccdd966ff702092d823ec80c0a128736eca275 100644 GIT binary patch delta 9193 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 M`EW{SajN5g0J^S^Jpcdz delta 9532 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 Date: Sun, 24 Sep 2017 18:12:25 +1000 Subject: [PATCH 431/839] SetupTool: Fix websetup, Fix ignored files --- tools/CustomSetupTool/CustomSetupTool/download.c | 12 ++++++------ tools/CustomSetupTool/CustomSetupTool/extract.c | 9 ++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index d76238c99456..227dc7e41df4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -312,12 +312,12 @@ BOOLEAN SetupQueryUpdateData( if (PhIsNullOrEmptyString(Context->SetupFileVersion)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->WebSetupFileDownloadUrl)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->WebSetupFileSignature)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->WebSetupFileVersion)) - goto CleanupExit; + //if (PhIsNullOrEmptyString(Context->WebSetupFileDownloadUrl)) + // goto CleanupExit; + //if (PhIsNullOrEmptyString(Context->WebSetupFileSignature)) + // goto CleanupExit; + //if (PhIsNullOrEmptyString(Context->WebSetupFileVersion)) + // goto CleanupExit; success = TRUE; diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index af1efe92e6ae..a24bb20e315b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -96,9 +96,9 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; - if (!strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) + if (strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) continue; - if (!strstr(zipFileStat.m_filename, "usernotesdb.xml")) + if (strstr(zipFileStat.m_filename, "usernotesdb.xml")) continue; if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) @@ -135,9 +135,9 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; - if (!strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) + if (strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) continue; - if (!strstr(zipFileStat.m_filename, "usernotesdb.xml")) + if (strstr(zipFileStat.m_filename, "usernotesdb.xml")) continue; if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) @@ -279,4 +279,3 @@ BOOLEAN SetupExtractBuild( return FALSE; } - From 5c67dcde1df084263bda334bf1a01b891a5ed2e9 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 27 Sep 2017 17:31:07 +1000 Subject: [PATCH 432/839] SetupTool: Remove CRT functions, Add workqueue support --- .../CustomSetupTool/configpage.c | 2 +- .../CustomSetupTool/download.c | 2 +- .../CustomSetupTool/downloadpage.c | 2 +- .../CustomSetupTool/CustomSetupTool/extract.c | 37 +++++++++---------- .../CustomSetupTool/include/setup.h | 1 + .../CustomSetupTool/CustomSetupTool/install.c | 6 +-- .../CustomSetupTool/CustomSetupTool/update.c | 1 - 7 files changed, 22 insertions(+), 29 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/configpage.c b/tools/CustomSetupTool/CustomSetupTool/configpage.c index f016e5902fb6..b4dbe1e2adb2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/configpage.c +++ b/tools/CustomSetupTool/CustomSetupTool/configpage.c @@ -72,7 +72,7 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK), TRUE); //Button_SetCheck(GetDlgItem(hwndDlg, IDC_KPH_CHECK), TRUE); //Button_SetCheck(GetDlgItem(hwndDlg, IDC_DBGTOOLS_CHECK), TRUE); - //Button_SetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK), TRUE); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK), TRUE); EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 227dc7e41df4..830e8f5939a0 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -591,8 +591,8 @@ BOOLEAN UpdateDownloadUpdateData( PhDereferenceObject(subMessage); PhDereferenceObject(statusMessage); PhDereferenceObject(totalSpeed); - PhDereferenceObject(totalLength); PhDereferenceObject(totalDownloaded); + PhDereferenceObject(totalLength); } } diff --git a/tools/CustomSetupTool/CustomSetupTool/downloadpage.c b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c index 8d82b65a09a4..6779444751ec 100644 --- a/tools/CustomSetupTool/CustomSetupTool/downloadpage.c +++ b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c @@ -97,7 +97,7 @@ INT_PTR CALLBACK SetupPropPage5_WndProc( // Disable Next/Back buttons PropSheet_SetWizButtons(context->DialogHandle, 0); - PhCreateThread2(SetupDownloadProgressThread, context); + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupDownloadProgressThread, context); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index a24bb20e315b..f5c74259fb86 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -92,28 +92,31 @@ BOOLEAN SetupExtractBuild( 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; - if (strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) - continue; - if (strstr(zipFileStat.m_filename, "usernotesdb.xml")) - continue; + fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (!strncmp(zipFileStat.m_filename, "x32\\", 4)) + if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; } else { - if (!strncmp(zipFileStat.m_filename, "x64\\", 4)) + if (PhStartsWithString2(fileName, L"x64\\", TRUE)) continue; - if (!strncmp(zipFileStat.m_filename, "x86\\", 4)) + 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; } @@ -135,30 +138,28 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; - if (strstr(zipFileStat.m_filename, "ProcessHacker.exe.settings.xml")) + fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); + + if (PhFindStringInString(fileName, 0, L"ProcessHacker.exe.settings.xml") != -1) continue; - if (strstr(zipFileStat.m_filename, "usernotesdb.xml")) + if (PhFindStringInString(fileName, 0, L"usernotesdb.xml") != -1) continue; if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (!strncmp(zipFileStat.m_filename, "x32\\", 4)) + if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; - fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - if (PhFindStringInString(fileName, 0, L"x64\\") != -1) PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); } else { - if (!strncmp(zipFileStat.m_filename, "x64\\", 4)) + if (PhStartsWithString2(fileName, L"x64\\", TRUE)) continue; - if (!strncmp(zipFileStat.m_filename, "x86\\", 4)) + if (PhStartsWithString2(fileName, L"x86\\", TRUE)) continue; - fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - if (PhFindStringInString(fileName, 0, L"x32\\") != -1) PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); } @@ -264,18 +265,14 @@ BOOLEAN SetupExtractBuild( } mz_zip_reader_end(&zip_archive); - if (extractPath) PhDereferenceObject(extractPath); - return TRUE; CleanupExit: mz_zip_reader_end(&zip_archive); - if (extractPath) PhDereferenceObject(extractPath); - return FALSE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index e63de392ceca..eef82542cdf9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/tools/CustomSetupTool/CustomSetupTool/install.c b/tools/CustomSetupTool/CustomSetupTool/install.c index 69826e9bc9ca..e54c6642ada3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/install.c +++ b/tools/CustomSetupTool/CustomSetupTool/install.c @@ -76,7 +76,6 @@ NTSTATUS SetupProgressThread( return STATUS_SUCCESS; CleanupExit: - PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); return STATUS_FAIL_CHECK; } @@ -139,8 +138,6 @@ INT_PTR CALLBACK SetupInstallPropPage_WndProc( break; case PSN_SETACTIVE: { - HANDLE threadHandle; - context->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); context->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); context->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); @@ -157,8 +154,7 @@ INT_PTR CALLBACK SetupInstallPropPage_WndProc( { SetupRunning = TRUE; - if (threadHandle = PhCreateThread(0, SetupProgressThread, context)) - NtClose(threadHandle); + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupProgressThread, context); } } break; diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 2bfaff5d7db2..8df1ced0220b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -20,7 +20,6 @@ #include #include -#include #define WM_TASKDIALOGINIT (WM_APP + 550) HWND UpdateDialogHandle = NULL; From de74d68f4bca7dad2194298eaaa2ab546f69cb76 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 28 Sep 2017 02:52:06 +1000 Subject: [PATCH 433/839] Add PhEnumSettings callback --- phlib/include/settings.h | 21 ++++++++++++++++----- phlib/settings.c | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/phlib/include/settings.h b/phlib/include/settings.h index 7cd07eb07b1d..b279298511b3 100644 --- a/phlib/include/settings.h +++ b/phlib/include/settings.h @@ -11,11 +11,11 @@ extern "C" { // 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); \ - } +{ \ + 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) @@ -61,6 +61,17 @@ VOID PhUpdateCachedSettings( VOID ); +// private +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 diff --git a/phlib/settings.c b/phlib/settings.c index 4f38546f7393..ba8c54732783 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -319,6 +319,27 @@ static PVOID PhpLookupSetting( 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 ) From 5af1f81eb4d68f6ed71f0f04861d23faf0379131 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 28 Sep 2017 03:52:40 +1000 Subject: [PATCH 434/839] HardwareDevices: Fix macro usage --- plugins/HardwareDevices/diskoptions.c | 5 +---- plugins/HardwareDevices/netoptions.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index bd252a5171ce..0e7f2668b46e 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -23,9 +23,6 @@ #include "devices.h" #include -#define ITEM_CHECKED (INDEXTOSTATEIMAGEMASK(2)) -#define ITEM_UNCHECKED (INDEXTOSTATEIMAGEMASK(1)) - typedef struct _DISK_ENUM_ENTRY { ULONG DeviceIndex; @@ -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); } diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index 0d5a25d4005c..a23e4a440e29 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -26,9 +26,6 @@ #include #include -#define ITEM_CHECKED (INDEXTOSTATEIMAGEMASK(2)) -#define ITEM_UNCHECKED (INDEXTOSTATEIMAGEMASK(1)) - typedef struct _NET_ENUM_ENTRY { BOOLEAN DevicePresent; @@ -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); } From b1f6127995f817e343f299a91736eb73dcff1e39 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 28 Sep 2017 16:43:37 +1000 Subject: [PATCH 435/839] Fix macro usage --- plugins/ExtraPlugins/disabled.c | 6 +++--- plugins/ExtraPlugins/main.h | 3 --- plugins/HardwareDevices/diskoptions.c | 4 ++-- plugins/HardwareDevices/netoptions.c | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/plugins/ExtraPlugins/disabled.c b/plugins/ExtraPlugins/disabled.c index ff4405df5234..9d57f680ad6a 100644 --- a/plugins/ExtraPlugins/disabled.c +++ b/plugins/ExtraPlugins/disabled.c @@ -47,7 +47,7 @@ VOID PhAddDisabledPlugins( lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, PhGetString(displayText), displayText); PhReleaseQueuedLockExclusive(&Context->ListLock); - ListView_SetItemState(Context->ListViewHandle, lvItemIndex, ITEM_CHECKED, LVIS_STATEIMAGEMASK); + ListView_SetCheckState(Context->ListViewHandle, lvItemIndex, TRUE); } } @@ -143,7 +143,7 @@ INT_PTR CALLBACK DisabledPluginsDlgProc( { switch (listView->uNewState & LVIS_STATEIMAGEMASK) { - case 0x2000: // checked + case INDEXTOSTATEIMAGEMASK(2): // checked { PPH_STRING param = (PPH_STRING)listView->lParam; @@ -163,7 +163,7 @@ INT_PTR CALLBACK DisabledPluginsDlgProc( //context->OptionsChanged = TRUE; } break; - case 0x1000: // unchecked + case INDEXTOSTATEIMAGEMASK(1): // unchecked { PPH_STRING param = (PPH_STRING)listView->lParam; diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h index 6ccbc8adda4b..36be49543ef1 100644 --- a/plugins/ExtraPlugins/main.h +++ b/plugins/ExtraPlugins/main.h @@ -60,9 +60,6 @@ ((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 diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index 0e7f2668b46e..01059908b1d8 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -663,7 +663,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 +678,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; diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index a23e4a440e29..c658ce3607fa 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -787,7 +787,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; @@ -802,7 +802,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; From 86cf15c228dfd185beb23fa1d9e5720c4e9d641b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 04:53:09 +1000 Subject: [PATCH 436/839] DotNetTools: Fix macro usage --- plugins/DotNetTools/counters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/DotNetTools/counters.c b/plugins/DotNetTools/counters.c index 669831c546db..badf758550f0 100644 --- a/plugins/DotNetTools/counters.c +++ b/plugins/DotNetTools/counters.c @@ -60,7 +60,7 @@ PVOID GetLegacyBlockTableEntry( // Directory has offset in bytes of block ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; - return ((PBYTE)IpcBlock) + offsetBase + offsetEntry; + return PTR_ADD_OFFSET(IpcBlock, offsetBase + offsetEntry); } else { @@ -71,7 +71,7 @@ PVOID GetLegacyBlockTableEntry( // Directory has offset in bytes of block ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; - return ((PBYTE)IpcBlock) + offsetBase + offsetEntry; + return PTR_ADD_OFFSET(IpcBlock, offsetBase + offsetEntry); } } From 29d62de5116a21ac0d80893133459501314c93a9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 04:53:50 +1000 Subject: [PATCH 437/839] HardwareDevices: Fix options window text clipping --- plugins/HardwareDevices/diskoptions.c | 2 ++ plugins/HardwareDevices/netoptions.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index 01059908b1d8..968ec97569f0 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -646,6 +646,8 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( case WM_SIZE: { PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; case WM_NOTIFY: diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index c658ce3607fa..e6a24711514f 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -744,6 +744,8 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( case WM_SIZE: { PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; case WM_COMMAND: @@ -767,6 +769,8 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( ListView_DeleteAllItems(context->ListViewHandle); FindNetworkAdapters(context); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; } From eaa227c0255c5a134bf59647b02e694f833be4e6 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 18:05:11 +1000 Subject: [PATCH 438/839] Update settings exports --- phlib/include/settings.h | 13 +++++++++++++ phlib/settings.c | 30 +++++++++--------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/phlib/include/settings.h b/phlib/include/settings.h index b279298511b3..ba1b03718fd0 100644 --- a/phlib/include/settings.h +++ b/phlib/include/settings.h @@ -62,6 +62,19 @@ VOID PhUpdateCachedSettings( ); // 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 diff --git a/phlib/settings.c b/phlib/settings.c index ba8c54732783..bb78e121b21b 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -57,18 +57,6 @@ 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 @@ -141,7 +129,7 @@ static ULONG PhpGetCurrentScale( return dpi; } -static PPH_STRING PhpSettingToString( +PPH_STRING PhSettingToString( _In_ PH_SETTING_TYPE Type, _In_ PPH_SETTING Setting ) @@ -181,7 +169,7 @@ static PPH_STRING PhpSettingToString( return PhReferenceEmptyString(); } -static BOOLEAN PhpSettingFromString( +BOOLEAN PhSettingFromString( _In_ PH_SETTING_TYPE Type, _In_ PPH_STRINGREF StringRef, _In_opt_ PPH_STRING String, @@ -685,14 +673,14 @@ VOID PhConvertIgnoredSettings( { PhpFreeSettingValue(setting->Type, setting); - if (!PhpSettingFromString( + if (!PhSettingFromString( setting->Type, &((PPH_STRING)ignoredSetting->u.Pointer)->sr, ignoredSetting->u.Pointer, setting )) { - PhpSettingFromString( + PhSettingFromString( setting->Type, &setting->DefaultValue, NULL, @@ -800,14 +788,14 @@ NTSTATUS PhLoadSettings( { PhpFreeSettingValue(setting->Type, setting); - if (!PhpSettingFromString( + if (!PhSettingFromString( setting->Type, &settingValue->sr, settingValue, setting )) { - PhpSettingFromString( + PhSettingFromString( setting->Type, &setting->DefaultValue, NULL, @@ -912,7 +900,7 @@ NTSTATUS PhSaveSettings( { PPH_STRING settingValue; - settingValue = PhpSettingToString(setting->Type, setting); + settingValue = PhSettingToString(setting->Type, setting); PhpCreateSettingElement(topNode, &setting->Name, &settingValue->sr); PhDereferenceObject(settingValue); } @@ -991,7 +979,7 @@ VOID PhResetSettings( while (setting = PhNextEnumHashtable(&enumContext)) { PhpFreeSettingValue(setting->Type, setting); - PhpSettingFromString(setting->Type, &setting->DefaultValue, NULL, setting); + PhSettingFromString(setting->Type, &setting->DefaultValue, NULL, setting); } PhReleaseQueuedLockExclusive(&PhSettingsLock); @@ -1010,7 +998,7 @@ VOID PhAddSetting( setting.DefaultValue = *DefaultValue; memset(&setting.u, 0, sizeof(setting.u)); - PhpSettingFromString(Type, &setting.DefaultValue, NULL, &setting); + PhSettingFromString(Type, &setting.DefaultValue, NULL, &setting); PhAddEntryHashtable(PhSettingsHashtable, &setting); } From ee6a027973c324b47fcbb0db8d9742a960a23cc1 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 20:04:44 +1000 Subject: [PATCH 439/839] Update options window layout, Add advaned options editor --- ProcessHacker/ProcessHacker.rc | 57 +- ProcessHacker/options.c | 1116 ++++++++++++++++++++++---------- ProcessHacker/resource.h | 26 +- 3 files changed, 786 insertions(+), 413 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 4e3105150b98..47f7c8402e2b 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1153,31 +1153,27 @@ BEGIN EDITTEXT IDC_CHOICESIMPLE,7,20,185,13,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_VISIBLE END -IDD_OPTGENERAL DIALOGEX 0, 0, 250, 154 +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,182,12,ES_AUTOHSCROLL + EDITTEXT IDC_SEARCHENGINE,61,7,247,12,ES_AUTOHSCROLL LTEXT "PE viewer:",IDC_STATIC,7,23,35,8 - EDITTEXT IDC_PEVIEWER,61,22,182,12,ES_AUTOHSCROLL + EDITTEXT IDC_PEVIEWER,61,22,247,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 + PUSHBUTTON "Font...",IDC_FONT,179,53,49,14 + PUSHBUTTON "Make default...",IDC_REPLACETASKMANAGER,179,69,72,14 + LTEXT "Graph history length:",IDC_STATIC,106,39,69,8 + EDITTEXT IDC_SAMPLECOUNT,180,38,48,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Automatic",IDC_SAMPLECOUNTAUTOMATIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,233,39,48,10 + CONTROL "",IDC_SETTINGS,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,301,125 + LTEXT "Application font:",IDC_STATIC,121,55,54,8 + RTEXT "Process Hacker is the default Task Manager:",IDC_DEFSTATE,7,71,166,8 END IDD_OPTHIGHLIGHTING DIALOGEX 0, 0, 250, 174 @@ -1325,31 +1321,12 @@ BEGIN RTEXT "Static",IDC_ZSHAREDWS_V,78,167,42,8,SS_ENDELLIPSIS END -IDD_OPTADVANCED DIALOGEX 0, 0, 250, 150 +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 "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 + 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 @@ -2157,9 +2134,9 @@ BEGIN IDD_OPTGENERAL, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 243 + RIGHTMARGIN, 308 TOPMARGIN, 7 - BOTTOMMARGIN, 147 + BOTTOMMARGIN, 213 END IDD_OPTHIGHLIGHTING, DIALOG @@ -2213,9 +2190,9 @@ BEGIN IDD_OPTADVANCED, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 243 + RIGHTMARGIN, 310 TOPMARGIN, 7 - BOTTOMMARGIN, 143 + BOTTOMMARGIN, 218 END IDD_GDIHANDLES, DIALOG diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index d57347a8497a..e095121f8975 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -117,6 +117,22 @@ PPH_OPTIONS_SECTION PhOptionsCreateSection( _In_ PVOID Parameter ); +PPH_OPTIONS_SECTION PhOptionsCreateSectionAdvanced( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ); + +BOOLEAN PhpIsDefaultTaskManager( + VOID + ); + +VOID PhpSetDefaultTaskManager( + _In_ HWND ParentWindowHandle + ); + static HWND PhOptionsWindowHandle = NULL; static PPH_LIST PhOptionsDialogList = NULL; static PH_LAYOUT_MANAGER WindowLayoutManager; @@ -129,7 +145,6 @@ static HIMAGELIST OptionsTreeImageList = NULL; // All static BOOLEAN RestartRequired = FALSE; -static POINT StartLocation; // General static PH_STRINGREF CurrentUserRunKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"); @@ -137,11 +152,11 @@ static BOOLEAN CurrentUserRunPresent; static BOOLEAN CurrentUserRunStartHidden; static HFONT CurrentFontInstance; static PPH_STRING NewFontSelection; +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; -static BOOLEAN OldReplaceTaskMgr; static HWND WindowHandleForElevate; // Highlighting @@ -152,19 +167,18 @@ VOID PhShowOptionsDialog( ) { if (PhStartupParameters.ShowOptions) - StartLocation = PhStartupParameters.Point; + { + PhpSetDefaultTaskManager(ParentWindowHandle); + } else - StartLocation.x = MINLONG; - - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_OPTIONS), - ParentWindowHandle, - PhOptionsDialogProc - ); - - if (!PhStartupParameters.ShowOptions) { + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + ParentWindowHandle, + PhOptionsDialogProc + ); + PhUpdateCachedSettings(); ProcessHacker_SaveAllSettings(PhMainWndHandle); PhInvalidateAllProcessNodes(); @@ -195,48 +209,98 @@ VOID PhShowOptionsDialog( } } } - else - { - // Main window not available. - if (PhSettingsFileName) - PhSaveSettings(PhSettingsFileName->Buffer); - } } static HTREEITEM PhpOptionsTreeViewAddItem( _In_ PWSTR Text, - _In_ PVOID Context + _In_ PVOID Context, + _In_ HTREEITEM InsertAfter ) { TV_INSERTSTRUCT insert; memset(&insert, 0, sizeof(TV_INSERTSTRUCT)); - insert.item.mask = TVIF_TEXT | TVIF_PARAM; - insert.hInsertAfter = TVI_LAST; insert.hParent = TVI_ROOT; + insert.hInsertAfter = InsertAfter; + 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 + ) +{ + HTREEITEM tvItemGeneral = NULL; + HTREEITEM tvItemAdvanced = NULL; + HTREEITEM tvItemCurrent; + + tvItemCurrent = TreeView_GetRoot(OptionsTreeControl); + + while (tvItemCurrent) + { + TVITEM tvItem; + WCHAR buffer[MAX_PATH]; + + tvItem.mask = TVIF_TEXT | TVIF_HANDLE; + tvItem.hItem = tvItemCurrent; + tvItem.cchTextMax = ARRAYSIZE(buffer); + tvItem.pszText = buffer; + + if (TreeView_GetItem(OptionsTreeControl, &tvItem)) + { + if (PhEqualStringZ(buffer, L"Advanced", TRUE)) + { + tvItemAdvanced = tvItemCurrent; + } + else if (PhEqualStringZ(buffer, L"General", TRUE)) + { + tvItemGeneral = tvItemCurrent; + } + } + + tvItemCurrent = TreeView_GetNextSibling(OptionsTreeControl, tvItemCurrent); + } + + if (Hide) + { + if (tvItemAdvanced) + TreeView_DeleteItem(OptionsTreeControl, tvItemAdvanced); + } + else + { + static PH_STRINGREF sectionName = PH_STRINGREF_INIT(L"Advanced"); + + if (tvItemGeneral) + { + PhpOptionsTreeViewAddItem( + sectionName.Buffer, + PhOptionsFindSection(§ionName), + tvItemGeneral + ); + } + } +} + static PPH_OPTIONS_SECTION PhpTreeViewGetSelectedSection( _In_ HTREEITEM SelectedTreeItem ) { - TVITEM item; + TVITEM tvItem; if (!SelectedTreeItem) return NULL; - item.mask = TVIF_PARAM | TVIF_HANDLE; - item.hItem = SelectedTreeItem; + tvItem.mask = TVIF_PARAM | TVIF_HANDLE; + tvItem.hItem = SelectedTreeItem; - if (!TreeView_GetItem(OptionsTreeControl, &item)) + if (!TreeView_GetItem(OptionsTreeControl, &tvItem)) return NULL; - return (PPH_OPTIONS_SECTION)item.lParam; + return (PPH_OPTIONS_SECTION)tvItem.lParam; } INT_PTR CALLBACK PhOptionsDialogProc( @@ -255,16 +319,7 @@ INT_PTR CALLBACK PhOptionsDialogProc( SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); SendMessage(PhOptionsWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); - // Set the location of the options window. - if (StartLocation.x == MINLONG) - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - } - else - { - SetWindowPos(hwndDlg, NULL, StartLocation.x, StartLocation.y, 0, 0, - SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER); - } + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); OptionsTreeImageList = ImageList_Create(2, 22, ILC_COLOR, 1, 1); OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); @@ -294,30 +349,22 @@ INT_PTR CALLBACK PhOptionsDialogProc( SectionList = PhCreateList(8); CurrentSection = NULL; - if (PhStartupParameters.ShowOptions) - { - // Disable all pages other than Advanced. - section = PhOptionsCreateSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); - } - else - { - section = PhOptionsCreateSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); - PhOptionsCreateSection(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); - PhOptionsCreateSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, NULL); - PhOptionsCreateSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); - PhOptionsCreateSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); + section = PhOptionsCreateSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); + PhOptionsCreateSectionAdvanced(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + PhOptionsCreateSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, 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; + if (PhPluginsEnabled) + { + PH_PLUGIN_OPTIONS_POINTERS pointers; - pointers.WindowHandle = PhOptionsWindowHandle; - pointers.CreateSection = PhOptionsCreateSection; - pointers.FindSection = PhOptionsFindSection; - pointers.EnterSectionView = PhOptionsEnterSectionView; + pointers.WindowHandle = PhOptionsWindowHandle; + pointers.CreateSection = PhOptionsCreateSection; + pointers.FindSection = PhOptionsFindSection; + pointers.EnterSectionView = PhOptionsEnterSectionView; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &pointers); - } + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &pointers); } PhOptionsEnterSectionView(section); @@ -503,7 +550,32 @@ PPH_OPTIONS_SECTION PhOptionsCreateSection( PhAddItemList(SectionList, section); - PhpOptionsTreeViewAddItem(Name, section); + PhpOptionsTreeViewAddItem(Name, section, TVI_LAST); + + return section; +} + +PPH_OPTIONS_SECTION PhOptionsCreateSectionAdvanced( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ) +{ + PPH_OPTIONS_SECTION section; + + section = PhAllocate(sizeof(PH_OPTIONS_SECTION)); + memset(section, 0, 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; } @@ -625,17 +697,30 @@ VOID PhOptionsCreateSectionDialog( #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) +{ \ + 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; @@ -737,151 +822,6 @@ static VOID WriteCurrentUserRun( } } -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; - - 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: - { - 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); - } - - if (CurrentFontInstance) - DeleteObject(CurrentFontInstance); - - PhClearReference(&NewFontSelection); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - 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; - } - - return FALSE; -} - static BOOLEAN PathMatchesPh( _In_ PPH_STRING Path ) @@ -911,158 +851,253 @@ static BOOLEAN PathMatchesPh( return match; } -VOID PhpAdvancedPageLoad( - _In_ HWND hwndDlg +BOOLEAN PhpIsDefaultTaskManager( + VOID ) { - HWND changeButton; + HANDLE taskmgrKeyHandle; + BOOLEAN alreadyReplaced = FALSE; - 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"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); + if (NT_SUCCESS(PhOpenKey( + &taskmgrKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &TaskMgrImageOptionsKeyName, + 0 + ))) + { + PhClearReference(&OldTaskMgrDebugger); - SetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, PhGetIntegerSetting(L"SampleCount"), FALSE); - SetDlgItemCheckForSetting(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); + if (OldTaskMgrDebugger = PhQueryRegistryString(taskmgrKeyHandle, L"Debugger")) + { + alreadyReplaced = PathMatchesPh(OldTaskMgrDebugger); + } - if (PhGetIntegerSetting(L"SampleCountAutomatic")) - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), FALSE); + NtClose(taskmgrKeyHandle); + } - // Replace Task Manager + return alreadyReplaced; +} - changeButton = GetDlgItem(hwndDlg, IDC_CHANGE); +VOID PhpSetDefaultTaskManager( + _In_ HWND ParentWindowHandle + ) +{ + PWSTR message; - if (PhGetOwnTokenAttributes().Elevated) - { - ShowWindow(changeButton, SW_HIDE); - } + if (PhpIsDefaultTaskManager()) + message = L"Do you want to restore the default Windows Task Manager?"; else - { - SendMessage(changeButton, BCM_SETSHIELD, 0, TRUE); - } + 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) { - HANDLE taskmgrKeyHandle = NULL; - ULONG disposition; - BOOLEAN success = FALSE; - BOOLEAN alreadyReplaced = FALSE; + NTSTATUS status; + HANDLE taskmgrKeyHandle; + UNICODE_STRING valueName; - // See if we can write to the key. if (NT_SUCCESS(PhCreateKey( &taskmgrKeyHandle, KEY_READ | KEY_WRITE, PH_KEY_LOCAL_MACHINE, &TaskMgrImageOptionsKeyName, + OBJ_OPENIF, 0, - 0, - &disposition - ))) - { - success = TRUE; - } - - if (taskmgrKeyHandle || NT_SUCCESS(PhOpenKey( - &taskmgrKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &TaskMgrImageOptionsKeyName, - 0 + NULL ))) { - PhClearReference(&OldTaskMgrDebugger); + RtlInitUnicodeString(&valueName, L"Debugger"); - if (OldTaskMgrDebugger = PhQueryRegistryString(taskmgrKeyHandle, L"Debugger")) + if (PhpIsDefaultTaskManager()) + { + status = NtDeleteValueKey(taskmgrKeyHandle, &valueName); + } + else { - alreadyReplaced = PathMatchesPh(OldTaskMgrDebugger); + 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); } + if (!NT_SUCCESS(status)) + PhShowStatus(ParentWindowHandle, L"Unable to replace Task Manager", status, 0); + NtClose(taskmgrKeyHandle); } - if (!success) - EnableWindow(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER), FALSE); - - OldReplaceTaskMgr = alreadyReplaced; - Button_SetCheck(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER), alreadyReplaced ? BST_CHECKED : BST_UNCHECKED); + if (PhSettingsFileName) + PhSaveSettings(PhSettingsFileName->Buffer); } } -VOID PhpAdvancedPageSave( - _In_ HWND hwndDlg +VOID PhpRefreshTaskManagerState( + _In_ HWND WindowHandle ) { - 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"); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); - - sampleCount = GetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, NULL, FALSE); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); + if (!PhGetOwnTokenAttributes().Elevated) + { + SendMessage(GetDlgItem(WindowHandle, IDC_REPLACETASKMANAGER), BCM_SETSHIELD, 0, TRUE); + } - if (sampleCount == 0) - sampleCount = 1; + 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..."); + } +} - if (sampleCount != PhGetIntegerSetting(L"SampleCount")) - RestartRequired = TRUE; +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_NETWORK_RESOLVE, + PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, + PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, + PHP_OPTIONS_INDEX_ENABLE_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; + +VOID PhpSetListViewItemState( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ BOOLEAN Hide + ) +{ + ListView_SetItemState(ListViewHandle, Index, INDEXTOSTATEIMAGEMASK(Hide ? 0 : 1), LVIS_STATEIMAGEMASK); +} - PhSetIntegerSetting(L"SampleCount", sampleCount); +static VOID PhpAdvancedPageLoad( + _In_ HWND hwndDlg + ) +{ + HWND listViewHandle; + + listViewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); - // Replace Task Manager - if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER))) + SetDlgItemInt(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_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_CYCLE_CPU_USAGE, L"Enable cycle-based CPU usage", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"Check images 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", 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_ENABLE_WARNINGS, L"EnableWarnings"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_PLUGINS, L"EnablePlugins"); + 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_STAGE2, L"EnableStage2"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_NETWORK_RESOLVE, L"EnableNetworkResolve"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, L"PropagateCpuUsage"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"EnableInstantTooltips"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); + 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"); + + if (CurrentUserRunPresent) { - NTSTATUS status; - HANDLE taskmgrKeyHandle; - BOOLEAN replaceTaskMgr; - UNICODE_STRING valueName; + ListView_SetCheckState(listViewHandle, PHP_OPTIONS_INDEX_START_ATLOGON, TRUE); - replaceTaskMgr = Button_GetCheck(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER)) == BST_CHECKED; + if (CurrentUserRunStartHidden) + ListView_SetCheckState(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN, TRUE); + } + else + { + PhpSetListViewItemState(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN, TRUE); + } +} - 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"); +static VOID PhpAdvancedPageSave( + _In_ HWND hwndDlg + ) +{ + HWND listViewHandle; + ULONG sampleCount; - if (replaceTaskMgr) - { - PPH_STRING quotedFileName; + listViewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); + sampleCount = GetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, NULL, FALSE); - 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); - } + SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); + + if (sampleCount == 0) + sampleCount = 1; - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to replace Task Manager", status, 0); + if (sampleCount != PhGetIntegerSetting(L"SampleCount")) + RestartRequired = TRUE; - NtClose(taskmgrKeyHandle); - } - } - } + 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", GetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, NULL, FALSE)); + + 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"); + 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"); + 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_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"EnableStage2"); + 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 + ); } -NTSTATUS PhpElevateAdvancedThreadStart( +static NTSTATUS PhpElevateAdvancedThreadStart( _In_ PVOID Parameter ) { @@ -1085,66 +1120,133 @@ NTSTATUS PhpElevateAdvancedThreadStart( return STATUS_SUCCESS; } -INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( +INT_PTR CALLBACK PhpOptionsGeneralDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { + static PH_LAYOUT_MANAGER LayoutManager; + switch (uMsg) { case WM_INITDIALOG: { - PhpAdvancedPageLoad(hwndDlg); + HWND comboBoxHandle; + HWND listviewHandle; + ULONG i; + LOGFONT font; + + GeneralListviewImageList = ImageList_Create(2, 20, ILC_COLOR, 1, 1); + comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); + listviewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); - if (PhStartupParameters.ShowOptions) + 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); + + 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 < ARRAYSIZE(PhSizeUnitNames); i++) + ComboBox_AddString(comboBoxHandle, PhSizeUnitNames[i]); + + if (PhMaxSizeUnit != -1) + ComboBox_SetCurSel(comboBoxHandle, PhMaxSizeUnit); + else + ComboBox_SetCurSel(comboBoxHandle, ARRAYSIZE(PhSizeUnitNames) - 1); + + SetDlgItemText(hwndDlg, IDC_SEARCHENGINE, PhaGetStringSetting(L"SearchEngine")->Buffer); + SetDlgItemText(hwndDlg, IDC_PEVIEWER, PhaGetStringSetting(L"ProgramInspectExecutables")->Buffer); + + SetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, PhGetIntegerSetting(L"IconProcesses"), FALSE); + + ReadCurrentUserRun(); + + // Set the font of the button for a nice preview. + if (GetCurrentFont(&font)) { - // 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); - - EnableWindow(GetDlgItem(PhOptionsWindowHandle, IDC_RESET), FALSE); - EnableWindow(GetDlgItem(PhOptionsWindowHandle, IDC_CLEANUP), FALSE); + CurrentFontInstance = CreateFontIndirect(&font); + + if (CurrentFontInstance) + SendMessage(GetDlgItem(hwndDlg, IDC_FONT), WM_SETFONT, (WPARAM)CurrentFontInstance, TRUE); } + + PhpAdvancedPageLoad(hwndDlg); + PhpRefreshTaskManagerState(hwndDlg); } break; case WM_DESTROY: { + if (NewFontSelection) + { + PhSetStringSetting2(L"Font", &NewFontSelection->sr); + PostMessage(PhMainWndHandle, WM_PH_UPDATE_FONT, 0, 0); + } + PhpAdvancedPageSave(hwndDlg); + if (CurrentFontInstance) + DeleteObject(CurrentFontInstance); + + PhClearReference(&NewFontSelection); PhClearReference(&OldTaskMgrDebugger); + + ImageList_Destroy(GeneralListviewImageList); + + PhDeleteLayoutManager(&LayoutManager); } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { - case IDC_CHANGE: + case IDC_FONT: { - RECT windowRect; + LOGFONT font; + CHOOSEFONT chooseFont; - // Save the options so they don't get "overwritten" when - // WM_PH_CHILD_EXIT gets sent. - PhpAdvancedPageSave(hwndDlg); + 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); - GetWindowRect(GetParent(GetParent(hwndDlg)), &windowRect); + CurrentFontInstance = CreateFontIndirect(&font); + SendMessage(GetDlgItem(hwndDlg, IDC_FONT), WM_SETFONT, (WPARAM)CurrentFontInstance, TRUE); + } + } + break; + case IDC_REPLACETASKMANAGER: + { WindowHandleForElevate = hwndDlg; PhCreateThread2(PhpElevateAdvancedThreadStart, PhFormatString( - L"-showoptions -hwnd %Ix -point %u,%u", - (ULONG_PTR)GetParent(GetParent(hwndDlg)), - windowRect.left + 20, - windowRect.top + 20 + L"-showoptions -hwnd %Ix", + (ULONG_PTR)GetParent(GetParent(hwndDlg)) )); } break; @@ -1158,7 +1260,320 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( break; case WM_PH_CHILD_EXIT: { - PhpAdvancedPageLoad(hwndDlg); + PhpRefreshTaskManagerState(hwndDlg); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + //if (header->code == LVN_ITEMCHANGING) + //{ + // 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: + // { + // if (PhShowMessage2( + // PhOptionsWindowHandle, + // TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, + // TD_WARNING_ICON, + // L"WARNING", + // L"DO NOT change advanced settings unless you know what you're doing..." + // ) == IDOK) + // { + // SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + // return FALSE; + // } + // else + // { + // SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + // return TRUE; + // } + // } + // break; + // } + // } + // break; + // } + // } + //} + + if (header->code == 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_START_ATLOGON: + { + PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, FALSE); + } + break; + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(FALSE); + } + break; + } + } + break; + case INDEXTOSTATEIMAGEMASK(1): // unchecked + { + switch (listView->iItem) + { + case PHP_OPTIONS_INDEX_START_ATLOGON: + { + PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, TRUE); + } + break; + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(TRUE); + } + 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((HFONT)SendMessage(control, WM_GETFONT, 0, 0), FW_BOLD); + // + // SetBkMode(hdc, TRANSPARENT); + // + // if (!PhpIsDefaultTaskManager()) + // { + // SelectObject(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))); + + SetWindowText(hwndDlg, L"Setting Editor"); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + SetProp(hwndDlg, PhMakeContextAtom(), 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); + + SetDlgItemText(hwndDlg, IDC_NAME, setting->Name.Buffer); + SetDlgItemText(hwndDlg, IDC_VALUE, PH_AUTO_T(PH_STRING, PhSettingToString(setting->Type, setting))->Buffer); + + EnableWindow(GetDlgItem(hwndDlg, IDC_NAME), FALSE); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); + } + break; + case WM_DESTROY: + { + RemoveProp(hwndDlg, PhMakeContextAtom()); + + 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 = (PPH_SETTING)GetProp(hwndDlg, PhMakeContextAtom()); + 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); + } + 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; } @@ -1246,9 +1661,6 @@ INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( return FALSE; } -#define CROSS_INDEX 0 -#define TICK_INDEX 1 - typedef struct _COLOR_ITEM { PWSTR SettingName; diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 80b0825bde89..cb5262a4eadc 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -208,7 +208,6 @@ #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 @@ -218,6 +217,7 @@ #define IDC_INTEGRITY 1079 #define IDC_MORE 1079 #define IDC_VIEWMITIGATION 1080 +#define IDC_REPLACETASKMANAGER 1080 #define IDC_VIEWPARENTPROCESS 1081 #define IDC_OPENFILENAME 1082 #define IDC_LIMITS 1083 @@ -318,16 +318,10 @@ #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 @@ -389,13 +383,10 @@ #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 @@ -409,21 +400,16 @@ #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 @@ -438,8 +424,6 @@ #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 @@ -517,7 +501,6 @@ #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 @@ -527,7 +510,6 @@ #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 @@ -539,6 +521,8 @@ #define IDC_TREELIST 1391 #define IDC_SECTIONTREE 1393 #define IDC_INFO 1396 +#define IDC_DEFSTATE 1398 +#define IDC_SETTINGS 1399 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -744,9 +728,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 238 +#define _APS_NEXT_RESOURCE_VALUE 239 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1397 +#define _APS_NEXT_CONTROL_VALUE 1400 #define _APS_NEXT_SYMED_VALUE 169 #endif #endif From d995e2e7dd4b04a18ed917e9be47033996980c27 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 20:51:45 +1000 Subject: [PATCH 440/839] Fix json file functions --- phlib/include/json.h | 17 +++- phlib/json.c | 17 +++- phlib/jsonc/json_util.c | 177 +++++++++++++++++++++++++--------------- phlib/jsonc/json_util.h | 6 +- 4 files changed, 144 insertions(+), 73 deletions(-) diff --git a/phlib/include/json.h b/phlib/include/json.h index fded874784d0..a8d43b25acba 100644 --- a/phlib/include/json.h +++ b/phlib/include/json.h @@ -148,12 +148,27 @@ PhGetJsonArrayIndexObject( ); PHLIBAPI -PVOID +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 diff --git a/phlib/json.c b/phlib/json.c index d22c766033da..fa889476750e 100644 --- a/phlib/json.c +++ b/phlib/json.c @@ -156,7 +156,7 @@ PVOID PhGetJsonArrayIndexObject( return json_object_array_get_idx(Object, Index); } -PVOID PhGetJsonObjectAsArrayList( +PPH_LIST PhGetJsonObjectAsArrayList( _In_ PVOID Object ) { @@ -180,3 +180,18 @@ PVOID PhGetJsonObjectAsArrayList( return listArray; } + +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); +} \ No newline at end of file diff --git a/phlib/jsonc/json_util.c b/phlib/jsonc/json_util.c index fda5d1ec00a7..077777afe9e9 100644 --- a/phlib/jsonc/json_util.c +++ b/phlib/jsonc/json_util.c @@ -9,6 +9,9 @@ * */ +#include "..\include\phbase.h" +#include "..\include\phnative.h" + #include "config.h" #undef realloc @@ -65,85 +68,123 @@ 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 json_object* json_object_from_file(wchar_t *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; + NTSTATUS status; + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + struct json_object *obj = NULL; + + status = PhCreateFileWin32( + &fileHandle, + filename, + FILE_GENERIC_WRITE, + 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] = 0; + + obj = json_tokener_parse(data); + + PhFree(data); + } + + return obj; } /* extended "format and write to file" function */ -int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) +int json_object_to_file_ext(wchar_t *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; + NTSTATUS status; + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + PSTR json_str; + + if (!(json_str = 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; } // backwards compatible "format and write to file" function -int json_object_to_file(const char *filename, struct json_object *obj) +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); + return json_object_to_file_ext(FileName, obj, JSON_C_TO_STRING_PRETTY); } int json_parse_double(const char *buf, double *retval) diff --git a/phlib/jsonc/json_util.h b/phlib/jsonc/json_util.h index 1005e58c5b0d..e9aea4876a66 100644 --- a/phlib/jsonc/json_util.h +++ b/phlib/jsonc/json_util.h @@ -21,9 +21,9 @@ extern "C" { #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 struct json_object* json_object_from_file(wchar_t *filename); +extern int json_object_to_file(wchar_t *FileName, struct json_object *obj); +extern int json_object_to_file_ext(wchar_t *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); From 1a1a53b70243c094df1991039f249b15d3b08429 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 29 Sep 2017 21:33:20 +1000 Subject: [PATCH 441/839] Add inital RS3 types --- phnt/include/ntexapi.h | 3 +++ phnt/include/ntioapi.h | 1 + phnt/include/ntmmapi.h | 4 +++- phnt/include/ntpsapi.h | 9 +++++++++ phnt/include/ntregapi.h | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 760260980a90..6d2e8ac48ce7 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1390,6 +1390,9 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemCodeIntegrityUnlockInformation, // SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION // 190 SystemIntegrityQuotaInformation, SystemFlushInformation, // q: SYSTEM_FLUSH_INFORMATION + SystemProcessorIdleMaskInformation, // since REDSTONE3 + SystemSecureDumpEncryptionInformation, + SystemWriteConstraintInformation, MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; diff --git a/phnt/include/ntioapi.h b/phnt/include/ntioapi.h index c13aa19c3721..6bbcc5652ef9 100644 --- a/phnt/include/ntioapi.h +++ b/phnt/include/ntioapi.h @@ -242,6 +242,7 @@ typedef enum _FILE_INFORMATION_CLASS FileRenameInformationExBypassAccessCheck, FileDesiredStorageClassInformation, // FILE_DESIRED_STORAGE_CLASS_INFORMATION // since REDSTONE2 FileStatInformation, // FILE_STAT_INFORMATION + FileMemoryPartitionInformation, // since REDSTONE3 FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 9499ee236911..9f282024dfe5 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -66,7 +66,9 @@ typedef enum _MEMORY_INFORMATION_CLASS MemorySharedCommitInformation, // MEMORY_SHARED_COMMIT_INFORMATION MemoryImageInformation, // MEMORY_IMAGE_INFORMATION MemoryRegionInformationEx, - MemoryPrivilegedBasicInformation + MemoryPrivilegedBasicInformation, + MemoryEnclaveImageInformation, // since REDSTONE3 + MemoryBasicInformationCapped } MEMORY_INFORMATION_CLASS; #if (PHNT_MODE == PHNT_MODE_KERNEL) diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index cc957c2baf71..0816befb2b34 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -180,6 +180,13 @@ typedef enum _PROCESSINFOCLASS ProcessDisableSystemAllowedCpuSets, ProcessWakeInformation, // PROCESS_WAKE_INFORMATION ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE + ProcessManageWritesToExecutableMemory, // since REDSTONE3 + ProcessCaptureTrustletLiveDump, + ProcessTelemetryCoverage, + ProcessEnclaveInformation, + ProcessEnableReadWriteVmLogging, + ProcessUptimeInformation, + ProcessImageSection, MaxProcessInfoClass } PROCESSINFOCLASS; #endif @@ -235,6 +242,8 @@ typedef enum _THREADINFOCLASS ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ThreadDbgkWerReportActive, ThreadAttachContainer, + ThreadManageWritesToExecutableMemory, // since REDSTONE3 + ThreadPowerThrottlingState, MaxThreadInfoClass } THREADINFOCLASS; #endif diff --git a/phnt/include/ntregapi.h b/phnt/include/ntregapi.h index d5d874173322..0a3a4c8be773 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, MaxKeySetInfoClass } KEY_SET_INFORMATION_CLASS; From 5464c0cae5907ec5dcb0f619b8db2a9c3640fca0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 30 Sep 2017 22:50:12 +1000 Subject: [PATCH 442/839] Fix incorrect SYSDBG types --- phnt/include/ntexapi.h | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 6d2e8ac48ce7..70e2310acc32 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -3156,35 +3156,31 @@ typedef struct _SYSDBG_TRIAGE_DUMP } SYSDBG_TRIAGE_DUMP, *PSYSDBG_TRIAGE_DUMP; // private -typedef struct _SYSDBG_LIVEDUMP_CONTROL_FLAGS +typedef union _SYSDBG_LIVEDUMP_CONTROL_FLAGS { - union + struct { - struct - { - ULONG UseDumpStorageStack : 1; - ULONG CompressMemoryPagesData : 1; - ULONG IncludeUserSpaceMemoryPages : 1; - ULONG Reserved : 28; - }; - ULONG AsUlong; + ULONG UseDumpStorageStack : 1; + ULONG CompressMemoryPagesData : 1; + ULONG IncludeUserSpaceMemoryPages : 1; + ULONG Reserved : 29; }; + ULONG AsUlong; } SYSDBG_LIVEDUMP_CONTROL_FLAGS, *PSYSDBG_LIVEDUMP_CONTROL_FLAGS; // private -typedef struct _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES +typedef union _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES { - union + struct { - struct - { - ULONG HypervisorPages : 1; - ULONG Reserved : 31; - }; - ULONG AsUlong; + 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 { @@ -3367,7 +3363,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]; From d5338e96fa5a918161b0ca9ca655274bc7ff37b7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 1 Oct 2017 03:18:29 +1100 Subject: [PATCH 443/839] SetupTool: Fix creating ifeo key --- tools/CustomSetupTool/CustomSetupTool/setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 9933ad96a515..48d5f8a41d72 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -686,10 +686,10 @@ VOID SetupCreateImageFileExecutionOptions( if (NT_SUCCESS(PhCreateKey( &keyHandle, - KEY_WRITE | DELETE, + KEY_WRITE, PH_KEY_LOCAL_MACHINE, &PhImageOptionsKeyName, - 0, + OBJ_OPENIF, 0, NULL ))) From 4df29b79242263d89bf79eb836cc0d7854db8ab6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 1 Oct 2017 04:08:26 +1100 Subject: [PATCH 444/839] Add undocumented RunFileDlg flags (Vista+) --- phlib/include/guisup.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index 60ac7efe1267..f7d7abb6d655 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -20,8 +20,10 @@ typedef BOOL (WINAPI *_IsImmersiveProcess)( #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 { From 85a1bde535dc01de088948527abbe6c47def8ec3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 1 Oct 2017 05:14:20 +1100 Subject: [PATCH 445/839] Reduce 0-byte string allocations (experimental) orang? --- phlib/include/phbasesup.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index cfaad93639d0..7dd9bb837a28 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -1131,12 +1131,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); } @@ -1146,16 +1156,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 From 03afda7eb8af18258cbcb5cb2e7e0a58fd31f57c Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 4 Oct 2017 11:55:37 +1100 Subject: [PATCH 446/839] NetworkTools: Fix ping dialog focus --- plugins/NetworkTools/ping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 0d94de17499a..4fd646f6e450 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -350,7 +350,7 @@ INT_PTR CALLBACK NetworkPingWndProc( EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } - return TRUE; + break; case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) From 77b3cb7e39a1daad02239537477051457e5d0a64 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 4 Oct 2017 11:57:36 +1100 Subject: [PATCH 447/839] ToolStatus: Fix toolbar/statusbar customize dialog focus --- plugins/ToolStatus/customizesb.c | 14 +++++++++++++- plugins/ToolStatus/customizetb.c | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index e8ce99115092..edb1929a8caf 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -328,7 +328,7 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( SendMessage(context->DialogHandle, WM_NEXTDLGCTL, (WPARAM)context->CurrentListHandle, TRUE); } - return TRUE; + break; case WM_DESTROY: { StatusBarSaveSettings(); @@ -394,6 +394,11 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( CustomizeAddStatusBarItem(context, index, indexto); } break; + case LBN_KILLFOCUS: + { + Button_Enable(context->AddButtonHandle, FALSE); + } + break; } } break; @@ -470,6 +475,13 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( CustomizeRemoveStatusBarItem(context, index); } break; + case LBN_KILLFOCUS: + { + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + Button_Enable(context->RemoveButtonHandle, FALSE); + } + break; } } break; diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index 27330c0c74ba..7e6e8eb74f28 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -365,6 +365,7 @@ VOID CustomizeLoadToolbarItems( // Disable buttons Button_Enable(Context->MoveUpButtonHandle, FALSE); Button_Enable(Context->MoveDownButtonHandle, FALSE); + Button_Enable(Context->AddButtonHandle, FALSE); Button_Enable(Context->RemoveButtonHandle, FALSE); } @@ -533,7 +534,7 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( SendMessage(context->DialogHandle, WM_NEXTDLGCTL, (WPARAM)context->CurrentListHandle, TRUE); } - return TRUE; + break; case WM_DESTROY: { ToolbarSaveButtonSettings(); @@ -558,6 +559,11 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { + case LBN_SELCHANGE: + { + Button_Enable(context->AddButtonHandle, TRUE); + } + break; case LBN_DBLCLK: { INT index; @@ -575,6 +581,11 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( CustomizeAddToolbarItem(context, index, indexto); } break; + case LBN_KILLFOCUS: + { + Button_Enable(context->AddButtonHandle, FALSE); + } + break; } } break; @@ -650,6 +661,13 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( CustomizeRemoveToolbarItem(context, index); } break; + case LBN_KILLFOCUS: + { + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + Button_Enable(context->RemoveButtonHandle, FALSE); + } + break; } } break; From 222cc32ed44cce1768eb816d162d25373a77436a Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 4 Oct 2017 12:35:55 +1100 Subject: [PATCH 448/839] NetworkTools: Fix tracert crash --- plugins/NetworkTools/tracert.c | 6 ++- plugins/NetworkTools/tracetree.c | 66 +++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 75f8e2b0d609..311dfe47787f 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -301,13 +301,15 @@ NTSTATUS NetworkTracertThreadStart( for (ULONG i = 0; i < DEFAULT_MAXIMUM_HOPS; i++) { + PTRACERT_ROOT_NODE node; IN_ADDR last4ReplyAddress = in4addr_any; IN6_ADDR last6ReplyAddress = in6addr_any; if (context->Cancel) break; - PTRACERT_ROOT_NODE node = AddTracertNode(context, pingOptions.Ttl); + node = AddTracertNode(context, pingOptions.Ttl); + PhReferenceObject(node); for (ULONG ii = 0; ii < DEFAULT_MAXIMUM_PINGS; ii++) { @@ -427,6 +429,8 @@ NTSTATUS NetworkTracertThreadStart( } } + PhDereferenceObject(node); + if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) { if (!memcmp(&last4ReplyAddress, &((PSOCKADDR_IN)&destinationAddress)->sin_addr, sizeof(IN_ADDR))) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 9327858f7434..0423c418332e 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -24,6 +24,49 @@ #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->CountryString) + PhDereferenceObject(tracertNode->CountryString); + 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]); + } +} + +PTRACERT_ROOT_NODE TracertTreeCreateNode( + VOID + ) +{ + PTRACERT_ROOT_NODE tracertNode; + + tracertNode = PhCreateObject(sizeof(TRACERT_ROOT_NODE), TracertTreeNodeItemType); + memset(tracertNode, 0, sizeof(TRACERT_ROOT_NODE)); + + PhInitializeTreeNewNode(&tracertNode->Node); + + return tracertNode; +} + #define SORT_FUNCTION(Column) TracertTreeNewCompare##Column #define BEGIN_SORT_FUNCTION(Column) static int __cdecl TracertTreeNewCompare##Column( \ _In_ void *_context, \ @@ -146,16 +189,6 @@ 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); } @@ -166,10 +199,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)); @@ -558,6 +588,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), From 2ee8055ebd71053e7cfe4a3f8f6e69f8554c643a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 12:18:45 +1100 Subject: [PATCH 449/839] Fix RS3 sdk conflict --- ProcessHacker/ProcessHacker.rc | 2 +- ProcessHacker/miniinfo.c | 14 +++++++------- ProcessHacker/resource.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 47f7c8402e2b..ffb1f8741c04 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1812,7 +1812,7 @@ 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 + 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 diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index a6695a217eb1..e1d3489d7296 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -352,7 +352,7 @@ VOID PhMipContainerOnShowWindow( for (i = 0; i < MaxMiniInfoPinType; i++) PhMipPinCounts[i] = 0; - Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), BST_UNCHECKED); + Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW), BST_UNCHECKED); PhMipSetPinned(FALSE); PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE); @@ -452,7 +452,7 @@ VOID PhMipOnInitDialog( SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); pin = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PIN)); - SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); + SET_BUTTON_ICON(PhMipWindow, IDC_PINWINDOW, pin); PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_LAYOUT), NULL, @@ -461,12 +461,12 @@ VOID PhMipOnInitDialog( 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, + PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_PINWINDOW), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); SetWindowSubclass(GetDlgItem(PhMipWindow, IDC_SECTION), PhMipSectionControlHookWndProc, 0, 0); - Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), !!PhGetIntegerSetting(L"MiniInfoWindowPinned")); + Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW), !!PhGetIntegerSetting(L"MiniInfoWindowPinned")); } VOID PhMipOnShowWindow( @@ -519,11 +519,11 @@ VOID PhMipOnCommand( case IDC_OPTIONS: PhMipShowOptionsMenu(); break; - case IDC_PIN: + case IDC_PINWINDOW: { BOOLEAN pinned; - pinned = Button_GetCheck(GetDlgItem(PhMipWindow, IDC_PIN)) == BST_CHECKED; + pinned = Button_GetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW)) == BST_CHECKED; PhPinMiniInformation(MiniInfoManualPinType, pinned ? 1 : -1, 0, 0, NULL, NULL); PhMipSetPinned(pinned); PhSetIntegerSetting(L"MiniInfoWindowPinned", pinned); @@ -987,7 +987,7 @@ VOID PhMipLayout( ); } - GetWindowRect(GetDlgItem(PhMipWindow, IDC_PIN), &rect); + GetWindowRect(GetDlgItem(PhMipWindow, IDC_PINWINDOW), &rect); MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2); } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index cb5262a4eadc..ffec44e7675d 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -504,7 +504,7 @@ #define IDC_ZPAGINGPAGEREADSDELTA_V 1369 #define IDC_BYTESPERROW 1369 #define IDC_ZPAGINGPAGEFILEWRITESDELTA_V 1370 -#define IDC_PIN 1370 +#define IDC_PINWINDOW 1370 #define IDC_ZPAGINGMAPPEDWRITESDELTA_V 1371 #define IDC_ZLISTMODIFIEDPAGEFILE_V 1373 #define IDC_SECTION 1375 From a147a5c8afc9062e71994fa922303dd854e14331 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 12:35:53 +1100 Subject: [PATCH 450/839] Update ForceNoParent default setting --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 933d27733ad9..5eaf3964eeda 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -61,7 +61,7 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"FileBrowseExecutable", L"%SystemRoot%\\explorer.exe /select,\"%s\""); PhpAddIntegerSetting(L"FirstRun", L"1"); PhpAddStringSetting(L"Font", L""); // null - PhpAddIntegerSetting(L"ForceNoParent", L"0"); + PhpAddIntegerSetting(L"ForceNoParent", L"1"); PhpAddStringSetting(L"HandleTreeListColumns", L""); PhpAddStringSetting(L"HandleTreeListSort", L"0,1"); // 0, AscendingSortOrder PhpAddIntegerSetting(L"HiddenProcessesMenuEnabled", L"0"); From f7c60b4bfa15d252d2cd1b8d4cfe9d255d5c9b92 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 18:04:26 +1100 Subject: [PATCH 451/839] Fix AppId column for store apps --- ProcessHacker/proctree.c | 69 ++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 22a3d5ad63ec..6b1da367e78d 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -608,7 +608,7 @@ VOID PhTickProcessNodes( // 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 + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS | PHPN_APPID; // Items that always remain valid // Invalidate graph buffers. node->CpuGraphBuffers.Valid = FALSE; @@ -1091,16 +1091,67 @@ static VOID PhpUpdateProcessNodeAppId( PhClearReference(&ProcessNode->AppIdText); - if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(PhGetProcessWindowTitle( - ProcessNode->ProcessItem->QueryHandle, - &windowFlags, - &windowTitle - ))) + if (ProcessNode->ProcessItem->QueryHandle) { - if (windowFlags & STARTF_TITLEISAPPID) - ProcessNode->AppIdText = windowTitle; + 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 + ); + } + } + } + + PhFree(info); + } + + NtClose(tokenHandle); + } + } else - PhDereferenceObject(windowTitle); + { + if (NT_SUCCESS(PhGetProcessWindowTitle( + ProcessNode->ProcessItem->QueryHandle, + &windowFlags, + &windowTitle + ))) + { + if (windowFlags & STARTF_TITLEISAPPID) + ProcessNode->AppIdText = windowTitle; + else + PhDereferenceObject(windowTitle); + } + } } ProcessNode->ValidMask |= PHPN_APPID; From 820ba59146b157e50b59b803561d7f6c9b95895d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 18:34:08 +1100 Subject: [PATCH 452/839] Add missing break from previous commit --- ProcessHacker/proctree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 6b1da367e78d..3e6e6d445f9b 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1128,6 +1128,8 @@ static VOID PhpUpdateProcessNodeAppId( L"!", attributeValue1->Buffer ); + + break; } } } From 624d8c30a6bcf057a67e45197d641f148962f4b9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 18:47:46 +1100 Subject: [PATCH 453/839] Improve store package lookup performance (experimental) --- ProcessHacker/appsup.c | 163 +++++++++----------------------- ProcessHacker/include/appsup.h | 8 +- ProcessHacker/include/procprv.h | 4 +- ProcessHacker/modprv.c | 2 +- ProcessHacker/procprv.c | 18 ++-- ProcessHacker/prpggen.c | 2 +- 6 files changed, 58 insertions(+), 139 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index e72d2cf1e37c..d8a109c3d3d2 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -41,26 +41,6 @@ #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 } }; @@ -242,119 +222,70 @@ 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); - } + HANDLE tokenHandle; + PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + PPH_STRING name = NULL; - if (result == ERROR_SUCCESS) - { - PhTrimToNullTerminatorString(name); - return name; - } - else + if (NT_SUCCESS(PhOpenProcessToken( + ProcessHandle, + TOKEN_QUERY, + &tokenHandle + ))) { - 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); + // rev from PackageIdFromFullName + 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]; - result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer); + if (RtlEqualUnicodeString(&attribute->Name, &attributeNameUs, FALSE)) + { + if (attribute->ValueType == TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING) + { + name = PhCreateStringFromUnicodeString(&attribute->Values.pString[0]); + break; + } + } + } - if (result == ERROR_INSUFFICIENT_BUFFER) - { - PhFree(packageIdBuffer); - packageIdBuffer = PhAllocate(packageIdBufferSize); + PhFree(info); + } - result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer); + NtClose(tokenHandle); } - if (result == ERROR_SUCCESS) - { - return packageIdBuffer; - } - else - { - PhFree(packageIdBuffer); - return NULL; - } + return name; } PPH_STRING PhGetPackagePath( - _In_ PACKAGE_ID *PackageId + _In_ PPH_STRING PackageFullName ) { - static _GetPackagePath getPackagePath = NULL; + 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; - LONG result; - PPH_STRING path; - ULONG pathLength; + keyPath = PhConcatStringRef2(&storeAppPackages, &PackageFullName->sr); - 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) + // rev from GetPackagePath + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &keyPath->sr, + 0 + ))) { - PhDereferenceObject(path); - path = PhCreateStringEx(NULL, (pathLength - 1) * 2); - - result = getPackagePath(PackageId, 0, &pathLength, path->Buffer); + packagePath = PhQueryRegistryString(keyHandle, L"PackageRootFolder"); + NtClose(keyHandle); } - if (result == ERROR_SUCCESS) - { - PhTrimToNullTerminatorString(path); - return path; - } - else - { - PhDereferenceObject(path); - return NULL; - } + PhDereferenceObject(keyPath); + + return packagePath; } /** diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 16d1762462da..3ae868ceb48e 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -8,8 +8,6 @@ extern GUID WIN8_CONTEXT_GUID; extern GUID WINBLUE_CONTEXT_GUID; extern GUID WINTHRESHOLD_CONTEXT_GUID; -typedef struct PACKAGE_ID PACKAGE_ID; - // begin_phapppub PHAPPAPI BOOLEAN @@ -28,12 +26,8 @@ PPH_STRING PhGetProcessPackageFullName( _In_ HANDLE ProcessHandle ); -PACKAGE_ID *PhPackageIdFromFullName( - _In_ PWSTR PackageFullName - ); - PPH_STRING PhGetPackagePath( - _In_ PACKAGE_ID *PackageId + _In_ PPH_STRING PackageFullName ); // begin_phapppub diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index c3d6b7dd7130..3e433d740952 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -311,13 +311,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 ); diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 0a507b872a4f..7a39345db8aa 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -292,7 +292,7 @@ NTSTATUS PhpModuleQueryWorker( data->VerifyResult = PhVerifyFileCached( data->ModuleItem->FileName, - PhGetString(data->ModuleProvider->PackageFullName), + data->ModuleProvider->PackageFullName, &data->VerifySignerName, FALSE ); diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index e4d2bdb48893..296ba8fbdc6a 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -680,7 +680,7 @@ INT NTAPI PhpVerifyCacheCompareFunction( VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( _In_ PPH_VERIFY_FILE_INFO Information, - _In_opt_ PWSTR PackageFullName, + _In_opt_ PPH_STRING PackageFullName, _Out_opt_ PPH_STRING *SignerName ) { @@ -693,18 +693,12 @@ VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( if (PackageFullName) { - PACKAGE_ID *packageId; PPH_STRING packagePath; - if (packageId = PhPackageIdFromFullName(PackageFullName)) + if (packagePath = PhGetPackagePath(PackageFullName)) { - if (packagePath = PhGetPackagePath(packageId)) - { - additionalCatalogFileName = PhConcatStringRef2(&packagePath->sr, &codeIntegrityFileName); - PhDereferenceObject(packagePath); - } - - PhFree(packageId); + additionalCatalogFileName = PhConcatStringRef2(&packagePath->sr, &codeIntegrityFileName); + PhDereferenceObject(packagePath); } } @@ -751,7 +745,7 @@ VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( */ VERIFY_RESULT PhVerifyFileCached( _In_ PPH_STRING FileName, - _In_opt_ PWSTR PackageFullName, + _In_opt_ PPH_STRING PackageFullName, _Out_opt_ PPH_STRING *SignerName, _In_ BOOLEAN CachedOnly ) @@ -1184,7 +1178,7 @@ VOID PhpProcessQueryStage2( Data->VerifyResult = PhVerifyFileCached( processItem->FileName, - PhGetString(packageFullName), + packageFullName, &Data->VerifySignerName, FALSE ); diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 925398bfa7eb..aa061e46e478 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -613,7 +613,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( info.hWnd = hwndDlg; PhVerifyFileWithAdditionalCatalog( &info, - PhGetString(processItem->PackageFullName), + processItem->PackageFullName, NULL ); } From 962fbd62ba644ad01aeda304956830626d94f9d8 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 5 Oct 2017 18:49:16 +1100 Subject: [PATCH 454/839] Update sdk --- ProcessHacker/include/sysinfop.h | 6 ------ phnt/include/ntexapi.h | 12 +++++++++--- phnt/include/phnt.h | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ProcessHacker/include/sysinfop.h b/ProcessHacker/include/sysinfop.h index 261c23151325..ccd8b02fd1df 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -233,12 +233,6 @@ PPH_STRING PhSipFormatSizeWithPrecision( // 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, diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 70e2310acc32..5a13354ad539 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1194,7 +1194,6 @@ NtAllocateUuids( // rev // private -// source:http://www.microsoft.com/whdc/system/Sysinternals/MoreThan64proc.mspx typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, // q: SYSTEM_BASIC_INFORMATION @@ -2276,13 +2275,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 { diff --git a/phnt/include/phnt.h b/phnt/include/phnt.h index 42bf2a9d8007..924b09b6f2fc 100644 --- a/phnt/include/phnt.h +++ b/phnt/include/phnt.h @@ -35,6 +35,7 @@ #define PHNT_THRESHOLD2 101 #define PHNT_REDSTONE 102 #define PHNT_REDSTONE2 103 +#define PHNT_REDSTONE3 104 #ifndef PHNT_MODE #define PHNT_MODE PHNT_MODE_USER From cbd7281ad8f51a15fb785f12b681c2b173c6500a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 06:44:14 +1100 Subject: [PATCH 455/839] Fix format specifier --- ProcessHacker/thrdprv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index a1eb226728a6..42c41e2f78fd 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -743,7 +743,7 @@ PPH_STRING PhGetBasePriorityIncrementString( case THREAD_PRIORITY_ERROR_RETURN: return NULL; default: - return PhFormatString(L"%d", Increment); + return PhFormatString(L"%ld", Increment); } } From 7db125defb581af4bede5ca29cbee483a724b1cc Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 06:54:20 +1100 Subject: [PATCH 456/839] Move DbgHelp code into appsup, Improve phsvc linking --- ProcessHacker/appsup.c | 40 +++++++++++++++++++++++++++++++ ProcessHacker/include/appsup.h | 7 ++++++ ProcessHacker/include/mainwnd.h | 4 ---- ProcessHacker/mainwnd.c | 42 +-------------------------------- ProcessHacker/phsvc/svcapi.c | 3 +-- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index d8a109c3d3d2..e5f004d5265f 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -969,6 +969,46 @@ VOID PhShellExecuteUserString( PhDereferenceObject(executeString); } +VOID PhLoadSymbolProviderDbgHelpFromPath( + _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 PhLoadSymbolProviderOptions( _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider ) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 3ae868ceb48e..294d41140ab4 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -149,6 +149,13 @@ PhShellExecuteUserString( _In_opt_ PWSTR ErrorMessage ); +PHAPPAPI +VOID +NTAPI +PhLoadSymbolProviderDbgHelpFromPath( + _In_ PWSTR DbgHelpPath + ); + PHAPPAPI VOID NTAPI diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index de4d3b04f33a..f1d96ac8c202 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -207,10 +207,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/mainwnd.c b/ProcessHacker/mainwnd.c index 73b7be8fccd5..8d1e89befcd3 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2286,46 +2286,6 @@ VOID PhMwpSaveWindowState( 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 @@ -2334,7 +2294,7 @@ VOID PhMwpSymInitHandler( PPH_STRING dbghelpPath; dbghelpPath = PhGetStringSetting(L"DbgHelpPath"); - PhLoadDbgHelpFromPath(dbghelpPath->Buffer); + PhLoadSymbolProviderDbgHelpFromPath(dbghelpPath->Buffer); PhDereferenceObject(dbghelpPath); } diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index 89eee95d20cb..e9e614c84c91 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -31,7 +31,6 @@ #include #include -#include #include typedef struct _PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS @@ -1408,7 +1407,7 @@ NTSTATUS PhSvcApiLoadDbgHelp( if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.LoadDbgHelp.i.DbgHelpPath, FALSE, &dbgHelpPath))) { PH_AUTO(dbgHelpPath); - PhLoadDbgHelpFromPath(dbgHelpPath->Buffer); + PhLoadSymbolProviderDbgHelpFromPath(dbgHelpPath->Buffer); alreadyLoaded = TRUE; } From b16c4521fbb5361d04e59a7ab211b534d01ca3cf Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 06:54:59 +1100 Subject: [PATCH 457/839] Improve handle enumeration error fallback --- ProcessHacker/hndlprv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 4afd571c33c9..cd1dd54c34c9 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -306,7 +306,7 @@ NTSTATUS PhEnumHandlesGeneric( // * Otherwise, NtQuerySystemInformation with SystemHandleInformation // can be used. - if (KphIsConnected() && KphIsVerified()) + if (KphIsConnected()) { PKPH_PROCESS_HANDLE_INFORMATION handles; PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; @@ -316,7 +316,7 @@ NTSTATUS PhEnumHandlesGeneric( // this only enumerates handles for a single process and saves a lot of processing. if (!NT_SUCCESS(status = KphEnumerateProcessHandles2(ProcessHandle, &handles))) - return status; + goto FAILED; convertedHandles = PhAllocate( FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + @@ -344,7 +344,7 @@ NTSTATUS PhEnumHandlesGeneric( else { PSYSTEM_HANDLE_INFORMATION_EX handles; - +FAILED: if (!NT_SUCCESS(status = PhEnumHandlesEx(&handles))) return status; From ea6f64dd030012621cb8d726e53493703650eb80 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 06:58:21 +1100 Subject: [PATCH 458/839] Improve KPH error messsages --- ProcessHacker/main.c | 54 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index ca0de0fe219f..b0ed3931001f 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -616,6 +616,47 @@ NTSTATUS PhpReadSignature( } } +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 ) @@ -633,6 +674,13 @@ VOID PhInitializeKph( kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); + if (!RtlDoesFileExists_U(kprocesshackerFileName->Buffer)) + { + if (PhGetIntegerSetting(L"EnableKphWarnings")) + 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; @@ -658,7 +706,7 @@ VOID PhInitializeKph( if (!NT_SUCCESS(status)) { if (PhGetIntegerSetting(L"EnableKphWarnings")) - PhShowStatus(NULL, L"Unable to verify the kernel driver signature.", status, 0); + PhpShowKphError(L"Unable to verify the kernel driver signature.", status); } PhFree(signature); @@ -666,13 +714,13 @@ VOID PhInitializeKph( else { if (PhGetIntegerSetting(L"EnableKphWarnings")) - PhShowStatus(NULL, L"Unable to load the kernel driver signature.", status, 0); + PhpShowKphError(L"Unable to load the kernel driver signature.", status); } } else { if (PhGetIntegerSetting(L"EnableKphWarnings") && PhGetOwnTokenAttributes().Elevated) - PhShowStatus(NULL, L"Unable to load the kernel driver.", status, 0); + PhpShowKphError(L"Unable to load the kernel driver.", status); } PhDereferenceObject(kprocesshackerFileName); From 5d2e02b48ae31d229b35e8f672a79a67cf7dafd2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 07:23:24 +1100 Subject: [PATCH 459/839] Move DbgHelp code into appsup, Improve mainwnd linking --- ProcessHacker/appsup.c | 43 ++++++++++++++++++++++++++++++ ProcessHacker/include/appsup.h | 7 +++++ ProcessHacker/include/mainwndp.h | 4 --- ProcessHacker/mainwnd.c | 45 +------------------------------- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index e5f004d5265f..3fe2ad206c8e 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -969,6 +970,48 @@ VOID PhShellExecuteUserString( PhDereferenceObject(executeString); } +PPH_STRING PhFindDbghelpPath( + 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 PhLoadSymbolProviderDbgHelpFromPath( _In_ PWSTR DbgHelpPath ) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 294d41140ab4..d82339461152 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -149,6 +149,13 @@ PhShellExecuteUserString( _In_opt_ PWSTR ErrorMessage ); +PHAPPAPI +PPH_STRING +NTAPI +PhFindDbghelpPath( + VOID + ); + PHAPPAPI VOID NTAPI diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 70f85d4f1569..29fea32a3fc7 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -53,10 +53,6 @@ NTSTATUS PhMwpDelayedLoadFunction( _In_ PVOID Parameter ); -PPH_STRING PhMwpFindDbghelpPath( - VOID - ); - // Event handlers VOID PhMwpOnDestroy( diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 8d1e89befcd3..fa80643d7f64 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -107,7 +106,7 @@ BOOLEAN PhMainWndInitialization( PPH_STRING autoDbghelpPath; // Try to set up the dbghelp path automatically if this is the first run. - if (autoDbghelpPath = PH_AUTO(PhMwpFindDbghelpPath())) + if (autoDbghelpPath = PH_AUTO(PhFindDbghelpPath())) PhSetStringSetting2(L"DbgHelpPath", &autoDbghelpPath->sr); PhSetIntegerSetting(L"FirstRun", FALSE); @@ -494,48 +493,6 @@ NTSTATUS PhMwpDelayedLoadFunction( 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 ) From 6110e0b758ec44cac461fda8e65aa12aa75801a4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 07:23:56 +1100 Subject: [PATCH 460/839] Enable KPH for Win10 insider previews --- ProcessHacker/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index b0ed3931001f..2f42307995e5 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -668,9 +668,6 @@ VOID PhInitializeKph( PPH_STRING processhackerSigFileName; KPH_PARAMETERS parameters; - if (WindowsVersion == WINDOWS_NEW) - return; - kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); From 7f806cb2737936d50e17434d2c55f8824f26057c Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 08:49:52 +1100 Subject: [PATCH 461/839] partial revert 'Fix AppId column for store apps' --- ProcessHacker/proctree.c | 119 +++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 3e6e6d445f9b..22bf759f5d1c 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -608,7 +608,7 @@ VOID PhTickProcessNodes( // 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; // Items that always remain valid + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS; // Items that always remain valid // Invalidate graph buffers. node->CpuGraphBuffers.Valid = FALSE; @@ -1093,66 +1093,65 @@ static VOID PhpUpdateProcessNodeAppId( if (ProcessNode->ProcessItem->QueryHandle) { - 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); - } - } - else + //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); + // } + //} + //else + + if (NT_SUCCESS(PhGetProcessWindowTitle( + ProcessNode->ProcessItem->QueryHandle, + &windowFlags, + &windowTitle + ))) { - if (NT_SUCCESS(PhGetProcessWindowTitle( - ProcessNode->ProcessItem->QueryHandle, - &windowFlags, - &windowTitle - ))) - { - if (windowFlags & STARTF_TITLEISAPPID) - ProcessNode->AppIdText = windowTitle; - else - PhDereferenceObject(windowTitle); - } + if (windowFlags & STARTF_TITLEISAPPID) + ProcessNode->AppIdText = windowTitle; + else + PhDereferenceObject(windowTitle); } } From ea538b4241f67955ca35ea88031c24399931f0ce Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 09:16:08 +1100 Subject: [PATCH 462/839] Fix sdk conflicts with DDK --- phnt/include/ntexapi.h | 12 ++---------- phnt/include/ntmmapi.h | 26 +++++++++++--------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 5a13354ad539..682d10db59f7 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -2151,6 +2151,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 { @@ -2168,6 +2169,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 @@ -2223,16 +2225,6 @@ 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 { diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 9f282024dfe5..9da77a7a4e87 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 { @@ -70,19 +78,6 @@ typedef enum _MEMORY_INFORMATION_CLASS MemoryEnclaveImageInformation, // 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; #endif typedef struct _MEMORY_WORKING_SET_BLOCK @@ -474,6 +469,7 @@ NtQueryVirtualMemory( // begin_private +#if (PHNT_MODE != PHNT_MODE_KERNEL) typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS { VmPrefetchInformation, @@ -486,7 +482,7 @@ typedef struct _MEMORY_RANGE_ENTRY PVOID VirtualAddress; SIZE_T NumberOfBytes; } MEMORY_RANGE_ENTRY, *PMEMORY_RANGE_ENTRY; - +#endif // end_private #if (PHNT_MODE != PHNT_MODE_KERNEL) From 8d475729fcf9f7f6f664eb643ec03f0cf00a2aec Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 12:46:50 +1100 Subject: [PATCH 463/839] Fix build error, Fix DDK conflicts --- phnt/include/ntmmapi.h | 12 ++++++++++++ phnt/include/ntzwapi.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 9da77a7a4e87..8446e6f5568d 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -78,6 +78,18 @@ typedef enum _MEMORY_INFORMATION_CLASS MemoryEnclaveImageInformation, // since REDSTONE3 MemoryBasicInformationCapped } MEMORY_INFORMATION_CLASS; +#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 diff --git a/phnt/include/ntzwapi.h b/phnt/include/ntzwapi.h index d8c445aba8ae..92def73473ca 100644 --- a/phnt/include/ntzwapi.h +++ b/phnt/include/ntzwapi.h @@ -1537,6 +1537,16 @@ ZwExtendSection( _Inout_ PLARGE_INTEGER NewSectionSize ); +#ifndef FILTER_BOOT_OPTION_OPERATION +typedef enum _FILTER_BOOT_OPTION_OPERATION +{ + FilterBootOptionOperationOpenSystemStore, + FilterBootOptionOperationSetElement, + FilterBootOptionOperationDeleteElement, + FilterBootOptionOperationMax +} FILTER_BOOT_OPTION_OPERATION; +#endif + NTSYSCALLAPI NTSTATUS NTAPI From 00d2e6b8f2c053e8ae8a2aa6b95fa392d3406124 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 12:51:25 +1100 Subject: [PATCH 464/839] Remove duplicate SDK definition --- phnt/include/ntzwapi.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/phnt/include/ntzwapi.h b/phnt/include/ntzwapi.h index 92def73473ca..48eb7f841507 100644 --- a/phnt/include/ntzwapi.h +++ b/phnt/include/ntzwapi.h @@ -1537,27 +1537,6 @@ ZwExtendSection( _Inout_ PLARGE_INTEGER NewSectionSize ); -#ifndef FILTER_BOOT_OPTION_OPERATION -typedef enum _FILTER_BOOT_OPTION_OPERATION -{ - FilterBootOptionOperationOpenSystemStore, - FilterBootOptionOperationSetElement, - FilterBootOptionOperationDeleteElement, - FilterBootOptionOperationMax -} FILTER_BOOT_OPTION_OPERATION; -#endif - -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 From 5961b14364b926a647a0e8bd82509caacaa5a3ac Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 13:59:34 +1100 Subject: [PATCH 465/839] ExtendedServices: Remove unused options tab --- .../ExtendedServices/ExtendedServices.vcxproj | 1 - .../ExtendedServices.vcxproj.filters | 3 -- plugins/ExtendedServices/extsrv.h | 9 ---- plugins/ExtendedServices/main.c | 46 ++---------------- plugins/ExtendedServices/options.c | 47 ------------------- 5 files changed, 4 insertions(+), 102 deletions(-) delete mode 100644 plugins/ExtendedServices/options.c diff --git a/plugins/ExtendedServices/ExtendedServices.vcxproj b/plugins/ExtendedServices/ExtendedServices.vcxproj index 55fd13d45770..f257f184c66c 100644 --- a/plugins/ExtendedServices/ExtendedServices.vcxproj +++ b/plugins/ExtendedServices/ExtendedServices.vcxproj @@ -54,7 +54,6 @@ - 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/extsrv.h b/plugins/ExtendedServices/extsrv.h index 6459073e14c5..45ee3d50f048 100644 --- a/plugins/ExtendedServices/extsrv.h +++ b/plugins/ExtendedServices/extsrv.h @@ -38,15 +38,6 @@ INT_PTR CALLBACK EspServiceDependentsDlgProc( _In_ LPARAM lParam ); -// options - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - // other typedef NTSTATUS (NTAPI *_RtlCreateServiceSid)( diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index 401b27a1629a..a84cdc669ae4 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -23,36 +23,10 @@ #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 - ) -{ - PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; - - optionsEntry->CreateSection( - L"ExtendedServices", - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - OptionsDlgProc, - NULL - ); -} +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration; VOID NTAPI MenuItemCallback( _In_opt_ PVOID Parameter, @@ -441,18 +415,6 @@ LOGICAL DllMain( info->Description = L"Extends service management capabilities."; info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1113"; - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); PhRegisterCallback( PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), MenuItemCallback, diff --git a/plugins/ExtendedServices/options.c b/plugins/ExtendedServices/options.c deleted file mode 100644 index c9bf6e7027cd..000000000000 --- a/plugins/ExtendedServices/options.c +++ /dev/null @@ -1,47 +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: - { - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLESERVICESMENU), PhGetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU) ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_DESTROY: - { - PhSetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLESERVICESMENU)) == BST_CHECKED); - } - break; - } - - return FALSE; -} From defaac11ba612711209abcd5bc4ba14e3abb9745 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 14:07:16 +1100 Subject: [PATCH 466/839] Update gitignore paths for KPH --- .gitignore | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 53afad1c8da7..346a563a6704 100644 --- a/.gitignore +++ b/.gitignore @@ -78,11 +78,12 @@ build/output/ plugins-extra/ 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/ From 10bc7c724f43e823634c0d8745284daa79ef17b0 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 14:12:50 +1100 Subject: [PATCH 467/839] Update KPH for VS17 --- KProcessHacker/KProcessHacker.sln | 35 ++ KProcessHacker/KProcessHacker.vcxproj | 112 ++++-- KProcessHacker/KProcessHacker.vcxproj.filters | 6 + KProcessHacker/bin/amd64/kprocesshacker.pdb | Bin 314368 -> 0 bytes KProcessHacker/bin/amd64/kprocesshacker.sys | Bin 27648 -> 0 bytes KProcessHacker/bin/i386/kprocesshacker.pdb | Bin 363520 -> 0 bytes KProcessHacker/bin/i386/kprocesshacker.sys | Bin 24064 -> 0 bytes KProcessHacker/clean/makefile | 1 - KProcessHacker/clean/sources | 7 - KProcessHacker/devctrl.c | 4 +- KProcessHacker/dirs | 1 - KProcessHacker/dyndata.c | 14 +- KProcessHacker/dynimp.c | 2 +- KProcessHacker/include/dyndata.h | 2 +- KProcessHacker/include/kph.h | 338 +++++++++--------- KProcessHacker/include/ntfill.h | 220 ++++++------ KProcessHacker/main.c | 40 ++- KProcessHacker/object.c | 100 +++--- KProcessHacker/process.c | 168 ++++----- KProcessHacker/qrydrv.c | 28 +- KProcessHacker/sign.cmd | 7 - KProcessHacker/sources.inc | 28 -- KProcessHacker/thread.c | 102 +++--- KProcessHacker/util.c | 18 +- KProcessHacker/verify.c | 48 +-- KProcessHacker/vm.c | 42 +-- 26 files changed, 670 insertions(+), 653 deletions(-) create mode 100644 KProcessHacker/KProcessHacker.sln delete mode 100644 KProcessHacker/bin/amd64/kprocesshacker.pdb delete mode 100644 KProcessHacker/bin/amd64/kprocesshacker.sys delete mode 100644 KProcessHacker/bin/i386/kprocesshacker.pdb delete mode 100644 KProcessHacker/bin/i386/kprocesshacker.sys delete mode 100644 KProcessHacker/clean/makefile delete mode 100644 KProcessHacker/clean/sources delete mode 100644 KProcessHacker/dirs delete mode 100644 KProcessHacker/sign.cmd delete mode 100644 KProcessHacker/sources.inc 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 87fb632baad2..3f18628eba62 100644 --- a/KProcessHacker/KProcessHacker.vcxproj +++ b/KProcessHacker/KProcessHacker.vcxproj @@ -1,27 +1,26 @@  - + - - Win8 Debug + + Debug Win32 - - Win8 Release + + Release Win32 - - Win8 Debug + + Debug x64 - - Win8 Release + + Release x64 {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF} {dd38f7fc-d7bd-488b-9242-7d8754cde80d} - v4.5 11.0 Win8 Debug Win32 @@ -29,28 +28,33 @@ KProcessHacker $(VCTargetsPath11) + $(LatestTargetPlatformVersion) - WindowsKernelModeDriver8.0 + WindowsKernelModeDriver10.0 Driver WDM - - Windows8 + + Windows7 true + Unicode - - Windows8 + + Windows7 false + Unicode - - Windows8 + + Windows7 true + Unicode - - Windows8 + + Windows7 false + Unicode @@ -62,53 +66,81 @@ DbgengKernelDebugger - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false kprocesshacker - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false kprocesshacker - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false kprocesshacker - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false kprocesshacker - + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) + 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;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) + 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;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) Level3 + true + true + + ksecdd.lib;%(AdditionalDependencies) + true + - + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) Level3 + true + true + + ksecdd.lib;%(AdditionalDependencies) + true + @@ -123,6 +155,8 @@ + + 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 93dbdc215621ddfdc319505af53842701ffccc47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314368 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 S$$_05*vWyN9QgO;!2bsxG=SLv diff --git a/KProcessHacker/bin/amd64/kprocesshacker.sys b/KProcessHacker/bin/amd64/kprocesshacker.sys deleted file mode 100644 index f6a94ff33aa26fc54554baa610c71102de06c990..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27648 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 aLoSVL6Z)0B!-8+KrF~A~^sgT{@IL{P_`l@< diff --git a/KProcessHacker/bin/i386/kprocesshacker.pdb b/KProcessHacker/bin/i386/kprocesshacker.pdb deleted file mode 100644 index b82fe335059d3b99358418a3b44030479fdfca75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363520 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%0A~cJj_!BflYX{Nmk(PyOVy(@x&_;X^;cpJ5~a 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 diff --git a/KProcessHacker/bin/i386/kprocesshacker.sys b/KProcessHacker/bin/i386/kprocesshacker.sys deleted file mode 100644 index 4f8f329cc11feb776ff36a3a36a6030a79924640..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24064 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 T#& NTSTATUS KphDispatchDeviceControl( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp ) { NTSTATUS 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 40d4cfe5d470..ff685369570b 100644 --- a/KProcessHacker/dyndata.c +++ b/KProcessHacker/dyndata.c @@ -26,8 +26,8 @@ #define C_2sTo4(x) ((unsigned int)(signed short)(x)) NTSTATUS KphpLoadDynamicConfiguration( - __in PVOID Buffer, - __in ULONG Length + _In_ PVOID Buffer, + _In_ ULONG Length ); #ifdef ALLOC_PRAGMA @@ -53,7 +53,7 @@ NTSTATUS KphDynamicDataInitialization( } NTSTATUS KphReadDynamicDataParameters( - __in_opt HANDLE KeyHandle + _In_opt_ HANDLE KeyHandle ) { NTSTATUS status; @@ -114,8 +114,8 @@ NTSTATUS KphReadDynamicDataParameters( } NTSTATUS KphpLoadDynamicConfiguration( - __in PVOID Buffer, - __in ULONG Length + _In_ PVOID Buffer, + _In_ ULONG Length ) { PKPH_DYN_CONFIGURATION config; @@ -126,13 +126,13 @@ NTSTATUS KphpLoadDynamicConfiguration( config = Buffer; - if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages)) + if (Length < (ULONG)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)) + if (Length < (ULONG)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); diff --git a/KProcessHacker/dynimp.c b/KProcessHacker/dynimp.c index d432611a3ca8..7b02b5b1841f 100644 --- a/KProcessHacker/dynimp.c +++ b/KProcessHacker/dynimp.c @@ -47,7 +47,7 @@ VOID KphDynamicImport( * not be found. */ PVOID KphGetSystemRoutineAddress( - __in PWSTR SystemRoutineName + _In_ PWSTR SystemRoutineName ) { UNICODE_STRING systemRoutineName; diff --git a/KProcessHacker/include/dyndata.h b/KProcessHacker/include/dyndata.h index d31f0f0e10e7..ab36182a3fd9 100644 --- a/KProcessHacker/include/dyndata.h +++ b/KProcessHacker/include/dyndata.h @@ -41,7 +41,7 @@ NTSTATUS KphDynamicDataInitialization( ); NTSTATUS KphReadDynamicDataParameters( - __in_opt HANDLE KeyHandle + _In_opt_ HANDLE KeyHandle ); #endif diff --git a/KProcessHacker/include/kph.h b/KProcessHacker/include/kph.h index 8ee115f9b7ab..4c5032411cf1 100644 --- a/KProcessHacker/include/kph.h +++ b/KProcessHacker/include/kph.h @@ -48,17 +48,17 @@ extern ULONG KphFeatures; extern KPH_PARAMETERS KphParameters; NTSTATUS KpiGetFeatures( - __out PULONG Features, - __in KPROCESSOR_MODE AccessMode + _Out_ PULONG Features, + _In_ KPROCESSOR_MODE AccessMode ); // devctrl -__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH KphDispatchDeviceControl; +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH KphDispatchDeviceControl; NTSTATUS KphDispatchDeviceControl( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp ); // dynimp @@ -68,298 +68,298 @@ VOID KphDynamicImport( ); PVOID KphGetSystemRoutineAddress( - __in PWSTR SystemRoutineName + _In_ PWSTR SystemRoutineName ); // object PHANDLE_TABLE KphReferenceProcessHandleTable( - __in PEPROCESS Process + _In_ PEPROCESS Process ); VOID KphDereferenceProcessHandleTable( - __in PEPROCESS Process + _In_ PEPROCESS Process ); VOID KphUnlockHandleTableEntry( - __in PHANDLE_TABLE HandleTable, - __in PHANDLE_TABLE_ENTRY HandleTableEntry + _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 + _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_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength + _In_ PVOID Object, + _Out_writes_bytes_(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 + _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_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode + _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_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __in KPROCESSOR_MODE AccessMode + _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 + _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 + _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 + _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 + _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 + _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 + _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_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __in KPROCESSOR_MODE AccessMode + _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 + _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 + _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 + _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 + _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 + _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_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode + _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_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode + _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_bcount(ProcessInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode + _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_bcount(ThreadInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __in KPROCESSOR_MODE AccessMode + _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 + _In_ PUNICODE_STRING CapturedUnicodeString ); NTSTATUS KphCaptureUnicodeString( - __in PUNICODE_STRING UnicodeString, - __out PUNICODE_STRING CapturedUnicodeString + _In_ PUNICODE_STRING UnicodeString, + _Out_ PUNICODE_STRING CapturedUnicodeString ); NTSTATUS KphEnumerateSystemModules( - __out PRTL_PROCESS_MODULES *Modules + _Out_ PRTL_PROCESS_MODULES *Modules ); NTSTATUS KphValidateAddressForSystemModules( - __in PVOID Address, - __in SIZE_T Length + _In_ PVOID Address, + _In_ SIZE_T Length ); NTSTATUS KphGetProcessMappedFileName( - __in HANDLE ProcessHandle, - __in PVOID BaseAddress, - __out PUNICODE_STRING *FileName + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_ PUNICODE_STRING *FileName ); // verify NTSTATUS KphHashFile( - __in PUNICODE_STRING FileName, - __out PVOID *Hash, - __out PULONG HashSize + _In_ PUNICODE_STRING FileName, + _Out_ PVOID *Hash, + _Out_ PULONG HashSize ); 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 ); 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 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 ); VOID KphGenerateKeysClient( - __inout PKPH_CLIENT Client + _Inout_ PKPH_CLIENT Client ); 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 ); 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 ); // 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 + _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 + _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 865bbe877cb1..0cf0bcdc300c 100644 --- a/KProcessHacker/include/ntfill.h +++ b/KProcessHacker/include/ntfill.h @@ -13,8 +13,8 @@ NTKERNELAPI VOID FASTCALL ExfUnblockPushLock( - __inout PEX_PUSH_LOCK PushLock, - __inout_opt PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock + _Inout_ PEX_PUSH_LOCK PushLock, + _Inout_opt_ PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock ); typedef struct _HANDLE_TABLE_ENTRY @@ -35,37 +35,37 @@ typedef struct _HANDLE_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 + _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 + _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 + _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 + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength ); // IO @@ -83,61 +83,47 @@ typedef enum _KAPC_ENVIRONMENT } KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT; typedef VOID (NTAPI *PKNORMAL_ROUTINE)( - __in PVOID NormalContext, - __in PVOID SystemArgument1, - __in PVOID SystemArgument2 + _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 + _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 + _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 + _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 + _Inout_ PRKAPC Apc, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2, + _In_ KPRIORITY Increment ); // OB @@ -195,7 +181,7 @@ FORCEINLINE ULONG ObpGetHandleAttributes(PHANDLE_TABLE_ENTRY HandleTableEntry) 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. +// This structure is not correct on Windows 7, but the offsets we need are still correct. typedef struct _OBJECT_HEADER { LONG PointerCount; @@ -204,11 +190,34 @@ typedef struct _OBJECT_HEADER LONG HandleCount; PVOID NextToFree; }; - POBJECT_TYPE Type; - UCHAR NameInfoOffset; - UCHAR HandleInfoOffset; - UCHAR QuotaInfoOffset; - UCHAR Flags; + 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; @@ -218,42 +227,43 @@ typedef struct _OBJECT_HEADER 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 + _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 + _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 - ); - -NTKERNELAPI -NTSTATUS -ObCloseHandle( - __in HANDLE Handle, - __in KPROCESSOR_MODE PreviousMode + _In_ HANDLE Handle, + _In_ POBJECT_HANDLE_FLAG_INFORMATION HandleFlags, + _In_ KPROCESSOR_MODE PreviousMode ); // PS @@ -262,48 +272,31 @@ 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 + _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_bcount(ThreadInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __out_opt PULONG ReturnLength + _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 - ); - -NTKERNELAPI -PVOID -NTAPI -PsGetThreadWin32Thread( - __in PETHREAD Thread + _In_ PCLIENT_ID ClientId, + _Out_opt_ PEPROCESS *Process, + _Out_ PETHREAD *Thread ); typedef struct _EJOB *PEJOB; @@ -314,21 +307,21 @@ NTKERNELAPI PEJOB NTAPI PsGetProcessJob( - __in PEPROCESS Process + _In_ PEPROCESS Process ); NTKERNELAPI NTSTATUS NTAPI PsAcquireProcessExitSynchronization( - __in PEPROCESS Process + _In_ PEPROCESS Process ); NTKERNELAPI VOID NTAPI PsReleaseProcessExitSynchronization( - __in PEPROCESS Process + _In_ PEPROCESS Process ); // RTL @@ -339,13 +332,4 @@ PsReleaseProcessExitSynchronization( #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 diff --git a/KProcessHacker/main.c b/KProcessHacker/main.c index 31623234b278..c2696cdee21f 100644 --- a/KProcessHacker/main.c +++ b/KProcessHacker/main.c @@ -24,17 +24,17 @@ DRIVER_INITIALIZE DriverEntry; DRIVER_UNLOAD DriverUnload; -__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH KphDispatchCreate; -__drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH KphDispatchClose; +_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 + _In_opt_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ ULONG DefaultValue ); NTSTATUS KphpReadDriverParameters( - __in PUNICODE_STRING RegistryPath + _In_ PUNICODE_STRING RegistryPath ); #ifdef ALLOC_PRAGMA @@ -51,8 +51,8 @@ ULONG KphFeatures; KPH_PARAMETERS KphParameters; NTSTATUS DriverEntry( - __in PDRIVER_OBJECT DriverObject, - __in PUNICODE_STRING RegistryPath + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; @@ -61,6 +61,8 @@ NTSTATUS DriverEntry( PAGED_CODE(); + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + KphDriverObject = DriverObject; if (!NT_SUCCESS(status = KphDynamicDataInitialization())) @@ -105,7 +107,7 @@ NTSTATUS DriverEntry( } VOID DriverUnload( - __in PDRIVER_OBJECT DriverObject + _In_ PDRIVER_OBJECT DriverObject ) { PAGED_CODE(); @@ -116,8 +118,8 @@ VOID DriverUnload( } NTSTATUS KphDispatchCreate( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; @@ -186,8 +188,8 @@ NTSTATUS KphDispatchCreate( } NTSTATUS KphDispatchClose( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; @@ -223,9 +225,9 @@ NTSTATUS KphDispatchClose( * \return The parameter value, or \a DefaultValue if the function failed. */ ULONG KphpReadIntegerParameter( - __in_opt HANDLE KeyHandle, - __in PUNICODE_STRING ValueName, - __in ULONG DefaultValue + _In_opt_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ ULONG DefaultValue ) { NTSTATUS status; @@ -267,7 +269,7 @@ ULONG KphpReadIntegerParameter( * \param RegistryPath The registry path of the driver. */ NTSTATUS KphpReadDriverParameters( - __in PUNICODE_STRING RegistryPath + _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; @@ -329,8 +331,8 @@ NTSTATUS KphpReadDriverParameters( } NTSTATUS KpiGetFeatures( - __out PULONG Features, - __in KPROCESSOR_MODE AccessMode + _Out_ PULONG Features, + _In_ KPROCESSOR_MODE AccessMode ) { PAGED_CODE(); diff --git a/KProcessHacker/object.c b/KProcessHacker/object.c index f272a16f9826..134a4d36e6db 100644 --- a/KProcessHacker/object.c +++ b/KProcessHacker/object.c @@ -41,16 +41,16 @@ typedef struct _KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT } KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT, *PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT; BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context + _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 + _In_ PHANDLE_TABLE HandleTable, + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context ); #ifdef ALLOC_PRAGMA @@ -77,7 +77,7 @@ BOOLEAN KphpEnumerateProcessHandlesEnumCallback( * longer needed. */ PHANDLE_TABLE KphReferenceProcessHandleTable( - __in PEPROCESS Process + _In_ PEPROCESS Process ) { PHANDLE_TABLE handleTable = NULL; @@ -106,7 +106,7 @@ PHANDLE_TABLE KphReferenceProcessHandleTable( * \param Process A process object. */ VOID KphDereferenceProcessHandleTable( - __in PEPROCESS Process + _In_ PEPROCESS Process ) { PAGED_CODE(); @@ -115,8 +115,8 @@ VOID KphDereferenceProcessHandleTable( } VOID KphUnlockHandleTableEntry( - __in PHANDLE_TABLE HandleTable, - __in PHANDLE_TABLE_ENTRY HandleTableEntry + _In_ PHANDLE_TABLE HandleTable, + _In_ PHANDLE_TABLE_ENTRY HandleTableEntry ) { PEX_PUSH_LOCK handleContentionEvent; @@ -140,9 +140,9 @@ VOID KphUnlockHandleTableEntry( } BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context ) { PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context = Context; @@ -205,10 +205,10 @@ BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( } BOOLEAN KphpEnumerateProcessHandlesEnumCallback( - __in PHANDLE_TABLE HandleTable, - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context + _In_ PHANDLE_TABLE HandleTable, + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context ) { BOOLEAN result; @@ -232,11 +232,11 @@ BOOLEAN KphpEnumerateProcessHandlesEnumCallback( * \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 + _In_ HANDLE ProcessHandle, + _Out_writes_bytes_(BufferLength) PVOID Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; @@ -321,7 +321,7 @@ NTSTATUS KpiEnumerateProcessHandles( ObDereferenceObject(process); // Write the number of handles if we can. - if (BufferLength >= FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) + if (BufferLength >= (ULONG)FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) { if (AccessMode != KernelMode) { @@ -378,10 +378,10 @@ NTSTATUS KpiEnumerateProcessHandles( * \a Buffer. */ NTSTATUS KphQueryNameObject( - __in PVOID Object, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength + _In_ PVOID Object, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength ) { NTSTATUS status; @@ -429,10 +429,10 @@ NTSTATUS KphQueryNameObject( * \a Buffer. */ NTSTATUS KphQueryNameFileObject( - __in PFILE_OBJECT FileObject, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength ) { NTSTATUS status = STATUS_SUCCESS; @@ -568,13 +568,13 @@ NTSTATUS KphQueryNameFileObject( * \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 + _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; @@ -1145,12 +1145,12 @@ NTSTATUS KpiQueryInformationObject( * \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 + _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; @@ -1249,11 +1249,11 @@ NTSTATUS KpiSetInformationObject( } NTSTATUS KphOpenNamedObject( - __out PHANDLE ObjectHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in POBJECT_TYPE ObjectType, - __in KPROCESSOR_MODE AccessMode + _Out_ PHANDLE ObjectHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ POBJECT_TYPE ObjectType, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; diff --git a/KProcessHacker/process.c b/KProcessHacker/process.c index c0aa6d1b63d2..17532e91dd5c 100644 --- a/KProcessHacker/process.c +++ b/KProcessHacker/process.c @@ -48,12 +48,12 @@ * \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 + _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; @@ -119,29 +119,29 @@ NTSTATUS KpiOpenProcess( KernelMode, &processHandle ); - } - - ObDereferenceObject(process); - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) + if (NT_SUCCESS(status)) { - __try + if (AccessMode != KernelMode) { - *ProcessHandle = processHandle; + __try + { + *ProcessHandle = processHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } } - __except (EXCEPTION_EXECUTE_HANDLER) + else { - status = GetExceptionCode(); + *ProcessHandle = processHandle; } } - else - { - *ProcessHandle = processHandle; - } } + ObDereferenceObject(process); + return status; } @@ -160,12 +160,12 @@ NTSTATUS KpiOpenProcess( * \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 + _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; @@ -218,6 +218,25 @@ NTSTATUS KpiOpenProcessToken( KernelMode, &tokenHandle ); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *TokenHandle = tokenHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *TokenHandle = tokenHandle; + } + } } PsDereferencePrimaryToken(primaryToken); @@ -229,25 +248,6 @@ NTSTATUS KpiOpenProcessToken( ObDereferenceObject(process); - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *TokenHandle = tokenHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *TokenHandle = tokenHandle; - } - } - return status; } @@ -260,16 +260,16 @@ NTSTATUS KpiOpenProcessToken( * \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 + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE JobHandle, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; PEPROCESS process; PEJOB job; - HANDLE jobHandle = NULL; + HANDLE jobHandle; PAGED_CODE(); @@ -310,32 +310,32 @@ NTSTATUS KpiOpenProcessJob( AccessMode, &jobHandle ); - } - else - { - status = STATUS_NOT_FOUND; - } - ObDereferenceObject(process); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) + if (NT_SUCCESS(status)) { - __try + if (AccessMode != KernelMode) { - *JobHandle = jobHandle; + __try + { + *JobHandle = jobHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } } - __except (EXCEPTION_EXECUTE_HANDLER) + else { - status = GetExceptionCode(); + *JobHandle = jobHandle; } } - else - { - *JobHandle = jobHandle; - } } + else + { + status = STATUS_NOT_FOUND; + } + + ObDereferenceObject(process); return status; } @@ -352,11 +352,11 @@ NTSTATUS KpiOpenProcessJob( * \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 + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; @@ -420,12 +420,12 @@ NTSTATUS KpiTerminateProcess( * \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 + _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; @@ -512,11 +512,11 @@ NTSTATUS KpiQueryInformationProcess( * \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 + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; diff --git a/KProcessHacker/qrydrv.c b/KProcessHacker/qrydrv.c index d2cb31b8ba18..641779fa0a52 100644 --- a/KProcessHacker/qrydrv.c +++ b/KProcessHacker/qrydrv.c @@ -22,8 +22,8 @@ #include VOID KphpCopyInfoUnicodeString( - __out PVOID Information, - __in_opt PUNICODE_STRING UnicodeString + _Out_ PVOID Information, + _In_opt_ PUNICODE_STRING UnicodeString ); #ifdef ALLOC_PRAGMA @@ -33,10 +33,10 @@ VOID KphpCopyInfoUnicodeString( #endif NTSTATUS KpiOpenDriver( - __out PHANDLE DriverHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in KPROCESSOR_MODE AccessMode + _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ KPROCESSOR_MODE AccessMode ) { PAGED_CODE(); @@ -51,12 +51,12 @@ NTSTATUS KpiOpenDriver( } 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 + _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; @@ -211,8 +211,8 @@ NTSTATUS KpiQueryInformationDriver( } VOID KphpCopyInfoUnicodeString( - __out PVOID Information, - __in_opt PUNICODE_STRING UnicodeString + _Out_ PVOID Information, + _In_opt_ PUNICODE_STRING UnicodeString ) { PUNICODE_STRING targetUnicodeString = Information; diff --git a/KProcessHacker/sign.cmd b/KProcessHacker/sign.cmd deleted file mode 100644 index 90e2927feed0..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 da8154c0c5d0..15ccd26705b8 100644 --- a/KProcessHacker/thread.c +++ b/KProcessHacker/thread.c @@ -37,11 +37,11 @@ typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT KKERNEL_ROUTINE KphpCaptureStackBackTraceThreadSpecialApc; VOID KphpCaptureStackBackTraceThreadSpecialApc( - __in PRKAPC Apc, - __inout PKNORMAL_ROUTINE *NormalRoutine, - __inout PVOID *NormalContext, - __inout PVOID *SystemArgument1, - __inout PVOID *SystemArgument2 + _In_ PRKAPC Apc, + _Inout_opt_ PKNORMAL_ROUTINE *NormalRoutine, + _Inout_opt_ PVOID *NormalContext, + _Inout_ PVOID *SystemArgument1, + _Inout_ PVOID *SystemArgument2 ); #ifdef ALLOC_PRAGMA @@ -70,19 +70,19 @@ VOID KphpCaptureStackBackTraceThreadSpecialApc( * \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 + _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; + HANDLE threadHandle = NULL; PAGED_CODE(); @@ -170,10 +170,10 @@ NTSTATUS KpiOpenThread( * \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 + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS status; @@ -257,11 +257,11 @@ NTSTATUS KpiOpenThreadProcess( * \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 + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _In_opt_ ULONG Flags, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG BackTraceHash ) { PVOID backTrace[MAX_STACK_DEPTH]; @@ -320,13 +320,13 @@ ULONG KphCaptureStackBackTrace( * \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 + _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; @@ -490,11 +490,11 @@ NTSTATUS KphCaptureStackBackTraceThread( } VOID KphpCaptureStackBackTraceThreadSpecialApc( - __in PRKAPC Apc, - __inout PKNORMAL_ROUTINE *NormalRoutine, - __inout PVOID *NormalContext, - __inout PVOID *SystemArgument1, - __inout PVOID *SystemArgument2 + _In_ PRKAPC Apc, + _Inout_opt_ PKNORMAL_ROUTINE *NormalRoutine, + _Inout_opt_ PVOID *NormalContext, + _Inout_ PVOID *SystemArgument1, + _Inout_ PVOID *SystemArgument2 ) { PCAPTURE_BACKTRACE_THREAD_CONTEXT context = *SystemArgument1; @@ -530,13 +530,13 @@ VOID KphpCaptureStackBackTraceThreadSpecialApc( * \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 + _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; @@ -582,12 +582,12 @@ NTSTATUS KpiCaptureStackBackTraceThread( * \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 + _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; @@ -665,11 +665,11 @@ NTSTATUS KpiQueryInformationThread( * \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 + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _In_ KPROCESSOR_MODE AccessMode ) { NTSTATUS 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..3d097c62128d 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; @@ -354,7 +354,7 @@ NTSTATUS KpiVerifyClient( } 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 41dc7ef8a85f..63e4e2d0657c 100644 --- a/KProcessHacker/vm.c +++ b/KProcessHacker/vm.c @@ -22,9 +22,9 @@ #include ULONG KphpGetCopyExceptionInfo( - __in PEXCEPTION_POINTERS ExceptionInfo, - __out PBOOLEAN HaveBadAddress, - __out PULONG_PTR BadAddress + _In_ PEXCEPTION_POINTERS ExceptionInfo, + _Out_ PBOOLEAN HaveBadAddress, + _Out_ PULONG_PTR BadAddress ); #ifdef ALLOC_PRAGMA @@ -38,9 +38,9 @@ ULONG KphpGetCopyExceptionInfo( #define KPH_POOL_COPY_THRESHOLD 0x3ff ULONG KphpGetCopyExceptionInfo( - __in PEXCEPTION_POINTERS ExceptionInfo, - __out PBOOLEAN HaveBadAddress, - __out PULONG_PTR BadAddress + _In_ PEXCEPTION_POINTERS ExceptionInfo, + _Out_ PBOOLEAN HaveBadAddress, + _Out_ PULONG_PTR BadAddress ) { PEXCEPTION_RECORD exceptionRecord; @@ -75,13 +75,13 @@ ULONG KphpGetCopyExceptionInfo( * \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 + _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]; @@ -311,14 +311,14 @@ NTSTATUS KphCopyVirtualMemory( * \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 + _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; From c68a3fc2de37ef39bc445fbae42130c82c717079 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 6 Oct 2017 18:04:29 +1100 Subject: [PATCH 468/839] UserNotes: Fix affinity type on 64bit --- plugins/UserNotes/db.c | 2 +- plugins/UserNotes/db.h | 2 +- plugins/UserNotes/main.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/UserNotes/db.c b/plugins/UserNotes/db.c index 6a7b2b9082ac..76de395f4e98 100644 --- a/plugins/UserNotes/db.c +++ b/plugins/UserNotes/db.c @@ -307,7 +307,7 @@ NTSTATUS LoadDb( PhStringToInteger64(&affinityMask->sr, 10, &affinityInteger); - object->AffinityMask = (ULONG)affinityInteger; + object->AffinityMask = (ULONG_PTR)affinityInteger; } PhClearReference(&tag); diff --git a/plugins/UserNotes/db.h b/plugins/UserNotes/db.h index bf8018c4b9f2..4c88fe239df5 100644 --- a/plugins/UserNotes/db.h +++ b/plugins/UserNotes/db.h @@ -43,7 +43,7 @@ typedef struct _DB_OBJECT ULONG IoPriorityPlusOne; COLORREF BackColor; BOOLEAN Collapse; - ULONG AffinityMask; + ULONG_PTR AffinityMask; } DB_OBJECT, *PDB_OBJECT; VOID InitializeDb( diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index e3c8e0deb96f..409f92957f66 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -171,12 +171,12 @@ PPH_STRING SaveCustomColors( return PhFinalStringBuilderString(&stringBuilder); } -ULONG GetProcessAffinity( +ULONG_PTR GetProcessAffinity( _In_ HANDLE ProcessId ) { HANDLE processHandle; - ULONG affinityMask = 0; + ULONG_PTR affinityMask = 0; PROCESS_BASIC_INFORMATION basicInfo; if (NT_SUCCESS(PhOpenProcess( @@ -190,7 +190,7 @@ ULONG GetProcessAffinity( &basicInfo ))) { - affinityMask = (ULONG)basicInfo.AffinityMask; + affinityMask = basicInfo.AffinityMask; } NtClose(processHandle); @@ -646,9 +646,9 @@ VOID NTAPI MenuHookCallback( 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) + if (object->AffinityMask != newAffinityMask) { - object->AffinityMask = (ULONG)newAffinityMask; + object->AffinityMask = newAffinityMask; changed = TRUE; } } From 3f9e8efe6ae62db6e9ed6cd3e424cd1df826893b Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 8 Oct 2017 16:08:39 +1100 Subject: [PATCH 469/839] peview: Fix searching pdb tab RVA --- tools/peview/pdbprp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 5f232ff44d79..438436d75740 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -883,6 +883,12 @@ BOOLEAN PvSymbolTreeFilterCallback( return TRUE; } + if (node->Pointer[0]) + { + if (WordMatchStringZ(context, node->Pointer)) + return TRUE; + } + return FALSE; } From 485ff5b6888cb84ffd6127ce551c56a1bde92de7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 8 Oct 2017 16:09:34 +1100 Subject: [PATCH 470/839] Setup: FIx multiple desktop icons --- .../CustomSetupTool/CustomSetupTool/appsup.c | 25 ++++++- .../CustomSetupTool/include/appsup.h | 3 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 68 +++++++++++++------ 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index a4c317b4f8ee..51e3eb7de171 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include VOID ExtractResourceToFile( _In_ PWSTR Resource, @@ -349,13 +351,15 @@ 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))) goto CleanupExit; @@ -363,9 +367,28 @@ VOID SetupCreateLink( 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); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h index 8601e3c1cc9e..3f0c27bb759e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/appsup.h @@ -51,7 +51,8 @@ VOID SetupInitializeFont( ); VOID SetupCreateLink( - _In_ PWSTR DestFilePath, + _In_ PWSTR AppUserModelId, + _In_ PWSTR LinkFilePath, _In_ PWSTR FilePath, _In_ PWSTR FileParentDir ); diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 48d5f8a41d72..2050d7bddf58 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -442,27 +442,44 @@ VOID SetupSetWindowsOptions( // Create the startmenu shortcut. if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) { - SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); + SetupCreateLink( + L"ProcessHacker.Desktop.App", + PhGetString(startmenuFolderString), + PhGetString(clientPathString), + PhGetString(Context->SetupInstallPath) + ); PhDereferenceObject(startmenuFolderString); } - // Create the desktop shortcut. - if (Context->SetupCreateDesktopShortcut) + // Create the all users shortcut. + if (Context->SetupCreateDesktopShortcutAllUsers) { - if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) { - SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); + SetupCreateLink( + L"ProcessHacker.Desktop.App", + PhGetString(startmenuFolderString), + PhGetString(clientPathString), + PhGetString(Context->SetupInstallPath) + ); PhDereferenceObject(startmenuFolderString); } } - - // Create the all users shortcut. - if (Context->SetupCreateDesktopShortcutAllUsers) + else { - if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + // Create the desktop shortcut. + if (Context->SetupCreateDesktopShortcut) { - SetupCreateLink(PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(Context->SetupInstallPath)); - PhDereferenceObject(startmenuFolderString); + if (startmenuFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) + { + SetupCreateLink( + L"ProcessHacker.Desktop.App", + PhGetString(startmenuFolderString), + PhGetString(clientPathString), + PhGetString(Context->SetupInstallPath) + ); + PhDereferenceObject(startmenuFolderString); + } } } @@ -472,6 +489,7 @@ VOID SetupSetWindowsOptions( PPH_STRING peviewPathString = PhConcatStrings2(PhGetString(Context->SetupInstallPath), L"\\peview.exe"); SetupCreateLink( + L"PeViewer.Desktop.App", PhGetString(startmenuFolderString), PhGetString(peviewPathString), PhGetString(Context->SetupInstallPath) @@ -495,30 +513,40 @@ VOID SetupSetWindowsOptions( if (Context->SetupCreateDefaultTaskManager) { NTSTATUS status; - HANDLE taskmgrKeyHandle = NULL; + HANDLE taskmgrKeyHandle; - if (NT_SUCCESS(status = PhCreateKey( + status = PhCreateKey( &taskmgrKeyHandle, KEY_READ | KEY_WRITE, PH_KEY_LOCAL_MACHINE, &TaskMgrImageOptionsKeyName, - 0, + OBJ_OPENIF, 0, NULL - ))) + ); + + if (NT_SUCCESS(status)) { PPH_STRING value; UNICODE_STRING valueName; - value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\""); - - // Configure the default Task Manager. RtlInitUnicodeString(&valueName, L"Debugger"); - NtSetValueKey(taskmgrKeyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); + 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); } - else + + if (!NT_SUCCESS(status)) { PhShowStatus(NULL, L"Unable to set the Windows default Task Manager.", status, 0); } From 45828459a283fce31c97d62e0f24661f0e6047a8 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 8 Oct 2017 22:38:20 +1100 Subject: [PATCH 471/839] Improve security dialog AppContainer SID<>Name mappings (#179) * Add fixes for security editor showing unknown SIDS for store app packages * Fix missing security dialog integrity labels --- phlib/include/secedit.h | 32 +++- phlib/include/seceditp.h | 112 +++++++++++++ phlib/secedit.c | 344 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 481 insertions(+), 7 deletions(-) diff --git a/phlib/include/secedit.h b/phlib/include/secedit.h index 50090d4452b1..cde07e7702d4 100644 --- a/phlib/include/secedit.h +++ b/phlib/include/secedit.h @@ -59,7 +59,10 @@ FORCEINLINE ACCESS_MASK PhGetAccessForGetSecurity( if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) || (SecurityInformation & GROUP_SECURITY_INFORMATION) || - (SecurityInformation & DACL_SECURITY_INFORMATION) + (SecurityInformation & DACL_SECURITY_INFORMATION) || + (SecurityInformation & LABEL_SECURITY_INFORMATION) || + (SecurityInformation & ATTRIBUTE_SECURITY_INFORMATION) || + (SecurityInformation & SCOPE_SECURITY_INFORMATION) ) { access |= READ_CONTROL; @@ -70,6 +73,11 @@ FORCEINLINE ACCESS_MASK PhGetAccessForGetSecurity( access |= ACCESS_SYSTEM_SECURITY; } + if (SecurityInformation & BACKUP_SECURITY_INFORMATION) + { + access |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; + } + return access; } @@ -81,22 +89,38 @@ FORCEINLINE ACCESS_MASK PhGetAccessForSetSecurity( if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) || - (SecurityInformation & GROUP_SECURITY_INFORMATION) + (SecurityInformation & GROUP_SECURITY_INFORMATION) || + (SecurityInformation & LABEL_SECURITY_INFORMATION) ) { access |= WRITE_OWNER; } - if (SecurityInformation & DACL_SECURITY_INFORMATION) + 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) + 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; } diff --git a/phlib/include/seceditp.h b/phlib/include/seceditp.h index d90cfd43d76c..3bd325245600 100644 --- a/phlib/include/seceditp.h +++ b/phlib/include/seceditp.h @@ -19,6 +19,25 @@ typedef struct BOOLEAN IsPage; } PhSecurityInformation; +typedef struct +{ + ISecurityInformation2Vtbl *VTable; + + ULONG RefCount; +} PhSecurityInformation2; + +typedef struct +{ + IDataObjectVtbl *VTable; + + ULONG RefCount; + + ULONG SidCount; + PSID *Sids; + + PPH_LIST NameCache; +} PhSecurityIDataObject; + ISecurityInformation *PhSecurityInformation_Create( _In_ PWSTR ObjectName, _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, @@ -90,4 +109,97 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( _In_ SI_PAGE_TYPE uPage ); +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 + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_QueryInterface( + _In_ IDataObject *This, + _In_ REFIID Riid, + _Out_ 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( + 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 + ); + #endif diff --git a/phlib/secedit.c b/phlib/secedit.c index 24bd903c867a..98e6df3ee5ab 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -3,6 +3,7 @@ * object security editor * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -22,6 +23,7 @@ #include #include +#include #include #include @@ -42,6 +44,31 @@ static ISecurityInformationVtbl PhSecurityInformation_VTable = PhSecurityInformation_PropertySheetPageCallback }; +static ISecurityInformation2Vtbl PhSecurityInformation_VTable2 = +{ + PhSecurityInformation2_QueryInterface, + PhSecurityInformation2_AddRef, + PhSecurityInformation2_Release, + PhSecurityInformation2_IsDaclCanonical, + PhSecurityInformation2_LookupSids +}; + +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 +}; + /** * Creates a security editor page. * @@ -172,6 +199,8 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( _Out_ PVOID *Object ) { + PhSecurityInformation *this = (PhSecurityInformation *)This; + if ( IsEqualIID(Riid, &IID_IUnknown) || IsEqualIID(Riid, &IID_ISecurityInformation) @@ -181,6 +210,20 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( *Object = This; return S_OK; } + else if (IsEqualGUID(Riid, &IID_ISecurityInformation2)) + { + if (WindowsVersion >= WINDOWS_8) + { + PhSecurityInformation2 *info; + + info = PhAllocate(sizeof(PhSecurityInformation2)); + info->VTable = &PhSecurityInformation_VTable2; + info->RefCount = 1; + + *Object = info; + return S_OK; + } + } *Object = NULL; return E_NOINTERFACE; @@ -230,9 +273,9 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation( SI_EDIT_AUDITS | SI_EDIT_OWNER | SI_EDIT_PERMS | - SI_ADVANCED | - SI_NO_ACL_PROTECT | - SI_NO_TREE_APPLY; + SI_ADVANCED; + //SI_NO_ACL_PROTECT | + //SI_NO_TREE_APPLY; ObjectInfo->hInstance = NULL; ObjectInfo->pszObjectName = this->ObjectName->Buffer; @@ -347,6 +390,301 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( return E_NOTIMPL; } +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 + ) +{ + PhSecurityIDataObject *dataObject; + + dataObject = PhAllocate(sizeof(PhSecurityInformation)); + dataObject->VTable = &PhDataObject_VTable; + dataObject->RefCount = 1; + + dataObject->SidCount = cSids; + dataObject->Sids = rgpSids; + dataObject->NameCache = PhCreateList(1); + + *ppdo = (LPDATAOBJECT)dataObject; + + return S_OK; +} + +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) + { + for (ULONG i = 0; i < this->NameCache->Count; i++) + PhDereferenceObject(this->NameCache->Items[i]); + 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; + + sidInfoList = (PSID_INFO_LIST)GlobalAlloc(GMEM_ZEROINIT, sizeof(SID_INFO_LIST) + (sizeof(SID_INFO) * this->SidCount)); + sidInfoList->cItems = this->SidCount; + + for (ULONG i = 0; i < this->SidCount; i++) + { + SID_INFO sidInfo; + PPH_STRING sidString; + SID_NAME_USE sidUse; + + memset(&sidInfo, 0, sizeof(SID_INFO)); + + sidInfo.pSid = this->Sids[i]; + + if (sidString = PhGetSidFullName(sidInfo.pSid, FALSE, &sidUse)) + { + switch (sidUse) + { + 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 = PhSidToStringSid(sidInfo.pSid)) + { + static PH_STRINGREF appcontainerMappings = PH_STRINGREF_INIT(L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Mappings\\"); + HANDLE keyHandle; + PPH_STRING keyPath; + PPH_STRING packageName = NULL; + + if (PhEqualString2(sidString, L"S-1-15-3-4096", FALSE)) + { + // Special case for Edge and Internet Explorer objects. + packageName = PhCreateString(L"InternetExplorer (APP_PACKAGE)"); + sidInfo.pwzCommonName = PhGetString(packageName);; + PhAddItemList(this->NameCache, packageName); + sidInfoList->aSidInfo[i] = sidInfo; + continue; + } + + keyPath = PhConcatStringRef2(&appcontainerMappings, &sidString->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &keyPath->sr, + 0 + ))) + { + packageName = PhQueryRegistryString(keyHandle, L"Moniker"); + NtClose(keyHandle); + } + + if (packageName) + { + PhMoveReference(&packageName, PhFormatString(L"%s (APP_PACKAGE)", PhGetString(packageName))); + sidInfo.pwzCommonName = PhGetString(packageName); + PhAddItemList(this->NameCache, packageName); + } + + PhDereferenceObject(keyPath); + PhDereferenceObject(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; +} + NTSTATUS PhpGetObjectSecurityWithTimeout( _In_ HANDLE Handle, _In_ SECURITY_INFORMATION SecurityInformation, From 7e51171ad4885d9af22da938f611f950c46bcd8d Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 8 Oct 2017 22:41:37 +1100 Subject: [PATCH 472/839] Remove unused line --- phlib/secedit.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/phlib/secedit.c b/phlib/secedit.c index 98e6df3ee5ab..83e5611e9502 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -199,8 +199,6 @@ HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( _Out_ PVOID *Object ) { - PhSecurityInformation *this = (PhSecurityInformation *)This; - if ( IsEqualIID(Riid, &IID_IUnknown) || IsEqualIID(Riid, &IID_ISecurityInformation) From 1bbddde5523a7b9807ba62f6d5e08035cb5d13b5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 8 Oct 2017 22:42:10 +1100 Subject: [PATCH 473/839] Add binary setting wrappers (unused) --- phlib/settings.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/phlib/settings.c b/phlib/settings.c index bb78e121b21b..a068218c07b2 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -479,6 +479,21 @@ _May_raise_ PPH_STRING PhGetStringSetting( 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 @@ -619,6 +634,19 @@ _May_raise_ VOID PhSetStringSetting2( 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 ) From 565298402f276abf3c637b089863802d5b4ebd96 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 9 Oct 2017 11:55:05 +1100 Subject: [PATCH 474/839] Fix macro usage --- phlib/lsasup.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/phlib/lsasup.c b/phlib/lsasup.c index c81051484571..b4ea741e4cea 100644 --- a/phlib/lsasup.c +++ b/phlib/lsasup.c @@ -38,11 +38,13 @@ NTSTATUS PhOpenLsaPolicy( _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 ); From ce3ea92cf5338196ca8c2f3db474c6fb612e3ca5 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 9 Oct 2017 12:22:51 +1100 Subject: [PATCH 475/839] Fix disabling token menu --- ProcessHacker/tokprp.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 97852c2c9925..ec8401daba6a 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -818,25 +818,23 @@ INT_PTR CALLBACK PhpTokenPageProc( // 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( + if (NT_SUCCESS(status = tokenPageContext->OpenObject( &tokenHandle, TOKEN_QUERY, tokenPageContext->Context ))) { - if (NT_SUCCESS(PhGetTokenIntegrityLevel( + if (NT_SUCCESS(status = PhGetTokenIntegrityLevel( tokenHandle, &integrityLevel, NULL ))) { - ULONG i; - - for (i = 0; i < menu->Items->Count; i++) + for (ULONG i = 0; i < menu->Items->Count; i++) { PPH_EMENU_ITEM menuItem = menu->Items->Items[i]; - if (menuItem->Id == integrityLevel) + if (menuItem->Id == (ULONG)integrityLevel) { menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; } @@ -850,6 +848,15 @@ INT_PTR CALLBACK PhpTokenPageProc( 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, From a8553b4a6d5dfa29027338eceea41e4f53823e63 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 9 Oct 2017 12:23:41 +1100 Subject: [PATCH 476/839] Remove unused export --- ProcessHacker/ProcessHacker.def | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 3cd1060436f0..51007a4e1896 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -636,7 +636,6 @@ EXPORTS PhGetJsonArrayLong64 PhGetJsonArrayLength PhGetJsonArrayIndexObject - PhGetJsonObjectAsArrayList ; cache PhClearCacheDirectory From ce910c82f22558902a6ab4129f81dda2be88bbcb Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 9 Oct 2017 12:25:57 +1100 Subject: [PATCH 477/839] Improve default task manager error checking, Fix color dialog window position --- ProcessHacker/options.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index e095121f8975..31ef37cd061c 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -902,7 +902,7 @@ VOID PhpSetDefaultTaskManager( HANDLE taskmgrKeyHandle; UNICODE_STRING valueName; - if (NT_SUCCESS(PhCreateKey( + status = PhCreateKey( &taskmgrKeyHandle, KEY_READ | KEY_WRITE, PH_KEY_LOCAL_MACHINE, @@ -910,7 +910,9 @@ VOID PhpSetDefaultTaskManager( OBJ_OPENIF, 0, NULL - ))) + ); + + if (NT_SUCCESS(status)) { RtlInitUnicodeString(&valueName, L"Debugger"); @@ -926,12 +928,12 @@ VOID PhpSetDefaultTaskManager( status = NtSetValueKey(taskmgrKeyHandle, &valueName, 0, REG_SZ, quotedFileName->Buffer, (ULONG)quotedFileName->Length + 2); } - if (!NT_SUCCESS(status)) - PhShowStatus(ParentWindowHandle, L"Unable to replace Task Manager", status, 0); - NtClose(taskmgrKeyHandle); } + if (!NT_SUCCESS(status)) + PhShowStatus(ParentWindowHandle, L"Unable to replace Task Manager", status, 0); + if (PhSettingsFileName) PhSaveSettings(PhSettingsFileName->Buffer); } @@ -1707,6 +1709,27 @@ COLORREF NTAPI PhpColorItemColorFunction( 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); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + } + + return FALSE; +} + INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -1816,7 +1839,8 @@ INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( chooseColor.hwndOwner = hwndDlg; chooseColor.rgbResult = item->CurrentColor; chooseColor.lpCustColors = customColors; - chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; + chooseColor.lpfnHook = PhpColorDlgHookProc; + chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK | CC_RGBINIT; if (ChooseColor(&chooseColor)) { From 5c1dddd6982070f78166188951fd8478a26c9312 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 9 Oct 2017 16:53:30 +1100 Subject: [PATCH 478/839] ToolStatus: Fix extra statusbar memory allocations, Fix regression customizing toolbar/statusbar items --- plugins/ToolStatus/customizesb.c | 39 ++++++++++++-------------------- plugins/ToolStatus/customizetb.c | 24 ++++++++++---------- plugins/ToolStatus/statusbar.c | 32 ++++---------------------- plugins/ToolStatus/toolstatus.h | 5 ---- 4 files changed, 30 insertions(+), 70 deletions(-) diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index edb1929a8caf..d823a35a3339 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -52,14 +52,7 @@ VOID CustomizeInsertStatusBarItem( _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); + PhInsertItemList(StatusBarItemList, Index, UlongToPtr(Button->IdCommand)); StatusBarUpdate(TRUE); } @@ -224,14 +217,10 @@ VOID CustomizeLoadStatusBarItems( 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; + button->IdCommand = PtrToUlong(StatusBarItemList->Items[index]); ListBox_AddItemData(Context->CurrentListHandle, button); } @@ -394,11 +383,11 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( CustomizeAddStatusBarItem(context, index, indexto); } break; - case LBN_KILLFOCUS: - { - Button_Enable(context->AddButtonHandle, FALSE); - } - break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->AddButtonHandle, FALSE); + // } + // break; } } break; @@ -475,13 +464,13 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( CustomizeRemoveStatusBarItem(context, index); } break; - case LBN_KILLFOCUS: - { - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - Button_Enable(context->RemoveButtonHandle, FALSE); - } - break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->MoveUpButtonHandle, FALSE); + // Button_Enable(context->MoveDownButtonHandle, FALSE); + // Button_Enable(context->RemoveButtonHandle, FALSE); + // } + // break; } } break; diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index 7e6e8eb74f28..c103c01b4d18 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -581,11 +581,11 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( CustomizeAddToolbarItem(context, index, indexto); } break; - case LBN_KILLFOCUS: - { - Button_Enable(context->AddButtonHandle, FALSE); - } - break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->AddButtonHandle, FALSE); + // } + // break; } } break; @@ -661,13 +661,13 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( CustomizeRemoveToolbarItem(context, index); } break; - case LBN_KILLFOCUS: - { - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - Button_Enable(context->RemoveButtonHandle, FALSE); - } - break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->MoveUpButtonHandle, FALSE); + // Button_Enable(context->MoveDownButtonHandle, FALSE); + // Button_Enable(context->RemoveButtonHandle, FALSE); + // } + // break; } } break; diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index 92a8515eb812..fa5df12ed8c8 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -57,14 +57,7 @@ VOID StatusBarLoadDefault( 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); + PhAddItemList(StatusBarItemList, UlongToPtr(StatusBarItems[i])); } } @@ -116,14 +109,7 @@ VOID StatusBarLoadSettings( 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); + PhInsertItemList(StatusBarItemList, i, UlongToPtr((ULONG)idInteger)); } } } @@ -145,12 +131,10 @@ VOID StatusBarSaveSettings( for (ULONG i = 0; i < StatusBarItemList->Count; i++) { - PSTATUSBAR_ITEM item = StatusBarItemList->Items[i]; - PhAppendFormatStringBuilder( &stringBuilder, L"%lu|", - item->Id + PtrToUlong(StatusBarItemList->Items[i]) ); } @@ -165,11 +149,6 @@ VOID StatusBarResetSettings( VOID ) { - for (ULONG i = 0; i < StatusBarItemList->Count; i++) - { - PhFree(StatusBarItemList->Items[i]); - } - PhClearList(StatusBarItemList); StatusBarLoadDefault(); @@ -296,11 +275,8 @@ VOID StatusBarUpdate( { SIZE size; ULONG width; - PSTATUSBAR_ITEM item; - - item = StatusBarItemList->Items[i]; - switch (item->Id) + switch (PtrToUlong(StatusBarItemList->Items[i])) { case ID_STATUS_CPUUSAGE: { diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 202cc23ced73..b905a915f8ff 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -268,11 +268,6 @@ 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; From 4b6f35f5c2eb3c00a7c166bd855b532728ab506b Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 16:23:10 +1100 Subject: [PATCH 479/839] Fix resource leak --- phlib/guisup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phlib/guisup.c b/phlib/guisup.c index b9667e8d2509..d952d7971cae 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -862,9 +862,13 @@ HWND PhCreateDialogFromTemplate( dialog = LockResource(resourceHandle); if (!dialog) + { + FreeResource(resourceHandle); return NULL; + } dialogCopy = PhAllocateCopy(dialog, resourceSize); + FreeResource(resourceHandle); if (dialogCopy->signature == 0xffff) { From 06cc8aa9c1e88490f3cfba01a5d2fb3423a21042 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 18:52:59 +1100 Subject: [PATCH 480/839] Improve FileVersionInfo lookup performance --- phlib/util.c | 58 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index b72bef57db02..100662c2e2e8 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1530,37 +1530,49 @@ PVOID PhGetFileVersionInfo( _In_ PWSTR FileName ) { - ULONG versionInfoSize; - ULONG dummy; + HMODULE resourceInstance; + HRSRC resourceInfo; + ULONG resourceSize; + HGLOBAL resourceHandle; PVOID versionInfo; + PVOID versionInfoCopy = NULL; - versionInfoSize = GetFileVersionInfoSize( - FileName, - &dummy - ); + resourceInstance = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE); - if (versionInfoSize) - { - versionInfo = PhAllocate(versionInfoSize); + if (!resourceInstance) + return NULL; - if (!GetFileVersionInfo( - FileName, - 0, - versionInfoSize, - versionInfo - )) - { - PhFree(versionInfo); + resourceInfo = FindResource(resourceInstance, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO); - return NULL; - } - } - else + if (!resourceInfo) + goto CleanupExit; + + resourceSize = SizeofResource(resourceInstance, resourceInfo); + + if (resourceSize == 0) + goto CleanupExit; + + resourceHandle = LoadResource(resourceInstance, resourceInfo); + + if (!resourceHandle) + goto CleanupExit; + + versionInfo = LockResource(resourceHandle); + + if (!versionInfo) { - return NULL; + FreeResource(resourceHandle); + goto CleanupExit; } - return versionInfo; + versionInfoCopy = PhAllocateCopy(versionInfo, resourceSize); + FreeResource(resourceHandle); + +CleanupExit: + + FreeLibrary(resourceInstance); + + return versionInfoCopy; } /** From d70896be4adf7e9d5b1e15d9fbb41c6812cc68b9 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 19:47:59 +1100 Subject: [PATCH 481/839] Revert "Fix resource leak" This reverts commit 4b6f35f5c2eb3c00a7c166bd855b532728ab506b. --- phlib/guisup.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index d952d7971cae..b9667e8d2509 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -862,13 +862,9 @@ HWND PhCreateDialogFromTemplate( dialog = LockResource(resourceHandle); if (!dialog) - { - FreeResource(resourceHandle); return NULL; - } dialogCopy = PhAllocateCopy(dialog, resourceSize); - FreeResource(resourceHandle); if (dialogCopy->signature == 0xffff) { From 49d857fc34262dbfa9282c3922d7c61795d8d3e0 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 21:42:09 +1100 Subject: [PATCH 482/839] Add PhLoadResource function (removes 600 unnecessary calls on startup) --- phlib/guisup.c | 43 +++++++--------------- phlib/include/phutil.h | 10 ++++++ phlib/util.c | 82 ++++++++++++++++++++++++------------------ phnt/include/ntldr.h | 12 +++++++ 4 files changed, 82 insertions(+), 65 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index b9667e8d2509..4871ac46d1ec 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -837,47 +837,30 @@ HWND PhCreateDialogFromTemplate( _In_ PVOID Parameter ) { - HRSRC resourceInfo; - ULONG resourceSize; - HGLOBAL resourceHandle; - PDLGTEMPLATEEX dialog; - PDLGTEMPLATEEX dialogCopy; + PDLGTEMPLATEEX dialogTemplate; 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) + if (!PhLoadResource(Instance, Template, RT_DIALOG, NULL, &dialogTemplate)) return NULL; - dialogCopy = PhAllocateCopy(dialog, resourceSize); - - if (dialogCopy->signature == 0xffff) + if (dialogTemplate->signature == USHRT_MAX) { - dialogCopy->style = Style; + dialogTemplate->style = Style; } else { - ((DLGTEMPLATE *)dialogCopy)->style = Style; + ((DLGTEMPLATE *)dialogTemplate)->style = Style; } - dialogHandle = CreateDialogIndirectParam(Instance, (DLGTEMPLATE *)dialogCopy, Parent, DialogProc, (LPARAM)Parameter); + dialogHandle = CreateDialogIndirectParam( + Instance, + (DLGTEMPLATE *)dialogTemplate, + Parent, + DialogProc, + (LPARAM)Parameter + ); - PhFree(dialogCopy); + PhFree(dialogTemplate); return dialogHandle; } diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 9eddea371402..a5588c4a143d 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1160,6 +1160,16 @@ PhGetNamespaceHandle( return directory; } +PHLIBAPI +BOOLEAN +NTAPI +PhLoadResource( + _In_ PVOID DllBase, + _In_ PCWSTR Name, + _In_ PCWSTR Type, + _Out_opt_ ULONG *ResourceLength, + _Out_ PVOID *ResourceBuffer + ); #ifdef __cplusplus } diff --git a/phlib/util.c b/phlib/util.c index 100662c2e2e8..d0502830b9de 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1530,49 +1530,28 @@ PVOID PhGetFileVersionInfo( _In_ PWSTR FileName ) { - HMODULE resourceInstance; - HRSRC resourceInfo; - ULONG resourceSize; - HGLOBAL resourceHandle; + PVOID libraryModule; PVOID versionInfo; - PVOID versionInfoCopy = NULL; - resourceInstance = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE); + libraryModule = LoadLibraryEx(FileName, NULL, LOAD_LIBRARY_AS_DATAFILE); - if (!resourceInstance) + if (!libraryModule) return NULL; - resourceInfo = FindResource(resourceInstance, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO); - - if (!resourceInfo) - goto CleanupExit; - - resourceSize = SizeofResource(resourceInstance, resourceInfo); - - if (resourceSize == 0) - goto CleanupExit; - - resourceHandle = LoadResource(resourceInstance, resourceInfo); - - if (!resourceHandle) - goto CleanupExit; - - versionInfo = LockResource(resourceHandle); - - if (!versionInfo) + if (PhLoadResource( + libraryModule, + MAKEINTRESOURCE(VS_VERSION_INFO), + VS_FILE_INFO, + NULL, + &versionInfo + )) { - FreeResource(resourceHandle); - goto CleanupExit; + FreeLibrary(libraryModule); + return versionInfo; } - versionInfoCopy = PhAllocateCopy(versionInfo, resourceSize); - FreeResource(resourceHandle); - -CleanupExit: - - FreeLibrary(resourceInstance); - - return versionInfoCopy; + FreeLibrary(libraryModule); + return NULL; } /** @@ -5157,3 +5136,36 @@ VOID PhDeleteCacheFile( PhDereferenceObject(cacheFullFilePath); } } + +BOOLEAN PhLoadResource( + _In_ PVOID DllBase, + _In_ PCWSTR Name, + _In_ PCWSTR Type, + _Out_opt_ ULONG *ResourceLength, + _Out_ PVOID *ResourceBuffer + ) +{ + ULONG resourceLength; + PVOID resourceInfo; + PVOID resourceBuffer; + + resourceInfo = FindResource(DllBase, Name, Type); + + if (!resourceInfo) + return FALSE; + + if (!NT_SUCCESS(LdrAccessResource(DllBase, resourceInfo, &resourceBuffer, &resourceLength))) + return FALSE; + + if (!resourceBuffer) + return FALSE; + + if (resourceLength == 0) + return FALSE; + + if (ResourceLength) + *ResourceLength = resourceLength; + *ResourceBuffer = PhAllocateCopy(resourceBuffer, resourceLength); + + return TRUE; +} diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 6ec947c0f407..f89b5f2360b0 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -586,4 +586,16 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX PVOID DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; +// Resources + +NTSYSAPI +NTSTATUS +NTAPI +LdrAccessResource( + _In_ PVOID BaseAddress, + _In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, + _Out_opt_ PVOID *ResourceBuffer, + _Out_opt_ ULONG *ResourceLength + ); + #endif From 275bfe85ff53efc764fa78ea38f8a3a12be48946 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 21:42:54 +1100 Subject: [PATCH 483/839] Convert macro to function --- phlib/include/phutil.h | 67 ++---------------------------------------- phlib/util.c | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index a5588c4a143d..cdfbbdbdba2b 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1092,74 +1092,13 @@ PhDeleteCacheFile( _In_ PPH_STRING FileName ); -FORCEINLINE -HANDLE +PHLIBAPI +HANDLE NTAPI 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)((PCHAR)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; -} PHLIBAPI BOOLEAN NTAPI diff --git a/phlib/util.c b/phlib/util.c index d0502830b9de..5a4089afec2d 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5137,6 +5137,72 @@ VOID PhDeleteCacheFile( } } +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)((PCHAR)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; +} + BOOLEAN PhLoadResource( _In_ PVOID DllBase, _In_ PCWSTR Name, From 0be81cc138e902fe22aa9acec1296d68f4469687 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 21:43:54 +1100 Subject: [PATCH 484/839] Searchbox: Use PhLoadResource function --- ProcessHacker/searchbox.c | 20 +++----------------- tools/peview/searchbox.c | 18 +++--------------- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 8581d2d15cfd..c60f2d6780f0 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -552,8 +552,6 @@ HBITMAP PhLoadPngImageFromResource( BOOLEAN success = FALSE; UINT frameCount = 0; ULONG resourceLength = 0; - HGLOBAL resourceHandle = NULL; - HRSRC resourceHandleSource = NULL; WICInProcPointer resourceBuffer = NULL; HDC screenHdc = NULL; HDC bufferDc = NULL; @@ -573,18 +571,8 @@ HBITMAP PhLoadPngImageFromResource( 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) + if (!PhLoadResource(DllBase, Name, L"PNG", &resourceLength, &resourceBuffer)) goto CleanupExit; // Create the Stream @@ -694,13 +682,11 @@ HBITMAP PhLoadPngImageFromResource( if (wicFactory) IWICImagingFactory_Release(wicFactory); - if (resourceHandle) - FreeResource(resourceHandle); + if (resourceBuffer) + PhFree(resourceBuffer); if (success) - { return bitmapHandle; - } DeleteObject(bitmapHandle); return NULL; diff --git a/tools/peview/searchbox.c b/tools/peview/searchbox.c index a758b0e2816b..e7774bb44573 100644 --- a/tools/peview/searchbox.c +++ b/tools/peview/searchbox.c @@ -160,8 +160,6 @@ HBITMAP PhLoadPngImageFromResource( BOOLEAN success = FALSE; UINT frameCount = 0; ULONG resourceLength = 0; - HGLOBAL resourceHandle = NULL; - HRSRC resourceHandleSource = NULL; WICInProcPointer resourceBuffer = NULL; HDC screenHdc = NULL; HDC bufferDc = NULL; @@ -181,18 +179,8 @@ HBITMAP PhLoadPngImageFromResource( 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) + if (!PhLoadResource(DllBase, Name, L"PNG", &resourceLength, &resourceBuffer)) goto CleanupExit; // Create the Stream @@ -302,8 +290,8 @@ HBITMAP PhLoadPngImageFromResource( if (wicFactory) IWICImagingFactory_Release(wicFactory); - if (resourceHandle) - FreeResource(resourceHandle); + if (resourceBuffer) + PhFree(resourceBuffer); if (success) { From 172d4330950d1e9ccd36ea618f5a9071a7ddb314 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 21:46:52 +1100 Subject: [PATCH 485/839] KPH: Fix issue deleting service after exit --- phlib/include/kphuser.h | 7 ++++++ phlib/kph.c | 54 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index 102534b55a62..e58b99f9de10 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -66,6 +66,13 @@ KphSetParameters( _In_ PKPH_PARAMETERS Parameters ); +PHLIBAPI +VOID +NTAPI +KphSetServiceSecurity( + _In_ SC_HANDLE ServiceHandle + ); + PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/kph.c b/phlib/kph.c index e27f622ca57b..43935b3f7d28 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -180,6 +180,8 @@ NTSTATUS KphConnect2Ex( { 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. @@ -215,7 +217,7 @@ NTSTATUS KphConnect2Ex( } CreateAndConnectEnd: - if (created) + 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 @@ -344,6 +346,54 @@ NTSTATUS KphSetParameters( 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)((PCHAR)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 @@ -388,6 +438,8 @@ NTSTATUS KphInstallEx( if (serviceHandle) { + KphSetServiceSecurity(serviceHandle); + // See KphConnect2Ex for more details. if (Parameters) { From 2450843721887cd096ccd5996aae4c6a3870a69f Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 10 Oct 2017 22:57:53 +1100 Subject: [PATCH 486/839] Fix options window seperator drawing issues (experimental) --- ProcessHacker/ProcessHacker.rc | 11 +-- ProcessHacker/options.c | 174 +++++++++++++++++++++++++++++---- 2 files changed, 159 insertions(+), 26 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index ffb1f8741c04..19a03b36c7da 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1854,12 +1854,11 @@ 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 + CONTROL "",IDC_SECTIONTREE,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_TRACKSELECT | TVS_FULLROWSELECT | WS_BORDER | WS_HSCROLL | WS_TABSTOP,2,0,103,246 + CONTROL "",IDD_CONTAINER,"#32770",0x44c,105,0,318,229,WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT | 0xc0000800L + PUSHBUTTON "Reset",IDC_RESET,107,231,50,14 PUSHBUTTON "Apply",IDC_APPLY,319,231,50,14,NOT WS_VISIBLE - PUSHBUTTON "Cleanup",IDC_CLEANUP,165,231,50,14 + PUSHBUTTON "Cleanup",IDC_CLEANUP,159,231,50,14 END @@ -2398,7 +2397,7 @@ BEGIN IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 2 - BOTTOMMARGIN, 245 + BOTTOMMARGIN, 246 END END #endif // APSTUDIO_INVOKED diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 31ef37cd061c..ff991452817b 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -80,6 +80,15 @@ INT_PTR CALLBACK PhOptionsDialogProc( _In_ LPARAM lParam ); +LRESULT CALLBACK PhpSelectionTreeSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ UINT_PTR uIdSubclass, + _In_ ULONG_PTR dwRefData + ); + VOID PhOptionsDestroySection( _In_ PPH_OPTIONS_SECTION Section ); @@ -325,8 +334,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); ContainerControl = GetDlgItem(PhOptionsWindowHandle, 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); @@ -334,13 +341,15 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhInitializeLayoutManager(&WindowLayoutManager, PhOptionsWindowHandle); 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(PhOptionsWindowHandle, IDC_RESET), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_CLEANUP), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); //PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_APPLY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + SetWindowSubclass(OptionsTreeControl, PhpSelectionTreeSubclassProc, 0, 0); + SendMessage(OptionsTreeControl, WM_THEMECHANGED, 0, 0); + EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); { @@ -451,23 +460,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( } } break; - case WM_DRAWITEM: - { - PDRAWITEMSTRUCT drawInfo = (PDRAWITEMSTRUCT)lParam; - - if (drawInfo->CtlID == IDC_SEPARATOR) - { - RECT rect; - - rect = drawInfo->rcItem; - rect.right = 2; - 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; @@ -1914,3 +1906,145 @@ INT_PTR CALLBACK PhpOptionsGraphsDlgProc( return FALSE; } + +VOID PhpSelectionTreeDrawThemedFrame( + _In_ HWND hWnd, + _In_ HRGN UpdateRegion, + _In_ HTHEME ThemeData + ) +{ + INT systemEdgeX; + INT systemEdgeY; + HDC hdc; + ULONG flags; + + systemEdgeX = GetSystemMetrics(SM_CXEDGE); + systemEdgeY = GetSystemMetrics(SM_CYEDGE); + + if (UpdateRegion == (HRGN)1) + 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)) + { + RECT windowRect; + RECT clientRect; + RECT tempRect; + LONG sizingBorderWidth; + LONG borderX; + LONG borderY; + + GetWindowRect(hWnd, &windowRect); + windowRect.right -= windowRect.left; + windowRect.bottom -= windowRect.top; + windowRect.left = 0; + windowRect.top = 0; + + clientRect.left = windowRect.left + systemEdgeX; + clientRect.top = windowRect.top + systemEdgeY; + clientRect.right = windowRect.right - systemEdgeX; + clientRect.bottom = windowRect.bottom - 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. + tempRect = windowRect; + tempRect.left = tempRect.right - 2; + FillRect(hdc, &tempRect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); + tempRect.left = tempRect.right - 1; + FillRect(hdc, &tempRect, GetSysColorBrush(COLOR_3DSHADOW)); + + // 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(ThemeData, 0, 0, TMT_SIZINGBORDERWIDTH, &sizingBorderWidth))) + { + borderX = sizingBorderWidth; + borderY = sizingBorderWidth; + } + else + { + borderX = GetSystemMetrics(SM_CXBORDER); + borderY = GetSystemMetrics(SM_CYBORDER); + } + + if (borderX < systemEdgeX || borderY < systemEdgeY) + { + windowRect.left += systemEdgeX - borderX; + windowRect.top += systemEdgeY - borderY; + windowRect.right -= systemEdgeX - borderX; + windowRect.bottom -= systemEdgeY - borderY; + + FillRect(hdc, &windowRect, GetSysColorBrush(COLOR_3DFACE)); + } + + ReleaseDC(hWnd, hdc); + } +} + +LRESULT CALLBACK PhpSelectionTreeSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ UINT_PTR uIdSubclass, + _In_ ULONG_PTR dwRefData + ) +{ + HTHEME treeThemeData = (HTHEME)GetWindowLongPtr(hWnd, GWLP_USERDATA); + + switch (uMsg) + { + case WM_DESTROY: + { + RemoveWindowSubclass(hWnd, PhpSelectionTreeSubclassProc, uIdSubclass); + + CloseThemeData(treeThemeData); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)NULL); + } + break; + case WM_SYSCOLORCHANGE: + case WM_SETFOCUS: + case WM_KILLFOCUS: + case WM_ENABLE: + { + if (treeThemeData) + { + PhpSelectionTreeDrawThemedFrame(hWnd, NULL, treeThemeData); + } + } + break; + case WM_THEMECHANGED: + { + if (treeThemeData) + { + CloseThemeData(treeThemeData); + treeThemeData = NULL; + } + + treeThemeData = OpenThemeData(hWnd, VSCLASS_TREEVIEW); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)treeThemeData); + InvalidateRect(hWnd, NULL, TRUE); + } + break; + case WM_NCPAINT: + { + if (treeThemeData) + { + PhpSelectionTreeDrawThemedFrame(hWnd, (HRGN)wParam, treeThemeData); + return TRUE; + } + } + break; + } + + return DefSubclassProc(hWnd, uMsg, wParam, lParam); +} + From 349a98c723d34997087161f5e96c06e4dc282be1 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 11 Oct 2017 00:31:22 +1100 Subject: [PATCH 487/839] Revert "Fix options window seperator drawing issues (experimental)" This reverts commit 2450843721887cd096ccd5996aae4c6a3870a69f. --- ProcessHacker/ProcessHacker.rc | 11 ++- ProcessHacker/options.c | 174 ++++----------------------------- 2 files changed, 26 insertions(+), 159 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 19a03b36c7da..ffb1f8741c04 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1854,11 +1854,12 @@ 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_BORDER | WS_HSCROLL | WS_TABSTOP,2,0,103,246 - CONTROL "",IDD_CONTAINER,"#32770",0x44c,105,0,318,229,WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT | 0xc0000800L - PUSHBUTTON "Reset",IDC_RESET,107,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,159,231,50,14 + PUSHBUTTON "Cleanup",IDC_CLEANUP,165,231,50,14 END @@ -2397,7 +2398,7 @@ BEGIN IDD_OPTIONS, DIALOG BEGIN LEFTMARGIN, 2 - BOTTOMMARGIN, 246 + BOTTOMMARGIN, 245 END END #endif // APSTUDIO_INVOKED diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index ff991452817b..31ef37cd061c 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -80,15 +80,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( _In_ LPARAM lParam ); -LRESULT CALLBACK PhpSelectionTreeSubclassProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - VOID PhOptionsDestroySection( _In_ PPH_OPTIONS_SECTION Section ); @@ -334,6 +325,8 @@ INT_PTR CALLBACK PhOptionsDialogProc( OptionsTreeControl = GetDlgItem(PhOptionsWindowHandle, IDC_SECTIONTREE); ContainerControl = GetDlgItem(PhOptionsWindowHandle, 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); @@ -341,15 +334,13 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhInitializeLayoutManager(&WindowLayoutManager, PhOptionsWindowHandle); 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(PhOptionsWindowHandle, IDC_RESET), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_CLEANUP), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); //PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_APPLY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - SetWindowSubclass(OptionsTreeControl, PhpSelectionTreeSubclassProc, 0, 0); - SendMessage(OptionsTreeControl, WM_THEMECHANGED, 0, 0); - EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); { @@ -460,6 +451,23 @@ INT_PTR CALLBACK PhOptionsDialogProc( } } break; + case WM_DRAWITEM: + { + PDRAWITEMSTRUCT drawInfo = (PDRAWITEMSTRUCT)lParam; + + if (drawInfo->CtlID == IDC_SEPARATOR) + { + RECT rect; + + rect = drawInfo->rcItem; + rect.right = 2; + 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; @@ -1906,145 +1914,3 @@ INT_PTR CALLBACK PhpOptionsGraphsDlgProc( return FALSE; } - -VOID PhpSelectionTreeDrawThemedFrame( - _In_ HWND hWnd, - _In_ HRGN UpdateRegion, - _In_ HTHEME ThemeData - ) -{ - INT systemEdgeX; - INT systemEdgeY; - HDC hdc; - ULONG flags; - - systemEdgeX = GetSystemMetrics(SM_CXEDGE); - systemEdgeY = GetSystemMetrics(SM_CYEDGE); - - if (UpdateRegion == (HRGN)1) - 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)) - { - RECT windowRect; - RECT clientRect; - RECT tempRect; - LONG sizingBorderWidth; - LONG borderX; - LONG borderY; - - GetWindowRect(hWnd, &windowRect); - windowRect.right -= windowRect.left; - windowRect.bottom -= windowRect.top; - windowRect.left = 0; - windowRect.top = 0; - - clientRect.left = windowRect.left + systemEdgeX; - clientRect.top = windowRect.top + systemEdgeY; - clientRect.right = windowRect.right - systemEdgeX; - clientRect.bottom = windowRect.bottom - 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. - tempRect = windowRect; - tempRect.left = tempRect.right - 2; - FillRect(hdc, &tempRect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); - tempRect.left = tempRect.right - 1; - FillRect(hdc, &tempRect, GetSysColorBrush(COLOR_3DSHADOW)); - - // 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(ThemeData, 0, 0, TMT_SIZINGBORDERWIDTH, &sizingBorderWidth))) - { - borderX = sizingBorderWidth; - borderY = sizingBorderWidth; - } - else - { - borderX = GetSystemMetrics(SM_CXBORDER); - borderY = GetSystemMetrics(SM_CYBORDER); - } - - if (borderX < systemEdgeX || borderY < systemEdgeY) - { - windowRect.left += systemEdgeX - borderX; - windowRect.top += systemEdgeY - borderY; - windowRect.right -= systemEdgeX - borderX; - windowRect.bottom -= systemEdgeY - borderY; - - FillRect(hdc, &windowRect, GetSysColorBrush(COLOR_3DFACE)); - } - - ReleaseDC(hWnd, hdc); - } -} - -LRESULT CALLBACK PhpSelectionTreeSubclassProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - HTHEME treeThemeData = (HTHEME)GetWindowLongPtr(hWnd, GWLP_USERDATA); - - switch (uMsg) - { - case WM_DESTROY: - { - RemoveWindowSubclass(hWnd, PhpSelectionTreeSubclassProc, uIdSubclass); - - CloseThemeData(treeThemeData); - SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)NULL); - } - break; - case WM_SYSCOLORCHANGE: - case WM_SETFOCUS: - case WM_KILLFOCUS: - case WM_ENABLE: - { - if (treeThemeData) - { - PhpSelectionTreeDrawThemedFrame(hWnd, NULL, treeThemeData); - } - } - break; - case WM_THEMECHANGED: - { - if (treeThemeData) - { - CloseThemeData(treeThemeData); - treeThemeData = NULL; - } - - treeThemeData = OpenThemeData(hWnd, VSCLASS_TREEVIEW); - SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)treeThemeData); - InvalidateRect(hWnd, NULL, TRUE); - } - break; - case WM_NCPAINT: - { - if (treeThemeData) - { - PhpSelectionTreeDrawThemedFrame(hWnd, (HRGN)wParam, treeThemeData); - return TRUE; - } - } - break; - } - - return DefSubclassProc(hWnd, uMsg, wParam, lParam); -} - From 5bd68c4357f311b4c70ffda680938f35315ac42b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:03:49 +1100 Subject: [PATCH 488/839] Remove GetTickCount --- ProcessHacker/include/phapp.h | 2 +- ProcessHacker/include/phuisup.h | 8 ++++---- ProcessHacker/include/proctree.h | 2 +- ProcessHacker/itemtips.c | 4 ++-- ProcessHacker/mdump.c | 8 ++++---- ProcessHacker/miniinfo.c | 3 --- ProcessHacker/proctree.c | 6 +++--- phlib/hndlinfo.c | 6 +++--- phlib/include/phsup.h | 6 +++--- phlib/util.c | 12 ++++++------ 10 files changed, 27 insertions(+), 30 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index aaf3ed2325b6..a23878fd7ae5 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -262,7 +262,7 @@ VOID PhShowDebugConsole( PPH_STRING PhGetProcessTooltipText( _In_ PPH_PROCESS_ITEM Process, - _Out_opt_ PULONG ValidToTickCount + _Out_opt_ PULONG64 ValidToTickCount ); PPH_STRING PhGetServiceTooltipText( diff --git a/ProcessHacker/include/phuisup.h b/ProcessHacker/include/phuisup.h index b7160daaf5b3..23f439a4cff8 100644 --- a/ProcessHacker/include/phuisup.h +++ b/ProcessHacker/include/phuisup.h @@ -8,7 +8,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 +27,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,7 +41,7 @@ FORCEINLINE VOID PhChangeShStateTn( do { \ NodeType *node; \ ULONG enumerationKey = 0; \ - ULONG tickCount; \ + ULONG64 tickCount; \ BOOLEAN preferFullInvalidate; \ HANDLE stateListHandle; \ BOOLEAN redrawDisabled = FALSE; \ @@ -50,7 +50,7 @@ FORCEINLINE VOID PhChangeShStateTn( if (!StateList || StateList->Count == 0) \ break; \ \ - tickCount = GetTickCount(); \ + tickCount = NtGetTickCount64(); \ preferFullInvalidate = StateList->Count > 8; \ \ while (PhEnumPointerList(StateList, &enumerationKey, &node)) \ diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index 4c9ab88a7ab2..3535bfbd605e 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -169,7 +169,7 @@ typedef struct _PH_PROCESS_NODE LARGE_INTEGER FileEndOfFile; PPH_STRING TooltipText; - ULONG TooltipTextValidToTickCount; + ULONG64 TooltipTextValidToTickCount; // Text buffers WCHAR CpuUsageText[PH_INT32_STR_LEN_1]; diff --git a/ProcessHacker/itemtips.c b/ProcessHacker/itemtips.c index 318592248aaf..b022e2dbb036 100644 --- a/ProcessHacker/itemtips.c +++ b/ProcessHacker/itemtips.c @@ -95,7 +95,7 @@ static int __cdecl ServiceForTooltipCompare( PPH_STRING PhGetProcessTooltipText( _In_ PPH_PROCESS_ITEM Process, - _Out_opt_ PULONG ValidToTickCount + _Out_opt_ PULONG64 ValidToTickCount ) { PH_STRING_BUILDER stringBuilder; @@ -422,7 +422,7 @@ PPH_STRING PhGetProcessTooltipText( } if (ValidToTickCount) - *ValidToTickCount = GetTickCount() + validForMs; + *ValidToTickCount = NtGetTickCount64() + validForMs; // Remove the trailing newline. if (stringBuilder.String->Length != 0) diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index b31dc20ae7b3..e4bf2a239f32 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -51,7 +51,7 @@ typedef struct _PROCESS_MINIDUMP_CONTEXT BOOLEAN Stop; BOOLEAN Succeeded; - ULONG LastTickCount; + ULONG64 LastTickCount; } PROCESS_MINIDUMP_CONTEXT, *PPROCESS_MINIDUMP_CONTEXT; BOOLEAN PhpCreateProcessMiniDumpWithProgress( @@ -410,9 +410,9 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( { PPROCESS_MINIDUMP_CONTEXT context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - ULONG currentTickCount; + ULONG64 currentTickCount; - currentTickCount = GetTickCount(); + currentTickCount = NtGetTickCount64(); if (currentTickCount - context->LastTickCount >= 2000) { @@ -438,7 +438,7 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( case PH_MINIDUMP_STATUS_UPDATE: SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, (PWSTR)lParam); InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE); - context->LastTickCount = GetTickCount(); + context->LastTickCount = NtGetTickCount64(); break; case PH_MINIDUMP_ERROR: PhShowStatus(hwndDlg, L"Unable to create the minidump", 0, (ULONG)lParam); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index e1d3489d7296..ccef3ad0b9c1 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1634,9 +1634,6 @@ BOOLEAN PhMipListSectionTreeNewCallback( { 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. diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 22bf759f5d1c..3d1ba2b22158 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2737,16 +2737,16 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( case TreeNewGetCellTooltip: { PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - ULONG tickCount; + ULONG64 tickCount; node = (PPH_PROCESS_NODE)getCellTooltip->Node; if (getCellTooltip->Column->Id != 0) return FALSE; - tickCount = GetTickCount(); + tickCount = NtGetTickCount64(); - if ((LONG)(node->TooltipTextValidToTickCount - tickCount) < 0) + if ((LONG64)(node->TooltipTextValidToTickCount - tickCount) < 0) PhClearReference(&node->TooltipText); if (!node->TooltipText) node->TooltipText = PhGetProcessTooltipText(node->ProcessItem, &node->TooltipTextValidToTickCount); diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 58c22e8b8bbf..877b530f9fd5 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -480,15 +480,15 @@ _Callback_ PPH_STRING PhStdGetClientIdName( { static PH_QUEUED_LOCK cachedProcessesLock = PH_QUEUED_LOCK_INIT; static PVOID processes = NULL; - static ULONG lastProcessesTickCount = 0; + static ULONG64 lastProcessesTickCount = 0; PPH_STRING name; - ULONG tickCount; + ULONG64 tickCount; PSYSTEM_PROCESS_INFORMATION processInfo; // Get a new process list only if 2 seconds have passed since the last update. - tickCount = GetTickCount(); + tickCount = NtGetTickCount64(); if (tickCount - lastProcessesTickCount >= 2000) { diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index f669fb940271..006955251e85 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -458,9 +458,9 @@ FORCEINLINE ULONG PhCountBits( return count; } -FORCEINLINE ULONG PhRoundNumber( - _In_ ULONG Value, - _In_ ULONG Granularity +FORCEINLINE ULONG64 PhRoundNumber( + _In_ ULONG64 Value, + _In_ ULONG64 Granularity ) { return (Value + Granularity / 2) / Granularity * Granularity; diff --git a/phlib/util.c b/phlib/util.c index 5a4089afec2d..67fd7a565518 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2296,11 +2296,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) @@ -2338,10 +2338,10 @@ NTSTATUS PhWaitForMultipleObjectsAndPump( if (Timeout != INFINITE) { - currentTickCount = GetTickCount(); + currentTickCount = NtGetTickCount64(); currentTimeout = Timeout - (currentTickCount - startTickCount); - if (currentTimeout < 0) + if ((LONG64)currentTimeout < 0) return STATUS_TIMEOUT; } } From f3a58abf51342e164bc7987d552c4d71f1b8b4cd Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:04:29 +1100 Subject: [PATCH 489/839] Fix PhCreateNamedPipe error on Win7 --- phlib/native.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 4b99fe5466c5..7f3c630d2d9a 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6562,7 +6562,7 @@ NTSTATUS PhCreateNamedPipe( ) { NTSTATUS status; - PACL pipeAcl; + PACL pipeAcl = NULL; HANDLE pipeHandle; PPH_STRING pipeName; LARGE_INTEGER pipeTimeout; @@ -6588,7 +6588,6 @@ NTSTATUS PhCreateNamedPipe( RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); - RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); oa.SecurityDescriptor = &securityDescriptor; } @@ -6615,6 +6614,9 @@ NTSTATUS PhCreateNamedPipe( *PipeHandle = pipeHandle; } + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + PhDereferenceObject(pipeName); return status; } From fe71733eb00b151bcac9435cb5b25277475c6bb7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:05:53 +1100 Subject: [PATCH 490/839] Update headers --- phnt/include/ntexapi.h | 2 +- phnt/include/ntldr.h | 32 ++++++++++++++++++++------------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 682d10db59f7..01452aa249b8 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -3457,7 +3457,7 @@ 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) == 0x704); // Visual Studio has a problem with this #define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)0x7ffe0000) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index f89b5f2360b0..84e8428ad7a8 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -552,6 +552,26 @@ LdrDisableThreadCalloutsForDll( _In_ PVOID DllImageBase ); +// Resources + +NTSYSAPI +NTSTATUS +NTAPI +LdrAccessResource( + _In_ PVOID BaseAddress, + _In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, + _Out_opt_ PVOID *ResourceBuffer, + _Out_opt_ ULONG *ResourceLength + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrFindEntryForAddress( + _In_ PVOID BaseAddress, + _Out_ PLDR_DATA_TABLE_ENTRY *Entry + ); + #endif // (PHNT_MODE != PHNT_MODE_KERNEL) // Module information @@ -586,16 +606,4 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX PVOID DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; -// Resources - -NTSYSAPI -NTSTATUS -NTAPI -LdrAccessResource( - _In_ PVOID BaseAddress, - _In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, - _Out_opt_ PVOID *ResourceBuffer, - _Out_opt_ ULONG *ResourceLength - ); - #endif From d83e887285e9a32433c4b82cadbc69e1325dc186 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:06:30 +1100 Subject: [PATCH 491/839] Remove log window parent --- ProcessHacker/logwnd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 74d7e4fe7289..61f3df459a57 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -52,7 +52,7 @@ VOID PhShowLogDialog( PhLogWindowHandle = CreateDialog( PhInstanceHandle, MAKEINTRESOURCE(IDD_LOG), - PhMainWndHandle, + NULL, PhpLogDlgProc ); PhRegisterDialog(PhLogWindowHandle); From 567ce0b75319dd89d69069ae236f6a77dc7721bf Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:06:40 +1100 Subject: [PATCH 492/839] Remove unused cast --- ProcessHacker/hndlprv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index cd1dd54c34c9..75d548132531 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -447,7 +447,7 @@ VOID PhHandleProviderUpdate( // Make a list of the relevant handles. if (filterNeeded) { - for (i = 0; i < (ULONG)numberOfHandles; i++) + for (i = 0; i < numberOfHandles; i++) { PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; @@ -463,7 +463,7 @@ VOID PhHandleProviderUpdate( } else { - for (i = 0; i < (ULONG)numberOfHandles; i++) + for (i = 0; i < numberOfHandles; i++) { PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; From 7b07f3638d94b7e3cbd4acac7228cf5ddf740d9f Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:07:26 +1100 Subject: [PATCH 493/839] Improve PhCommandModeStart error checking --- ProcessHacker/cmdmode.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/cmdmode.c b/ProcessHacker/cmdmode.c index 481e03937358..6dcd32625fbe 100644 --- a/ProcessHacker/cmdmode.c +++ b/ProcessHacker/cmdmode.c @@ -64,19 +64,21 @@ NTSTATUS PhCommandModeStart( { { PH_COMMAND_OPTION_HWND, L"hwnd", MandatoryArgumentType } }; - NTSTATUS status = STATUS_SUCCESS; - PH_STRINGREF commandLine; + NTSTATUS status; + PPH_STRING commandLine; - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + if (!NT_SUCCESS(status = PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + return status; PhParseCommandLine( - &commandLine, + &commandLine->sr, options, sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, PhpCommandModeOptionCallback, NULL ); + PhDereferenceObject(commandLine); if (PhEqualString2(PhStartupParameters.CommandType, L"process", TRUE)) { From 786b6c55197293d99df75474c776b1146b78ddcb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:07:54 +1100 Subject: [PATCH 494/839] ToolStatus: Fix statusbar issue with timezones --- plugins/ToolStatus/statusbar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index fa5df12ed8c8..7e18a6c56622 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -258,14 +258,14 @@ VOID StatusBarUpdate( // Reset max. widths for Max. CPU Process and Max. I/O Process parts once in a while. { - LARGE_INTEGER tickCount; + ULONG64 tickCount; - PhQuerySystemTime(&tickCount); + tickCount = NtGetTickCount64(); - if (tickCount.QuadPart - lastTickCount >= 10 * PH_TICKS_PER_SEC) + if (tickCount - lastTickCount >= 10 * 1000) { resetMaxWidths = TRUE; - lastTickCount = tickCount.QuadPart; + lastTickCount = tickCount; } } From 72a8fc818d6c4d1e72e16a845d432830907a6b0f Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:09:06 +1100 Subject: [PATCH 495/839] CustomSetupTool: Clear download cache after updates --- tools/CustomSetupTool/CustomSetupTool/install.c | 2 ++ tools/CustomSetupTool/CustomSetupTool/setup.c | 5 ++++- tools/CustomSetupTool/CustomSetupTool/update.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/install.c b/tools/CustomSetupTool/CustomSetupTool/install.c index e54c6642ada3..18794635fbaf 100644 --- a/tools/CustomSetupTool/CustomSetupTool/install.c +++ b/tools/CustomSetupTool/CustomSetupTool/install.c @@ -72,6 +72,8 @@ NTSTATUS SetupProgressThread( if (Context->SetupInstallKphService) SetupStartKph(Context); + PhClearCacheDirectory(); + PostMessage(Context->ExtractPageHandle, WM_END_SETUP, 0, 0); return STATUS_SUCCESS; diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index 2050d7bddf58..d914e1d90444 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -197,12 +197,13 @@ BOOLEAN SetupCreateUninstallFile( PPH_STRING tempFileName; PPH_STRING tempFilePath; - tempFileName = PhCreateString(L"processhacker-setup.old"); + tempFileName = PhCreateString(L"processhacker-setup.bak"); tempFilePath = PhCreateCacheFile(tempFileName); if (!MoveFile(backupFilePath->Buffer, tempFilePath->Buffer)) { Context->ErrorCode = GetLastError(); + return FALSE; } PhDereferenceObject(tempFilePath); @@ -215,12 +216,14 @@ BOOLEAN SetupCreateUninstallFile( if (!MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer)) { Context->ErrorCode = GetLastError(); + return FALSE; } } if (!CopyFile(currentFilePath->Buffer, uninstallFilePath->Buffer, TRUE)) { Context->ErrorCode = GetLastError(); + return FALSE; } PhDereferenceObject(uninstallFilePath); diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 8df1ced0220b..43ea2e386524 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -54,6 +54,8 @@ NTSTATUS SetupUpdateBuild( if (!SetupExecuteProcessHacker(Context)) goto CleanupExit; + PhClearCacheDirectory(); + PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); PhDereferenceObject(Context); return STATUS_SUCCESS; From cd1c173df6d85c737a8a1768951fc188e1a7f664 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:09:21 +1100 Subject: [PATCH 496/839] Update DelayLoadedDLLs --- ProcessHacker/ProcessHacker.vcxproj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 1b6219b26070..99d582dc11ed 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -93,6 +93,7 @@ StdCall true true + true aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) @@ -102,7 +103,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;comdlg32.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -124,6 +125,7 @@ StdCall true true + true aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) @@ -133,7 +135,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;comdlg32.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -173,7 +175,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;comdlg32.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) @@ -211,7 +213,7 @@ 6.01 $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) ProcessHacker.def - aclui.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + aclui.dll;comdlg32.dll;iphlpapi.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) From 6f221ae34bbe7b8300766bb8a76f0f0294d87231 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 17:09:47 +1100 Subject: [PATCH 497/839] Update ProtectedSignerStrings for RS3 --- ProcessHacker/prpggen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index aa061e46e478..4d4ffca566eb 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -36,7 +36,7 @@ #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)" }; NTSTATUS PhpProcessGeneralOpenProcess( _Out_ PHANDLE Handle, From 7775c8af021a5eadf5acb909c51fdd7a51b71931 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 19:52:29 +1100 Subject: [PATCH 498/839] Update string search progress dialog --- ProcessHacker/memsrch.c | 193 ++++++++++++++++++++++++---------------- 1 file changed, 118 insertions(+), 75 deletions(-) diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index f558f0aa4fb8..1d38f573cf66 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -3,6 +3,7 @@ * memory searchers * * Copyright (C) 2010 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -38,11 +39,22 @@ typedef struct _MEMORY_STRING_CONTEXT HANDLE ProcessId; HANDLE ProcessHandle; ULONG MinimumLength; - BOOLEAN DetectUnicode; - BOOLEAN Private; - BOOLEAN Image; - BOOLEAN Mapped; + union + { + BOOLEAN Flags; + struct + { + BOOLEAN DetectUnicode : 1; + BOOLEAN Private : 1; + BOOLEAN Image : 1; + BOOLEAN Mapped : 1; + BOOLEAN EnableCloseDialog : 1; + BOOLEAN Spare : 3; + }; + }; + + HWND ParentWindowHandle; HWND WindowHandle; PH_MEMORY_STRING_OPTIONS Options; PPH_LIST Results; @@ -55,11 +67,8 @@ INT_PTR CALLBACK PhpMemoryStringDlgProc( _In_ LPARAM lParam ); -INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam +BOOLEAN PhpShowMemoryStringProgressDialog( + _In_ PMEMORY_STRING_CONTEXT Context ); PVOID PhMemorySearchHeap = NULL; @@ -170,9 +179,7 @@ VOID PhDereferenceMemoryResults( _In_ ULONG NumberOfResults ) { - ULONG i; - - for (i = 0; i < NumberOfResults; i++) + for (ULONG i = 0; i < NumberOfResults; i++) PhDereferenceMemoryResult(Results[i]); } @@ -477,7 +484,6 @@ VOID PhShowMemoryStringDialog( NTSTATUS status; HANDLE processHandle; MEMORY_STRING_CONTEXT context; - PPH_SHOW_MEMORY_RESULTS showMemoryResults; if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, @@ -490,6 +496,7 @@ VOID PhShowMemoryStringDialog( } memset(&context, 0, sizeof(MEMORY_STRING_CONTEXT)); + context.ParentWindowHandle = ParentWindowHandle; context.ProcessId = ProcessItem->ProcessId; context.ProcessHandle = processHandle; @@ -507,14 +514,10 @@ VOID PhShowMemoryStringDialog( context.Results = PhCreateList(1024); - if (DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PROGRESS), - ParentWindowHandle, - PhpMemoryStringProgressDlgProc, - (LPARAM)&context - ) == IDOK) + if (PhpShowMemoryStringProgressDialog(&context)) { + PPH_SHOW_MEMORY_RESULTS showMemoryResults; + showMemoryResults = PhAllocate(sizeof(PH_SHOW_MEMORY_RESULTS)); showMemoryResults->ProcessId = ProcessItem->ProcessId; showMemoryResults->Results = context.Results; @@ -633,88 +636,128 @@ NTSTATUS PhpMemoryStringThreadStart( return STATUS_SUCCESS; } -INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( +LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam + _In_ LPARAM lParam, + _In_ UINT_PTR uIdSubclass, + _In_ ULONG_PTR dwRefData ) { + PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)dwRefData; + switch (uMsg) { - case WM_INITDIALOG: + case WM_NCDESTROY: + RemoveWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, uIdSubclass); + break; + case WM_PH_MEMORY_STATUS_UPDATE: { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)lParam; + switch (wParam) + { + case PH_SEARCH_COMPLETED: + { + context->EnableCloseDialog = TRUE; + SendMessage(hwndDlg, TDM_CLICK_BUTTON, IDOK, 0); + } + break; + } + } + break; + } - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); +} - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, L"Searching..."); +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; - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); + switch (uMsg) + { + case TDN_CREATED: + { + HICON iconSmall; + HICON iconLarge; context->WindowHandle = hwndDlg; - PhCreateThread2(PhpMemoryStringThreadStart, context); + // 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); - SetTimer(hwndDlg, 1, 500, NULL); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - { - PMEMORY_STRING_CONTEXT context = - (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + // Set the progress state. + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); - context->Options.Header.Cancel = TRUE; - } - break; - } + // Subclass the Taskdialog. + SetWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, 0, (ULONG_PTR)context); + + // Create the search thread. + PhCreateThread2(PhpMemoryStringThreadStart, context); } break; - case WM_TIMER: + case TDN_BUTTON_CLICKED: { - 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); - } + if ((INT)wParam == IDCANCEL) + context->Options.Header.Cancel = TRUE; + + if (!context->EnableCloseDialog) + return S_FALSE; } break; - case WM_PH_MEMORY_STATUS_UPDATE: + case TDN_TIMER: { - PMEMORY_STRING_CONTEXT context; + PPH_STRING numberText; + PPH_STRING progressText; - context = (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + numberText = PhFormatUInt64(context->Results->Count, TRUE); + progressText = PhFormatString(L"%s strings found...", numberText->Buffer); - switch (wParam) - { - case PH_SEARCH_COMPLETED: - EndDialog(hwndDlg, IDOK); - break; - } + 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; } From 55060a57e92fc354f9ee77897fcd94f90c98ae27 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 14 Oct 2017 20:10:52 +1100 Subject: [PATCH 499/839] Improve handle menu error checking --- ProcessHacker/hndlmenu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/hndlmenu.c b/ProcessHacker/hndlmenu.c index 802e41c26ff1..8b86873713e0 100644 --- a/ProcessHacker/hndlmenu.c +++ b/ProcessHacker/hndlmenu.c @@ -206,17 +206,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, @@ -227,7 +228,6 @@ VOID PhShowHandleObjectProperties1( if (handle) { - NTSTATUS status; PPH_STRING sectionName = NULL; SECTION_BASIC_INFORMATION basicInfo; SIZE_T viewSize = PH_MAX_SECTION_EDIT_SIZE; @@ -236,7 +236,7 @@ VOID PhShowHandleObjectProperties1( PhGetHandleInformation(NtCurrentProcess(), handle, -1, 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; @@ -291,7 +291,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); } } @@ -299,6 +299,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)) { From 9192d42b87ac39553aecc988fa67a2064942e76f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:06:13 +1100 Subject: [PATCH 500/839] Fix json wrapper memory leak --- phlib/include/json.h | 4 ++-- phlib/json.c | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/phlib/include/json.h b/phlib/include/json.h index a8d43b25acba..74ed35752f23 100644 --- a/phlib/include/json.h +++ b/phlib/include/json.h @@ -48,7 +48,7 @@ PhFreeJsonParser( ); PHLIBAPI -PSTR +PPH_STRING NTAPI PhGetJsonValueAsString( _In_ PVOID Object, @@ -118,7 +118,7 @@ PhAddJsonArrayObject( ); PHLIBAPI -PSTR +PPH_STRING NTAPI PhGetJsonArrayString( _In_ PVOID Object diff --git a/phlib/json.c b/phlib/json.c index fa889476750e..0eba2c3682a5 100644 --- a/phlib/json.c +++ b/phlib/json.c @@ -56,12 +56,17 @@ VOID PhFreeJsonParser( json_object_put(Object); } -PSTR PhGetJsonValueAsString( +PPH_STRING PhGetJsonValueAsString( _In_ PVOID Object, _In_ PSTR Key ) { - return json_object_get_string(json_get_object(Object, Key)); + PSTR value; + + if (value = json_object_get_string(json_get_object(Object, Key))) + return PhConvertUtf8ToUtf16(value); + else + return NULL; } INT64 PhGetJsonValueAsLong64( @@ -126,11 +131,16 @@ VOID PhAddJsonArrayObject( json_object_array_add(Object, jsonEntry); } -PSTR PhGetJsonArrayString( +PPH_STRING PhGetJsonArrayString( _In_ PVOID Object ) { - return _strdup( json_object_to_json_string(Object) ); // leak + PSTR value; + + if (value = json_object_to_json_string(Object)) + return PhConvertUtf8ToUtf16(value); + else + return NULL; } INT64 PhGetJsonArrayLong64( From c82312a7677d526aa4a39fe08c686b55ffe3bae0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:07:40 +1100 Subject: [PATCH 501/839] BuildTools: Update appveyor format --- tools/CustomBuildTool/Source Files/Build.cs | 36 +++++++++++++++------ tools/CustomBuildTool/Source Files/Utils.cs | 21 ++++++++---- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 73d01c1bc7b4..2c0a63c2d5aa 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -53,6 +53,7 @@ public static class Build private static long BuildBinFileLength; private static string BuildBinHash; + private static string BuildBinSig; private static long BuildSetupFileLength; private static string BuildSetupHash; @@ -259,7 +260,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildBranch, ConsoleColor.White); Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildCommit, ConsoleColor.White); + Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.White); string currentGitTag = Win32.ShellExecute(GitExePath, "describe --abbrev=0 --tags --always").Trim(); BuildRevision = Win32.ShellExecute(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\"").Trim(); @@ -920,12 +921,21 @@ public static bool BuildUpdateSignature() 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" @@ -949,24 +959,27 @@ public static void WebServiceUpdateConfig() return; if (string.IsNullOrEmpty(BuildBinHash)) return; + if (string.IsNullOrEmpty(BuildBinSig)) + return; string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\""); string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\" --abbrev-commit"); string buildPostString = Json.Serialize(new BuildUpdateRequest { + Version = BuildVersion, + Commit = BuildCommit, Updated = TimeStart.ToString("o"), - FileLength = BuildSetupFileLength.ToString(), - ForumUrl = "https://wj32.org/processhacker/nightly.php", + + BinUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-bin.zip", + BinLength = BuildBinFileLength.ToString(), + BinHash = BuildBinHash, + BinSig = BuildBinSig, SetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-setup.exe", - SetupVersion = BuildVersion, + SetupLength = BuildSetupFileLength.ToString(), SetupHash = BuildSetupHash, SetupSig = BuildSetupSig, - BinUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-" + BuildVersion + "-bin.zip", - BinHash = BuildBinHash, - //BinSig = BuildBinSig, - //WebSetupUrl = "https://ci.appveyor.com/api/projects/processhacker/processhacker2/artifacts/processhacker-websetup.exe", //WebSetupHash = BuildWebSetupHash, //WebSetupVersion = BuildWebSetupVersion, @@ -974,6 +987,11 @@ public static void WebServiceUpdateConfig() Message = buildSummary, Changelog = buildChangelog, + + FileLengthDeprecated = BuildSetupFileLength.ToString(), // TODO: Remove after most users have updated. + ForumUrlDeprecated = "https://wj32.org/processhacker/", // TODO: Remove after most users have updated. + SetupSigDeprecated = BuildSetupSig, // TODO: Remove after most users have updated. + BinHashDeprecated = BuildBinHash // TODO: Remove after most users have updated. }); if (string.IsNullOrEmpty(buildPostString)) @@ -1087,7 +1105,7 @@ public static bool AppveyorUploadBuildFiles() } // Update Appveyor build version string. - Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); + Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + "\" "); return true; } diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 7fd432512415..088112be4cac 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -338,26 +338,33 @@ public VisualStudioInstance(ISetupInstance2 FromInstance) [DataContract] public class BuildUpdateRequest { + [DataMember(Name = "version")] public string Version { get; set; } + [DataMember(Name = "commit")] public string Commit { get; set; } [DataMember(Name = "updated")] public string Updated { get; set; } - [DataMember(Name = "size")] public string FileLength { get; set; } - [DataMember(Name = "forum_url")] public string ForumUrl { get; set; } [DataMember(Name = "bin_url")] public string BinUrl { get; set; } - [DataMember(Name = "hash_bin")] public string BinHash { 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 = "hash_setup")] public string SetupHash { get; set; } - [DataMember(Name = "sig")] public string SetupSig { get; set; } - [DataMember(Name = "version")] public string SetupVersion { 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 = "websetup_version")] public string WebSetupVersion { get; set; } [DataMember(Name = "message")] public string Message { get; set; } [DataMember(Name = "changelog")] public string Changelog { get; set; } + + [DataMember(Name = "size")] public string FileLengthDeprecated { get; set; } // TODO: Remove after most users have updated. + [DataMember(Name = "forum_url")] public string ForumUrlDeprecated { get; set; } // TODO: Remove after most users have updated. + [DataMember(Name = "hash_bin")] public string BinHashDeprecated { get; set; } // TODO: Remove after most users have updated. + [DataMember(Name = "sig")] public string SetupSigDeprecated { get; set; } // TODO: Remove after most users have updated. } [Flags] From 4920387ace1c980e33879353b170b60a241453ad Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:10:39 +1100 Subject: [PATCH 502/839] Plugins: Update to latest json format --- plugins/ExtraPlugins/cloud.c | 40 +++++++++++----------- plugins/OnlineChecks/upload.c | 26 +++++++------- plugins/OnlineChecks/virustotal.c | 27 ++++++++------- plugins/Updater/page3.c | 2 +- plugins/Updater/updater.c | 57 +++++++++++-------------------- plugins/Updater/updater.h | 13 ++++--- 6 files changed, 73 insertions(+), 92 deletions(-) diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c index 07a94476fcc9..7a7d72bfec24 100644 --- a/plugins/ExtraPlugins/cloud.c +++ b/plugins/ExtraPlugins/cloud.c @@ -125,26 +125,26 @@ NTSTATUS QueryPluginsCallbackThread( memset(entry, 0, sizeof(PLUGIN_NODE)); jvalue = PhGetJsonArrayIndexObject(rootJsonObject, i); - entry->Id = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_id")); - entry->InternalName = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_internal_name")); - entry->Name = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_name")); - entry->Version = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_version")); - entry->Author = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_author")); - entry->Description = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_description")); - entry->IconUrl = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_icon")); - entry->Requirements = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_requirements")); - entry->FeedbackUrl = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_feedback")); - entry->Screenshots = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_screenshots")); - entry->AddedTime = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_datetime_added")); - entry->UpdatedTime = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_datetime_updated")); - entry->Download_count = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_count")); - entry->Download_link_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_link_32")); - entry->Download_link_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_download_link_64")); - entry->SHA2_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_hash_32")); - entry->SHA2_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_hash_64")); - entry->HASH_32 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_signed_32")); - entry->HASH_64 = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_signed_64")); - entry->FileName = PhConvertUtf8ToUtf16(PhGetJsonValueAsString(jvalue, "plugin_filename")); + entry->Id = PhGetJsonValueAsString(jvalue, "plugin_id"); + entry->InternalName = PhGetJsonValueAsString(jvalue, "plugin_internal_name"); + entry->Name = PhGetJsonValueAsString(jvalue, "plugin_name"); + entry->Version = PhGetJsonValueAsString(jvalue, "plugin_version"); + entry->Author = PhGetJsonValueAsString(jvalue, "plugin_author"); + entry->Description = PhGetJsonValueAsString(jvalue, "plugin_description"); + entry->IconUrl = PhGetJsonValueAsString(jvalue, "plugin_icon"); + entry->Requirements = PhGetJsonValueAsString(jvalue, "plugin_requirements"); + entry->FeedbackUrl = PhGetJsonValueAsString(jvalue, "plugin_feedback"); + entry->Screenshots = PhGetJsonValueAsString(jvalue, "plugin_screenshots"); + entry->AddedTime = PhGetJsonValueAsString(jvalue, "plugin_datetime_added"); + entry->UpdatedTime = PhGetJsonValueAsString(jvalue, "plugin_datetime_updated"); + entry->Download_count = PhGetJsonValueAsString(jvalue, "plugin_download_count"); + entry->Download_link_32 = PhGetJsonValueAsString(jvalue, "plugin_download_link_32"); + entry->Download_link_64 = PhGetJsonValueAsString(jvalue, "plugin_download_link_64"); + entry->SHA2_32 = PhGetJsonValueAsString(jvalue, "plugin_hash_32"); + entry->SHA2_64 = PhGetJsonValueAsString(jvalue, "plugin_hash_64"); + entry->HASH_32 = PhGetJsonValueAsString(jvalue, "plugin_signed_32"); + entry->HASH_64 = PhGetJsonValueAsString(jvalue, "plugin_signed_64"); + entry->FileName = PhGetJsonValueAsString(jvalue, "plugin_filename"); swscanf( PhGetString(entry->UpdatedTime), diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 8646b1d98eae..77a6f619f96c 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -800,7 +800,6 @@ NTSTATUS UploadFileThreadStart( case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: { PSTR buffer = NULL; - PSTR redirectUrl; ULONG bufferLength; PVOID jsonRootObject; @@ -820,10 +819,7 @@ NTSTATUS UploadFileThreadStart( // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha256")); // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "verbose_msg")); - if (redirectUrl = PhGetJsonValueAsString(jsonRootObject, "permalink")) - { - PhMoveReference(&context->LaunchCommand, PhZeroExtendToUtf16(redirectUrl)); - } + PhMoveReference(&context->LaunchCommand, PhGetJsonValueAsString(jsonRootObject, "permalink")); PhFreeJsonParser(jsonRootObject); } @@ -849,7 +845,6 @@ NTSTATUS UploadFileThreadStart( case MENUITEM_JOTTI_UPLOAD_SERVICE: { PSTR buffer = NULL; - PSTR redirectUrl; ULONG bufferLength; PVOID rootJsonObject; @@ -861,9 +856,12 @@ NTSTATUS UploadFileThreadStart( if (rootJsonObject = PhCreateJsonParser(buffer)) { + PPH_STRING redirectUrl; + if (redirectUrl = PhGetJsonValueAsString(rootJsonObject, "redirecturl")) { - PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%hs", redirectUrl)); + PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%s", redirectUrl->Buffer)); + PhDereferenceObject(redirectUrl); } PhFreeJsonParser(rootJsonObject); @@ -1066,12 +1064,12 @@ NTSTATUS UploadCheckThreadStart( context->Detected = PhFormatString(L"%I64d", detected); context->MaxDetected = PhFormatString(L"%I64d", detectedMax); - context->UploadUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "upload_url")); - context->ReAnalyseUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "reanalyse_url")); - context->LastAnalysisUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_url")); - context->FirstAnalysisDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "first_analysis_date")); - context->LastAnalysisDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_date")); - context->LastAnalysisAgo = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "last_analysis_ago")); + 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)); @@ -1108,7 +1106,7 @@ NTSTATUS UploadCheckThreadStart( } else { - context->UploadUrl = PhZeroExtendToUtf16(PhGetJsonValueAsString(rootJsonObject, "upload_url")); + context->UploadUrl = PhGetJsonValueAsString(rootJsonObject, "upload_url"); // No file found... Start the upload. if (!PhIsNullOrEmptyString(context->UploadUrl)) diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 0a93f6e81a92..3d2f2d5e80fb 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -220,7 +220,6 @@ PPH_LIST VirusTotalJsonToResultList( { PVIRUSTOTAL_API_RESULT result; PVOID jsonArrayObject; - PSTR fileHash; if (!(jsonArrayObject = PhGetJsonArrayIndexObject(JsonObject, i))) continue; @@ -228,8 +227,7 @@ PPH_LIST VirusTotalJsonToResultList( result = PhAllocate(sizeof(VIRUSTOTAL_API_RESULT)); memset(result, 0, sizeof(VIRUSTOTAL_API_RESULT)); - fileHash = PhGetJsonValueAsString(jsonArrayObject, "hash"); - result->FileHash = fileHash ? PhZeroExtendToUtf16(fileHash) : NULL; + result->FileHash = PhGetJsonValueAsString(jsonArrayObject, "hash"); result->Found = PhGetJsonObjectBool(jsonArrayObject, "found") == TRUE; result->Positives = PhGetJsonValueAsLong64(jsonArrayObject, "positives"); result->Total = PhGetJsonValueAsLong64(jsonArrayObject, "total"); @@ -596,14 +594,14 @@ PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( result->Total = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "total"), FALSE); result->Positives = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "positives"), FALSE); - result->Resource = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "resource")); - result->ScanId = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_id")); - result->Md5 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "md5")); - result->Sha1 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha1")); - result->Sha256 = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha256")); - result->ScanDate = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_date")); - result->Permalink = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "permalink")); - result->StatusMessage = PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "verbose_msg")); + result->Resource = PhGetJsonValueAsString(jsonRootObject, "resource"); + result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); + result->Md5 = PhGetJsonValueAsString(jsonRootObject, "md5"); + result->Sha1 = PhGetJsonValueAsString(jsonRootObject, "sha1"); + result->Sha256 = PhGetJsonValueAsString(jsonRootObject, "sha256"); + result->ScanDate = PhGetJsonValueAsString(jsonRootObject, "scan_date"); + result->Permalink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); //if (jsonScanObject = PhGetJsonObject(jsonRootObject, "scans")) //{ @@ -651,11 +649,11 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( { ULONG i; INT64 resultLength; - PSTR jsonArrayToSendString; PSTR jsonApiResult = NULL; PVOID jsonArray; PVOID rootJsonObject = NULL; PVOID dataJsonObject; + PPH_STRING jsonArrayToSendString = NULL; PPH_LIST resultTempList = NULL; PPH_LIST virusTotalResults = NULL; @@ -695,7 +693,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (!(jsonArrayToSendString = PhGetJsonArrayString(jsonArray))) goto CleanupExit; - if (!(jsonApiResult = VirusTotalSendHttpRequest(PhCreateBytes(jsonArrayToSendString)))) + if (!(jsonApiResult = VirusTotalSendHttpRequest(PhConvertUtf16ToUtf8(jsonArrayToSendString->Buffer)))) goto CleanupExit; if (!(rootJsonObject = PhCreateJsonParser(jsonApiResult))) @@ -761,6 +759,9 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( PhFreeJsonParser(rootJsonObject); } + if (jsonArrayToSendString) + PhDereferenceObject(jsonArrayToSendString); + if (jsonArray) { PhFreeJsonParser(jsonArray); diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index 40cda4253452..ced2fcffb38a 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -90,7 +90,7 @@ VOID ShowAvailableDialog( 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->Size) + PhGetStringOrEmpty(Context->SetupFileLength) )->Buffer; SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index abb34f2a49e7..ea940d5b0030 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -47,18 +47,16 @@ VOID FreeUpdateContext( _In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context ) { + PhClearReference(&Context->CurrentVersionString); + 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); + PhClearReference(&Context->SetupFileLength); + PhClearReference(&Context->SetupFileHash); + PhClearReference(&Context->SetupFileSignature); PhClearReference(&Context->BuildMessage); - PhClearReference(&Context->CurrentVersionString); - + PhDereferenceObject(Context); } @@ -77,7 +75,7 @@ VOID TaskDialogLinkClicked( _In_ PPH_UPDATER_CONTEXT Context ) { - if (!PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) + if (!PhIsNullOrEmptyString(Context->BuildMessage)) { DialogBoxParam( PluginInstance->DllBase, @@ -420,42 +418,27 @@ BOOLEAN QueryUpdateData( if (!(jsonObject = PhCreateJsonParser(stringBuffer))) goto CleanupExit; - Context->Size = PhFormatSize(PhGetJsonValueAsLong64(jsonObject, "size"), 2); - if (tempValue = PhGetJsonValueAsString(jsonObject, "version")) - { - Context->Version = PhConvertUtf8ToUtf16(tempValue); - Context->RevVersion = PhConvertUtf8ToUtf16(tempValue); - } - if (tempValue = PhGetJsonValueAsString(jsonObject, "updated")) - Context->RelDate = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = PhGetJsonValueAsString(jsonObject, "hash_setup")) - Context->Hash = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = PhGetJsonValueAsString(jsonObject, "sig")) - Context->Signature = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = PhGetJsonValueAsString(jsonObject, "forum_url")) - Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = PhGetJsonValueAsString(jsonObject, "setup_url")) - Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(tempValue); - if (tempValue = PhGetJsonValueAsString(jsonObject, "changelog")) - Context->BuildMessage = PhConvertUtf8ToUtf16(tempValue); + 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"); PhFreeJsonParser(jsonObject); 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)) + if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) + if (PhIsNullOrEmptyString(Context->SetupFileLength)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) + if (PhIsNullOrEmptyString(Context->SetupFileHash)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Signature)) + if (PhIsNullOrEmptyString(Context->SetupFileSignature)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->BuildMessage)) goto CleanupExit; @@ -911,12 +894,12 @@ NTSTATUS UpdateDownloadThread( } } - if (UpdaterVerifyHash(hashContext, context->Hash)) + if (UpdaterVerifyHash(hashContext, context->SetupFileHash)) { hashSuccess = TRUE; } - if (UpdaterVerifySignature(hashContext, context->Signature)) + if (UpdaterVerifySignature(hashContext, context->SetupFileSignature)) { signatureSuccess = TRUE; } diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 604bf3280b79..b207301f2ab2 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -84,17 +84,16 @@ typedef struct _PH_UPDATER_CONTEXT HWND DialogHandle; ULONG ErrorCode; + PPH_STRING SetupFilePath; PPH_STRING CurrentVersionString; 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; + PPH_STRING SetupFileLength; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFileHash; + PPH_STRING SetupFileSignature; + // Nightly builds only PPH_STRING BuildMessage; } PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; From 439388b1f352718346497fffa48beb3346e865a5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:11:15 +1100 Subject: [PATCH 503/839] SetupTool: Update to latest json format --- .../CustomSetupTool/download.c | 57 ++++++++----------- .../CustomSetupTool/include/setup.h | 13 +++-- .../CustomSetupTool/CustomSetupTool/install.c | 2 +- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 830e8f5939a0..51f76385dc58 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -267,50 +267,39 @@ BOOLEAN SetupQueryUpdateData( if (!(jsonObject = PhCreateJsonParser(stringBuffer))) goto CleanupExit; - if (value = PhGetJsonValueAsString(jsonObject, "updated")) - Context->RelDate = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "size")) - Context->Size = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "forum_url")) - Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(value); - - if (value = PhGetJsonValueAsString(jsonObject, "bin_url")) - Context->BinFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "hash_bin")) - Context->BinFileHash = PhConvertUtf8ToUtf16(value); - - if (value = PhGetJsonValueAsString(jsonObject, "setup_url")) - Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "sig")) - Context->SetupFileSignature = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "version")) - Context->SetupFileVersion = PhConvertUtf8ToUtf16(value); - - if (value = PhGetJsonValueAsString(jsonObject, "websetup_url")) - Context->WebSetupFileDownloadUrl = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "websetup_sig")) - Context->WebSetupFileSignature = PhConvertUtf8ToUtf16(value); - if (value = PhGetJsonValueAsString(jsonObject, "websetup_version")) - Context->WebSetupFileVersion = PhConvertUtf8ToUtf16(value); - - if (PhIsNullOrEmptyString(Context->RelDate)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Size)) + 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->ReleaseNotesUrl)) + if (PhIsNullOrEmptyString(Context->RelDate)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->BinFileDownloadUrl)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->BinFileHash)) + if (PhIsNullOrEmptyString(Context->BinFileSignature)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) goto CleanupExit; if (PhIsNullOrEmptyString(Context->SetupFileSignature)) goto CleanupExit; - if (PhIsNullOrEmptyString(Context->SetupFileVersion)) - goto CleanupExit; //if (PhIsNullOrEmptyString(Context->WebSetupFileDownloadUrl)) // goto CleanupExit; @@ -427,7 +416,7 @@ BOOLEAN UpdateDownloadUpdateData( SetWindowText(Context->MainHeaderHandle, PhFormatString( L"Downloading Process Hacker %s...", - PhGetString(Context->SetupFileVersion) + PhGetString(Context->RelVersion) )->Buffer); //SetWindowText(Context->SubHeaderHandle, L"Progress: ~ of ~ (0.0%)"); diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index eef82542cdf9..204e4eaff6cf 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -105,20 +105,25 @@ typedef struct _PH_SETUP_CONTEXT ULONG ErrorCode; PPH_STRING FilePath; + PPH_STRING RelDate; - PPH_STRING Size; - PPH_STRING ReleaseNotesUrl; + 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 SetupFileVersion; PPH_STRING WebSetupFileDownloadUrl; - PPH_STRING WebSetupFileSignature; PPH_STRING WebSetupFileVersion; + PPH_STRING WebSetupFileLength; + PPH_STRING WebSetupFileHash; + PPH_STRING WebSetupFileSignature; HWND MainHeaderHandle; HWND StatusHandle; diff --git a/tools/CustomSetupTool/CustomSetupTool/install.c b/tools/CustomSetupTool/CustomSetupTool/install.c index 18794635fbaf..28e57eda6da7 100644 --- a/tools/CustomSetupTool/CustomSetupTool/install.c +++ b/tools/CustomSetupTool/CustomSetupTool/install.c @@ -175,7 +175,7 @@ INT_PTR CALLBACK SetupInstallPropPage_WndProc( #else SetWindowText(context->MainHeaderHandle, PhaFormatString( L"Installing Process Hacker %s", - PhGetString(context->SetupFileVersion) + PhGetString(context->RelVersion) )->Buffer); #endif SendMessage(context->ProgressHandle, PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); From 6befd020b0e7c2a8feff5896b607c50a6789adee Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:20:15 +1100 Subject: [PATCH 504/839] BuildTools: Update binaries --- .../bin/Release/CustomBuildTool.exe | Bin 162304 -> 163328 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 75264 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index eccc44aa16cc4def7600af91ef53d0824753c23d..5007bee5757ffc76010dffbf1e2a9721f0f42ddc 100644 GIT binary patch delta 18570 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 delta 17352 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 XRq(f61%H#iT_aB(xNA>Z`bPS{t0t-2 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index f3ccdd966ff702092d823ec80c0a128736eca275..c1fefa50203825176a1fd5a6879b0487bf090184 100644 GIT binary patch delta 18564 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 delta 15801 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 From 22dc344005ca7fcf1ed23761dee0049b406e8250 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 07:57:56 +1100 Subject: [PATCH 505/839] BuildTools: Fix commit hash --- tools/CustomBuildTool/Source Files/Build.cs | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 163328 -> 163328 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 2c0a63c2d5aa..4b58ebfece6e 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -255,7 +255,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (!GitExportBuild) { BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); - BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD").Trim(); + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse HEAD").Trim(); Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildBranch, ConsoleColor.White); diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 5007bee5757ffc76010dffbf1e2a9721f0f42ddc..9e9a9cd51082919cb782ba4c5b1a9f4c65341c94 100644 GIT binary patch delta 7129 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index c1fefa50203825176a1fd5a6879b0487bf090184..b2f8f7a0dd82ae675d6ced25eae4f43ee35488cc 100644 GIT binary patch delta 1374 zcmZ|PT}TvB6bJDC9cNc}+|?~MS9A%>MF=z{Ovznh*9w?_v*?oOSY z6%Pv{UeA1@Do||(_U8$8>GRTz8LGS$73WRkRR6dZpQ4-)J@F|svi+r8uebO18T=F1 z{6MR|tz(5Y+hthdjQZ7{Ze2Ae`fAwAZNuHhtWT8X4|hMNm_{Sz6XDob`q|)w odod{+g;i!>Ezp)_Mo<%F%4C-j+j*u>Yht00{c5HoCE`=@Z{bd@sQ>@~ delta 1369 zcmZ{kO=uHQ5Xb*7*~Dg>v=P!ok|3?grBJI@s2Yl-h#wdb=}D0qZ}uR92d!G^2GIys z6scuFsh3{-2->=6dMFW5DS=WDX%7lL)T-!7!Jgx6voFoA&RpKdZ{|OuzCNP%1=m!qS z4cJ+U*k!YnHM6tbO~AB7j<{dMIL&UY85m(i-vohAjOc@nKnueriS@Z8v={0M0kd`a zH0fr3eVG)ynZQZ}c;c1bcG1V1frUCz>1ApTw*rH5!_k@5Rs`^>E}?O&vZQEZY15zU zin)K15&S{yGgHVrH;`rG5(;oaGR;xtFu@rK9W3Pm!c4-W)U7LL7)`CS0XV|I zE5!CN>?N@eOv1bh{AS_?8-d(93)H6O|119Sh{G;ACL`^@UJJSK^Sbmh6PAgUsK2H; z*^RBhr7HS|p%X|lEJ^H?WST9ZfBYK3Aq%>(>yb5ufPcI~Y>{CL+kmqS+qWHf{$B>@ zJwDMSpl5s{wtkl>9WcKGxMPLUNvWC1`nv#nHP+0@^ny=B=@>jEpB{b6Ct9fP@3cr+ n0kz9UYg4RkCy=RQdQcTnx;%PEbu1oI#Y`jnU>4(HvC{k(UF55( From cbeecc7f15dc23dfd5c2dbcbc897fd0357f03c80 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 15 Oct 2017 08:01:17 +1100 Subject: [PATCH 506/839] BuildTools: Fix appveyor version --- tools/CustomBuildTool/Source Files/Build.cs | 2 +- .../bin/Release/CustomBuildTool.exe | Bin 163328 -> 163328 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 4b58ebfece6e..088571bb97a0 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -273,7 +273,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo BuildCount = "0"; BuildVersion = "3.0." + BuildRevision; - BuildLongVersion = "3.0." + BuildRevision + "." + BuildCount; + BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; if (ShowBuildInfo && !GitExportBuild) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 9e9a9cd51082919cb782ba4c5b1a9f4c65341c94..75b114bc7dbdf48d404d2a57782ce2527e4c2191 100644 GIT binary patch delta 108 zcmZqp!`bkMb3zBx(W;GIcC6fd3=Awq%NZF8>i9QbX0^N|kXN2_`@Bu4!?rh@_N1(g zJib}xhKPjD(W*z03_!pH#4?Nw3?6)p3?3gC7=nUbfC$T6Wwai>8&nOQ9 zPgt_+A*+WnN;&j`LJ*1&Br4E{q6~>5=t1N9^VId*b@#j-8|UHJ+0V>(W@l&ix~HY? zX=y~V=fCKfKgs|c+8L@pR&mWYzIfW_?D^0d_1ZV)xaa=dF7LZKWc6cTziY5->fW!n zEu(c0*_{lp%{u7$)L0gEZLlzD?sC@esJrOzgxw#YFw~{{15xk%mjXEyQR`}92u030 zf%>H?{hDoptV5lOR?JqF1w1i`>$gBA%B+;Lfj&X#WDf975PCBg$P?HISwotE=82}c zftM-c$wo8v>8B{MQqVE67I+E5ZrtEj=4T;X7*8268cKYW z^j9KU74`l}LEtyC_kxK$w*ZZTenk>++%TD&Wf$Qy139a)0v^GD2WYi=1vZ-w91 zr5V6}0d|sA3v7(+jbOlDJFqC|*X{tim(6855wWSkFZwE bR)R9JT-~xz8uP7TlNRBBXUywipR@k~HpKq= delta 1890 zcmaKtYe-a45Xb*#z3XEiy2+*sE@3G_R9cC;Nth*66oeFn5tT$lLgfd+EiE?|i@+9gPNEYkdByuB-lH?Rn{gBiG;b4rf>Aw|>kV z+;X4YNew1DhpSe4S4X`ol9)E~d89Au&-)X1g?dN~tUlBe4NiZ_(M!#<>4PV7+5>Fk z2h;2a0dy!tM}5Fsh3JhXz)}SpAhyRLpuHJ~A9$Hi zPZwc~Za+qfC5pt^Y@jb$MB7DsmjROrq9TfGHs=Ci;gBZtI~QI&PAC}doc*Bbo(PXu zO@;dpDFnX}d#7mRMG|mWk(Z|d2L#ilE}IEH6HsDhP9RHBu!^SDreISkK(#_sFGvM; zD_|3`jS4nI?6sm`n+y1%$P2rHmU-s2{UTz&oNT8<@2==r1(aJ+4&0iHXXvgNBNnIm za+BGmwZNHwXaPe3kgs6*#A*c7Z3Tf=f@dvg)iqE2_OryM6l}5(IHh2f>wrfJrvD_^ zU_lKuOv`w|&q+h;=F0!3_ZI6w~Z^?RB-pwpxtdnDI1?xl`Kf`pLb?7))c? zN59g?GAkk(x5cf%J6c`@>{753i?A56?Fo6t4j0?YVBB=EocUMBD>M`X^oC8NXE$e@ zos*a`L+PaAamy-~rAQ&eRjx_QsG)RHXM`c8IqVWVXDNwyV(UNEx&MRevgXp zg6H!NI*Exp%Ipd;@d{)XStRdnBHJfAwF>qVJZbs75)07$X$6}mvqECD^G! Date: Sun, 15 Oct 2017 09:33:23 +1100 Subject: [PATCH 507/839] BuildTools: Fix updating from older builds --- tools/CustomBuildTool/Source Files/Build.cs | 1 + tools/CustomBuildTool/Source Files/Utils.cs | 1 + .../bin/Release/CustomBuildTool.exe | Bin 163328 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 4 files changed, 2 insertions(+) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 088571bb97a0..0307f39ce004 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -990,6 +990,7 @@ public static void WebServiceUpdateConfig() FileLengthDeprecated = BuildSetupFileLength.ToString(), // TODO: Remove after most users have updated. ForumUrlDeprecated = "https://wj32.org/processhacker/", // TODO: Remove after most users have updated. + SetupHashDeprecated = BuildSetupHash, // TODO: Remove after most users have updated. SetupSigDeprecated = BuildSetupSig, // TODO: Remove after most users have updated. BinHashDeprecated = BuildBinHash // TODO: Remove after most users have updated. }); diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 088112be4cac..39727e0272cb 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -364,6 +364,7 @@ public class BuildUpdateRequest [DataMember(Name = "size")] public string FileLengthDeprecated { get; set; } // TODO: Remove after most users have updated. [DataMember(Name = "forum_url")] public string ForumUrlDeprecated { get; set; } // TODO: Remove after most users have updated. [DataMember(Name = "hash_bin")] public string BinHashDeprecated { get; set; } // TODO: Remove after most users have updated. + [DataMember(Name = "hash_setup")] public string SetupHashDeprecated { get; set; } // TODO: Remove after most users have updated. [DataMember(Name = "sig")] public string SetupSigDeprecated { get; set; } // TODO: Remove after most users have updated. } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 75b114bc7dbdf48d404d2a57782ce2527e4c2191..c47c6a0ee102e9e4af0762e2f4cd1e2d4a64a7c9 100644 GIT binary patch delta 8530 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 LZhM-7;(PHglgNPA delta 8258 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 wBdn;b?MPe5VK(Bk5E2pQ Date: Sun, 15 Oct 2017 20:44:30 +1100 Subject: [PATCH 508/839] Update PhGetPhVersionNumbers --- ProcessHacker/appsup.c | 4 +++- ProcessHacker/include/appsup.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 3fe2ad206c8e..4736a7a02e33 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1255,7 +1255,7 @@ PPH_STRING PhGetPhVersion( VOID PhGetPhVersionNumbers( _Out_opt_ PULONG MajorVersion, _Out_opt_ PULONG MinorVersion, - _Reserved_ PULONG Reserved, + _Out_opt_ PULONG BuildNumber, _Out_opt_ PULONG RevisionNumber ) { @@ -1263,6 +1263,8 @@ VOID PhGetPhVersionNumbers( *MajorVersion = PHAPP_VERSION_MAJOR; if (MinorVersion) *MinorVersion = PHAPP_VERSION_MINOR; + if (BuildNumber) + *BuildNumber = PHAPP_VERSION_BUILD; if (RevisionNumber) *RevisionNumber = PHAPP_VERSION_REVISION; } diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index d82339461152..bd3c04e42099 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -242,7 +242,7 @@ NTAPI PhGetPhVersionNumbers( _Out_opt_ PULONG MajorVersion, _Out_opt_ PULONG MinorVersion, - _Reserved_ PULONG Reserved, + _Out_opt_ PULONG BuildNumber, _Out_opt_ PULONG RevisionNumber ); From 77f43a80bb4c0fb10b32f24e174ed64fe4c76442 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 16 Oct 2017 00:43:01 +1100 Subject: [PATCH 509/839] Add KPH fixes for RS4 --- phlib/kphdata.c | 129 ++++++++++++++++++------------------------------ 1 file changed, 48 insertions(+), 81 deletions(-) diff --git a/phlib/kphdata.c b/phlib/kphdata.c index 1ec3c206008b..ba09f5928584 100644 --- a/phlib/kphdata.c +++ b/phlib/kphdata.c @@ -125,52 +125,31 @@ NTSTATUS KphInitializeDynamicPackage( Package->StructData.ObAttributesShift = 17; } // Windows 10 - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 10240) + else if (majorVersion == 10 && minorVersion == 0) { - 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; + 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; + default: + Package->BuildNumber = USHRT_MAX; + Package->ResultingNtVersion = PHNT_THRESHOLD; + break; + } Package->StructData.EgeGuid = 0x18; Package->StructData.EpObjectTable = 0x418; @@ -274,43 +253,31 @@ NTSTATUS KphInitializeDynamicPackage( 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) + else if (majorVersion == 10 && minorVersion == 0) { - 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; + 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; + default: + Package->BuildNumber = USHRT_MAX; + Package->ResultingNtVersion = PHNT_THRESHOLD; + break; + } Package->StructData.EgeGuid = 0xc; Package->StructData.EpObjectTable = 0x154; From b21eb234669e24781be116415886213389fdad8c Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 18 Oct 2017 04:40:48 +1100 Subject: [PATCH 510/839] NetworkTools: Fix hostname lookup not always working properly, Fix memory leak --- plugins/NetworkTools/tracert.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 311dfe47787f..c69ccd659198 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -264,6 +264,11 @@ NTSTATUS NetworkTracertThreadStart( IP_FLAG_DF, 0 }; + WSADATA winsockStartup; + + // WSAStartup required by GetNameInfo. + if (WSAStartup(WINSOCK_VERSION, &winsockStartup) != ERROR_SUCCESS) + goto CleanupExit; if (icmpRandString = PhCreateStringEx(NULL, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) * 2 + 2)) { @@ -448,13 +453,15 @@ NTSTATUS NetworkTracertThreadStart( CleanupExit: if (icmpHandle != INVALID_HANDLE_VALUE) - { IcmpCloseHandle(icmpHandle); - } PostMessage(context->WindowHandle, NTM_RECEIVEDFINISH, 0, 0); - PhDereferenceObject(context); + + if (icmpEchoBuffer) + PhDereferenceObject(icmpEchoBuffer); + + WSACleanup(); return STATUS_SUCCESS; } From b4614ff45e4e9527645cdc949260a8ad9b9fdef8 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 18 Oct 2017 21:41:35 +1100 Subject: [PATCH 511/839] Update to latest Windows SDK --- KProcessHacker/KProcessHacker.vcxproj | 2 +- ProcessHacker/ProcessHacker.vcxproj | 2 +- phlib/phlib.vcxproj | 2 +- phnt/include/ntmmapi.h | 8 ++++++++ plugins/DotNetTools/DotNetTools.vcxproj | 2 +- .../ExtendedNotifications/ExtendedNotifications.vcxproj | 2 +- plugins/ExtendedServices/ExtendedServices.vcxproj | 2 +- plugins/ExtendedTools/ExtendedTools.vcxproj | 2 +- plugins/ExtraPlugins/ExtraPlugins.vcxproj | 2 +- plugins/HardwareDevices/HardwareDevices.vcxproj | 2 +- plugins/NetworkTools/NetworkTools.vcxproj | 2 +- plugins/OnlineChecks/OnlineChecks.vcxproj | 2 +- plugins/ToolStatus/ToolStatus.vcxproj | 2 +- plugins/Updater/Updater.vcxproj | 2 +- plugins/UserNotes/UserNotes.vcxproj | 2 +- plugins/WindowExplorer/WindowExplorer.vcxproj | 2 +- .../CustomSetupTool/CustomSetupTool.vcxproj | 2 +- tools/peview/peview.vcxproj | 2 +- 18 files changed, 25 insertions(+), 17 deletions(-) diff --git a/KProcessHacker/KProcessHacker.vcxproj b/KProcessHacker/KProcessHacker.vcxproj index 3f18628eba62..b19957e7dee6 100644 --- a/KProcessHacker/KProcessHacker.vcxproj +++ b/KProcessHacker/KProcessHacker.vcxproj @@ -28,7 +28,7 @@ KProcessHacker $(VCTargetsPath11) - $(LatestTargetPlatformVersion) + 10.0.16299.0 WindowsKernelModeDriver10.0 diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 99d582dc11ed..b998678db459 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -22,7 +22,7 @@ {0271DD27-6707-4290-8DFE-285702B7115D} ProcessHacker Win32Proj - 10.0.15063.0 + 10.0.16299.0 diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index fe378512fe80..8582f9290f38 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -22,7 +22,7 @@ {477D0215-F252-41A1-874B-F27E3EA1ED17} phlib Win32Proj - 10.0.15063.0 + 10.0.16299.0 diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 8446e6f5568d..4f22eb67380e 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -629,6 +629,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 { diff --git a/plugins/DotNetTools/DotNetTools.vcxproj b/plugins/DotNetTools/DotNetTools.vcxproj index f0b912ea36de..60fe4cddde3e 100644 --- a/plugins/DotNetTools/DotNetTools.vcxproj +++ b/plugins/DotNetTools/DotNetTools.vcxproj @@ -23,7 +23,7 @@ DotNetTools Win32Proj DotNetTools - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj b/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj index 348d996c5ca4..4c13d33c0dc2 100644 --- a/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj +++ b/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj @@ -23,7 +23,7 @@ ExtendedNotifications Win32Proj ExtendedNotifications - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/ExtendedServices/ExtendedServices.vcxproj b/plugins/ExtendedServices/ExtendedServices.vcxproj index f257f184c66c..4c2f84f3736b 100644 --- a/plugins/ExtendedServices/ExtendedServices.vcxproj +++ b/plugins/ExtendedServices/ExtendedServices.vcxproj @@ -23,7 +23,7 @@ ExtendedServices Win32Proj ExtendedServices - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/ExtendedTools/ExtendedTools.vcxproj b/plugins/ExtendedTools/ExtendedTools.vcxproj index ea3f79282e4e..4dee2ca5e0ec 100644 --- a/plugins/ExtendedTools/ExtendedTools.vcxproj +++ b/plugins/ExtendedTools/ExtendedTools.vcxproj @@ -23,7 +23,7 @@ ExtendedTools Win32Proj ExtendedTools - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/ExtraPlugins/ExtraPlugins.vcxproj b/plugins/ExtraPlugins/ExtraPlugins.vcxproj index d42305f9cc4d..15bbc0c42f1f 100644 --- a/plugins/ExtraPlugins/ExtraPlugins.vcxproj +++ b/plugins/ExtraPlugins/ExtraPlugins.vcxproj @@ -23,7 +23,7 @@ ExtraPlugins Win32Proj ExtraPlugins - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/HardwareDevices/HardwareDevices.vcxproj b/plugins/HardwareDevices/HardwareDevices.vcxproj index e61a3c53b8db..754c8b7765ab 100644 --- a/plugins/HardwareDevices/HardwareDevices.vcxproj +++ b/plugins/HardwareDevices/HardwareDevices.vcxproj @@ -23,7 +23,7 @@ HardwareDevices Win32Proj HardwareDevices - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/NetworkTools/NetworkTools.vcxproj b/plugins/NetworkTools/NetworkTools.vcxproj index 3f96a14ded81..64ed76316c01 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj +++ b/plugins/NetworkTools/NetworkTools.vcxproj @@ -23,7 +23,7 @@ NetworkTools Win32Proj NetworkTools - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/OnlineChecks/OnlineChecks.vcxproj b/plugins/OnlineChecks/OnlineChecks.vcxproj index 9683eec73393..f6a7ecd4e1b2 100644 --- a/plugins/OnlineChecks/OnlineChecks.vcxproj +++ b/plugins/OnlineChecks/OnlineChecks.vcxproj @@ -23,7 +23,7 @@ OnlineChecks Win32Proj OnlineChecks - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/ToolStatus/ToolStatus.vcxproj b/plugins/ToolStatus/ToolStatus.vcxproj index b92b848af210..2931e6480717 100644 --- a/plugins/ToolStatus/ToolStatus.vcxproj +++ b/plugins/ToolStatus/ToolStatus.vcxproj @@ -23,7 +23,7 @@ ToolStatus Win32Proj ToolStatus - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/Updater/Updater.vcxproj b/plugins/Updater/Updater.vcxproj index 0cacd9bc1f76..1bb143343361 100644 --- a/plugins/Updater/Updater.vcxproj +++ b/plugins/Updater/Updater.vcxproj @@ -23,7 +23,7 @@ Updater Win32Proj Updater - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/UserNotes/UserNotes.vcxproj b/plugins/UserNotes/UserNotes.vcxproj index 45cec0ecac59..891f9462e957 100644 --- a/plugins/UserNotes/UserNotes.vcxproj +++ b/plugins/UserNotes/UserNotes.vcxproj @@ -23,7 +23,7 @@ UserNotes Win32Proj UserNotes - 10.0.15063.0 + 10.0.16299.0 diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj b/plugins/WindowExplorer/WindowExplorer.vcxproj index f61d6d998529..3bd826bcf330 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj @@ -23,7 +23,7 @@ WindowExplorer Win32Proj WindowExplorer - 10.0.15063.0 + 10.0.16299.0 diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 46933fea0f5e..3f43de47288d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -13,7 +13,7 @@ {5C00734F-F50A-49FC-9D2A-F6EE51ECB00F} CustomSetupTool - 10.0.15063.0 + 10.0.16299.0 CustomSetupTool diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index a5a6ae577e12..9ea3c2dce967 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -22,7 +22,7 @@ {72C124A2-3C80-41C6-ABA1-C4948B713204} peview Win32Proj - 10.0.15063.0 + 10.0.16299.0 From c8749bf759dd94f889df9c9fe54f17bf47ca36e9 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 18 Oct 2017 21:57:48 +1100 Subject: [PATCH 512/839] BuildTools: Fix build error while using Build Tools for Visual Studio 2017 --- tools/CustomBuildTool/Source Files/Build.cs | 7 +++---- tools/CustomBuildTool/Source Files/Utils.cs | 1 + .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 0307f39ce004..ac7a3f1f6170 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -206,8 +206,7 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) { if (CheckDependencies) { - Program.PrintColorMessage("Git not installed... Exiting.", ConsoleColor.Red); - return false; + Program.PrintColorMessage("[Warning] Git not installed...", ConsoleColor.Yellow); } } @@ -252,7 +251,7 @@ public static void CleanupBuildEnvironment() public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) { - if (!GitExportBuild) + if (!GitExportBuild && File.Exists(GitExePath)) { BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse HEAD").Trim(); @@ -280,7 +279,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildVersion + Environment.NewLine, ConsoleColor.White); - if (!BuildNightly && ShowLogInfo) + 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); diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 39727e0272cb..39cfc47bb98f 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -214,6 +214,7 @@ public static string GetMsbuildFilePath() { string vswhereResult = Win32.ShellExecute(vswhere, "-latest " + + "-products * " + "-requires Microsoft.Component.MSBuild " + "-property installationPath " ); diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index c47c6a0ee102e9e4af0762e2f4cd1e2d4a64a7c9..6d4f94ed7598186a47d75359070eba401bea087f 100644 GIT binary patch delta 9363 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 delta 9299 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index bd969a28c9e155179474e75a8c3859d44792fdd3..596e48415e9b89b3d78904c08a570cc77e4b95ca 100644 GIT binary patch delta 11806 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 BQX2pO From 1f050dcb6b070ba6e03995073f91839ee45bf313 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 18 Oct 2017 22:13:57 +1100 Subject: [PATCH 513/839] Update versions for RS3 --- phlib/global.c | 3 +++ phlib/include/phconfig.h | 1 + phlib/kphdata.c | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/phlib/global.c b/phlib/global.c index 26c69aa8a4a8..325b13764b8f 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -214,6 +214,9 @@ static VOID PhInitializeWindowsVersion( case 15063: WindowsVersion = WINDOWS_10_RS2; break; + case 16299: + WindowsVersion = WINDOWS_10_RS3; + break; default: WindowsVersion = WINDOWS_NEW; break; diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 482921a90007..dcff8133381b 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -31,6 +31,7 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_10_TH2 101 #define WINDOWS_10_RS1 102 #define WINDOWS_10_RS2 103 +#define WINDOWS_10_RS3 104 #define WINDOWS_NEW MAXLONG #define WINDOWS_HAS_IMMERSIVE (WindowsVersion >= WINDOWS_8) diff --git a/phlib/kphdata.c b/phlib/kphdata.c index ba09f5928584..472c7800d2c2 100644 --- a/phlib/kphdata.c +++ b/phlib/kphdata.c @@ -145,6 +145,10 @@ NTSTATUS KphInitializeDynamicPackage( Package->BuildNumber = 15063; Package->ResultingNtVersion = PHNT_REDSTONE2; break; + case 16299: + Package->BuildNumber = 16299; + Package->ResultingNtVersion = PHNT_REDSTONE3; + break; default: Package->BuildNumber = USHRT_MAX; Package->ResultingNtVersion = PHNT_THRESHOLD; @@ -273,6 +277,10 @@ NTSTATUS KphInitializeDynamicPackage( Package->BuildNumber = 15063; Package->ResultingNtVersion = PHNT_REDSTONE2; break; + case 16299: + Package->BuildNumber = 16299; + Package->ResultingNtVersion = PHNT_REDSTONE3; + break; default: Package->BuildNumber = USHRT_MAX; Package->ResultingNtVersion = PHNT_THRESHOLD; From 4c5b9185a0e95a57c3baec1929fde25ee5573e49 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 03:53:27 +1100 Subject: [PATCH 514/839] PHNT: Update types for RS3 --- phnt/include/ntexapi.h | 99 +++++++++++++++++++++++++++++++++++------ phnt/include/ntioapi.h | 18 +++++++- phnt/include/ntldr.h | 21 ++++++++- phnt/include/ntmmapi.h | 40 ++++++++++++----- phnt/include/ntpebteb.h | 30 ++++++++++--- phnt/include/ntpsapi.h | 67 +++++++++++++++++++++++++--- phnt/include/ntregapi.h | 11 ++++- 7 files changed, 246 insertions(+), 40 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 01452aa249b8..dcdad9fc1ba5 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -833,7 +833,8 @@ typedef enum _WNF_DATA_SCOPE WnfDataScopeSystem, WnfDataScopeSession, WnfDataScopeUser, - WnfDataScopeProcess + WnfDataScopeProcess, + WnfDataScopeMachine // REDSTONE3 } WNF_DATA_SCOPE; typedef struct _WNF_TYPE_ID @@ -1240,12 +1241,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) @@ -1265,8 +1266,8 @@ typedef enum _SYSTEM_INFORMATION_CLASS 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 @@ -1391,7 +1392,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemFlushInformation, // q: SYSTEM_FLUSH_INFORMATION SystemProcessorIdleMaskInformation, // since REDSTONE3 SystemSecureDumpEncryptionInformation, - SystemWriteConstraintInformation, + SystemWriteConstraintInformation, // SYSTEM_WRITE_CONSTRAINT_INFORMATION MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; @@ -1578,7 +1579,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 @@ -1792,12 +1795,25 @@ 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 @@ -1819,6 +1835,8 @@ typedef enum _EVENT_TRACE_INFORMATION_CLASS EventTraceStackCachingInformation, // EVENT_TRACE_STACK_CACHING_INFORMATION EventTraceObjectTypeFilterInformation, // EVENT_TRACE_TAG_FILTER_INFORMATION EventTraceSoftRestartInformation, // EVENT_TRACE_SOFT_RESTART_INFORMATION + EventTraceLastBranchConfigurationInformation, // REDSTONE3 + EventTraceLastBranchEventListInformation, MaxEventTraceInfoClass } EVENT_TRACE_INFORMATION_CLASS; @@ -2712,7 +2730,7 @@ typedef struct _PROCESS_ENERGY_VALUES_EXTENSION { union { - TIMELINE_BITMAP Timelines[9]; + TIMELINE_BITMAP Timelines[14]; // 9 for REDSTONE2, 14 for REDSTONE3 struct { TIMELINE_BITMAP CpuTimeline; @@ -2724,8 +2742,29 @@ typedef struct _PROCESS_ENERGY_VALUES_EXTENSION 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 @@ -2734,6 +2773,16 @@ typedef struct _PROCESS_EXTENDED_ENERGY_VALUES PROCESS_ENERGY_VALUES_EXTENSION Extension; } PROCESS_EXTENDED_ENERGY_VALUES, *PPROCESS_EXTENDED_ENERGY_VALUES; +// private +typedef enum _SYSTEM_PROCESS_CLASSIFICATION +{ + SystemProcessClassificationNormal, + SystemProcessClassificationSystem, + SystemProcessClassificationSecureSystem, + SystemProcessClassificationMemCompression, + SystemProcessClassificationMaximum +} SYSTEM_PROCESS_CLASSIFICATION; + // private typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION { @@ -2745,7 +2794,7 @@ typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION struct { ULONG HasStrongId : 1; - ULONG Classification : 4; + ULONG Classification : 4; // SYSTEM_PROCESS_CLASSIFICATION ULONG BackgroundActivityModerated : 1; ULONG Spare : 26; }; @@ -2969,13 +3018,28 @@ typedef enum _SYSTEM_ACTIVITY_MODERATION_STATE MaxSystemActivityModerationState } SYSTEM_ACTIVITY_MODERATION_STATE; -// private -typedef struct _SYSTEM_ACTIVITY_MODERATION_EXE_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 { @@ -3007,6 +3071,13 @@ 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; + #if (PHNT_MODE != PHNT_MODE_KERNEL) NTSYSCALLAPI @@ -3296,7 +3367,7 @@ typedef struct _KUSER_SHARED_DATA LONG TimeZoneBiasStamp; ULONG NtBuildNumber; - ULONG NtProductType; + NT_PRODUCT_TYPE NtProductType; BOOLEAN ProductTypeIsValid; UCHAR Reserved0[1]; USHORT NativeProcessorArchitecture; @@ -3395,7 +3466,9 @@ typedef struct _KUSER_SHARED_DATA USHORT UnparkedProcessorCount; ULONG EnclaveFeatureMask[4]; - ULONG Reserved8; + + ULONG TelemetryCoverageRound; + USHORT UserModeGlobalLogger[16]; ULONG ImageFileExecutionOptions; diff --git a/phnt/include/ntioapi.h b/phnt/include/ntioapi.h index 6bbcc5652ef9..158e3b091840 100644 --- a/phnt/include/ntioapi.h +++ b/phnt/include/ntioapi.h @@ -242,7 +242,7 @@ typedef enum _FILE_INFORMATION_CLASS FileRenameInformationExBypassAccessCheck, FileDesiredStorageClassInformation, // FILE_DESIRED_STORAGE_CLASS_INFORMATION // since REDSTONE2 FileStatInformation, // FILE_STAT_INFORMATION - FileMemoryPartitionInformation, // since REDSTONE3 + FileMemoryPartitionInformation, // FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3 FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; @@ -680,6 +680,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; @@ -695,6 +696,21 @@ 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; + // NtQueryDirectoryFile types typedef struct _FILE_DIRECTORY_INFORMATION diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 84e8428ad7a8..09c2f1299805 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -76,6 +76,8 @@ typedef enum _LDR_DLL_LOAD_REASON LoadReasonDynamicLoad, LoadReasonAsImageLoad, LoadReasonAsDataLoad, + LoadReasonEnclavePrimary, // REDSTONE3 + LoadReasonEnclaveDependency, LoadReasonUnknown = -1 } LDR_DLL_LOAD_REASON, *PLDR_DLL_LOAD_REASON; @@ -471,9 +473,25 @@ LdrUnregisterDllNotification( // private typedef struct _PS_MITIGATION_OPTIONS_MAP { - ULONG_PTR Map[2]; + union + { + ULONG_PTR Map[2]; // REDSTONE2 + struct + { + ULONG_PTR Depth : 16; // REDSTONE3 + ULONG_PTR Sequence : 48; + ULONG_PTR Reserved : 4; + ULONG_PTR NextEntry : 60; + }; + }; } 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 { @@ -496,6 +514,7 @@ typedef struct _PS_SYSTEM_DLL_INIT_BLOCK 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) diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 4f22eb67380e..02a3e19e74b4 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -127,7 +127,9 @@ 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 Reserved : 24; }; }; SIZE_T RegionSize; @@ -205,6 +207,7 @@ typedef struct _MEMORY_IMAGE_INFORMATION { ULONG ImagePartialMap : 1; ULONG ImageNotExecutable : 1; + ULONG ImageSigningLevel : 1; // REDSTONE3 ULONG Reserved : 30; }; }; @@ -378,10 +381,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; @@ -480,13 +481,13 @@ 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 } VIRTUAL_MEMORY_INFORMATION_CLASS; typedef struct _MEMORY_RANGE_ENTRY @@ -494,6 +495,14 @@ typedef struct _MEMORY_RANGE_ENTRY PVOID VirtualAddress; 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; +} CFG_CALL_TARGET_LIST_INFORMATION, *PCFG_CALL_TARGET_LIST_INFORMATION; #endif // end_private @@ -664,10 +673,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 @@ -722,7 +732,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) diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 6169a18d4f44..37d4fbb43a55 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -4,6 +4,16 @@ 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; + // symbols typedef struct _PEB { @@ -62,9 +72,11 @@ typedef struct _PEB ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[2]; - PVOID ReadOnlySharedMemoryBase; - PVOID HotpatchInformation; + + PVOID ReadOnlySharedMemoryBase; + PVOID SharedData; // HotpatchInformation PVOID *ReadOnlyStaticServerData; + PVOID AnsiCodePageData; // PCPTABLEINFO PVOID OemCodePageData; // PCPTABLEINFO PVOID UnicodeCaseTableData; // PNLSTABLEINFO @@ -127,7 +139,7 @@ typedef struct _PEB PVOID WerRegistrationData; PVOID WerShipAssertPtr; - PVOID pContextData; + PVOID pUnused; // pContextData PVOID pImageHeaderHash; union { @@ -144,6 +156,8 @@ typedef struct _PEB PVOID TppWorkerpListLock; LIST_ENTRY TppWorkerpList; PVOID WaitOnAddressHashTable[128]; + PVOID TelemetryCoverageHeader; // REDSTONE3 + ULONG CloudFileFlags; } PEB, *PPEB; #define GDI_BATCH_BUFFER_SIZE 310 @@ -188,11 +202,17 @@ typedef struct _TEB LCID CurrentLocale; ULONG FpSoftwareStatusRegister; PVOID ReservedForDebuggerInstrumentation[16]; - PVOID SystemReserved1[37]; + PVOID SystemReserved1[30]; + + 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; diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 0816befb2b34..205d892c7f7a 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -180,12 +180,12 @@ typedef enum _PROCESSINFOCLASS ProcessDisableSystemAllowedCpuSets, ProcessWakeInformation, // PROCESS_WAKE_INFORMATION ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE - ProcessManageWritesToExecutableMemory, // since REDSTONE3 + ProcessManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 ProcessCaptureTrustletLiveDump, ProcessTelemetryCoverage, ProcessEnclaveInformation, - ProcessEnableReadWriteVmLogging, - ProcessUptimeInformation, + ProcessEnableReadWriteVmLogging, // PROCESS_READWRITEVM_LOGGING_INFORMATION + ProcessUptimeInformation, // PROCESS_UPTIME_INFORMATION ProcessImageSection, MaxProcessInfoClass } PROCESSINFOCLASS; @@ -242,8 +242,8 @@ typedef enum _THREADINFOCLASS ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ThreadDbgkWerReportActive, ThreadAttachContainer, - ThreadManageWritesToExecutableMemory, // since REDSTONE3 - ThreadPowerThrottlingState, + ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 + ThreadPowerThrottlingState, // THREAD_POWER_THROTTLING_STATE MaxThreadInfoClass } THREADINFOCLASS; #endif @@ -587,6 +587,9 @@ 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_POLICY_INFORMATION, *PPROCESS_MITIGATION_POLICY_INFORMATION; @@ -725,7 +728,9 @@ 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 @@ -744,6 +749,45 @@ typedef struct _PROCESS_ENERGY_TRACKING_STATE 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; +} MANAGE_WRITES_TO_EXECUTABLE_MEMORY, *PMANAGE_WRITES_TO_EXECUTABLE_MEMORY; + +typedef struct _PROCESS_READWRITEVM_LOGGING_INFORMATION +{ + union + { + BOOLEAN Flags; + struct + { + BOOLEAN EnableReadVmLogging : 1; + BOOLEAN EnableWriteVmLogging : 1; + BOOLEAN 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; + // end_private #endif @@ -1258,6 +1302,7 @@ typedef enum _PS_ATTRIBUTE_NUM PsAttributeSafeOpenPromptOriginClaim, PsAttributeBnoIsolation, // PS_BNO_ISOLATION_PARAMETERS PsAttributeDesktopAppPolicy, // in ULONG + PsAttributeChpe, // since REDSTONE3 PsAttributeMax } PS_ATTRIBUTE_NUM; @@ -1409,7 +1454,15 @@ typedef enum _PS_MITIGATION_OPTION 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_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; // windows-internals-book:"Chapter 5" diff --git a/phnt/include/ntregapi.h b/phnt/include/ntregapi.h index 0a3a4c8be773..da9bc244de3a 100644 --- a/phnt/include/ntregapi.h +++ b/phnt/include/ntregapi.h @@ -120,7 +120,7 @@ typedef enum _KEY_SET_INFORMATION_CLASS KeySetVirtualizationInformation, // KEY_SET_VIRTUALIZATION_INFORMATION KeySetDebugInformation, KeySetHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION - KeySetLayerInformation, + KeySetLayerInformation, // KEY_SET_LAYER_INFORMATION MaxKeySetInfoClass } KEY_SET_INFORMATION_CLASS; @@ -139,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; From c741c5963ff012cc7160d9cbc5dc83fdbca12edd Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 03:53:57 +1100 Subject: [PATCH 515/839] Update type name --- ProcessHacker/appsup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 4736a7a02e33..233b59ec909c 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -146,7 +146,7 @@ NTSTATUS PhGetProcessSwitchContext( { if (!NT_SUCCESS(status = NtReadVirtualMemory( ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pContextData)), + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pUnused)), &data, sizeof(PVOID), NULL From 78f06aa426b4db13cde5eca81ab83339ae4db44e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 04:02:17 +1100 Subject: [PATCH 516/839] Add initial RS3 mitigation options --- ProcessHacker/include/procmtgn.h | 24 +++++---- ProcessHacker/mtgndlg.c | 1 + ProcessHacker/procmtgn.c | 87 ++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/include/procmtgn.h b/ProcessHacker/include/procmtgn.h index 480e4d1c6415..20522d78764b 100644 --- a/ProcessHacker/include/procmtgn.h +++ b/ProcessHacker/include/procmtgn.h @@ -4,16 +4,20 @@ 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 } PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION, *PPH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION; NTSTATUS PhGetProcessMitigationPolicy( diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index cd864ba0eba6..164152b0f52f 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -116,6 +116,7 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( } 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); } diff --git a/ProcessHacker/procmtgn.c b/ProcessHacker/procmtgn.c index 4a2c99011157..e002fe1beba4 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. * @@ -115,6 +116,9 @@ 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); return status; } @@ -219,6 +223,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 +266,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: @@ -391,6 +417,67 @@ 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; default: result = FALSE; } From a0943b8e42214ab4cdae43f9939b9ad6639652a5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 04:02:39 +1100 Subject: [PATCH 517/839] Remove mitigation dialog column header --- ProcessHacker/ProcessHacker.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index ffb1f8741c04..e37738d8f2f0 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1828,7 +1828,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + 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 From 25d4882497ded0f36a633c458eea69c60e76c3b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 04:03:30 +1100 Subject: [PATCH 518/839] Rename Win10 context guid --- ProcessHacker/appsup.c | 2 +- ProcessHacker/include/appsup.h | 2 +- ProcessHacker/proctree.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 233b59ec909c..8ac68a44e908 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -47,7 +47,7 @@ GUID VISTA_CONTEXT_GUID = { 0xe2011457, 0x1546, 0x43c5, { 0xa5, 0xfe, 0x00, 0x8d 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 } }; +GUID WIN10_CONTEXT_GUID = { 0x8e0f7a12, 0xbfb3, 0x4fe8, { 0xb9, 0xa5, 0x48, 0xfd, 0x50, 0xa1, 0x5a, 0x9a } }; /** * Determines whether a process is suspended. diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index bd3c04e42099..9df859d7fc9a 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -6,7 +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; +extern GUID WIN10_CONTEXT_GUID; // begin_phapppub PHAPPAPI diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 3d1ba2b22158..fcebcb953846 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -972,7 +972,7 @@ static VOID PhpUpdateProcessOsContext( { if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid))) { - if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINTHRESHOLD_CONTEXT_GUID)) + if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN10_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_10; else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID)) ProcessNode->OsContextVersion = WINDOWS_8_1; From 66677fced0386dc0b213a5d94a9727e292391590 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 04:25:36 +1100 Subject: [PATCH 519/839] Remove RS3 type --- phnt/include/ntldr.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 09c2f1299805..37ed1d78536b 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -476,13 +476,13 @@ typedef struct _PS_MITIGATION_OPTIONS_MAP union { ULONG_PTR Map[2]; // REDSTONE2 - struct - { - ULONG_PTR Depth : 16; // REDSTONE3 - ULONG_PTR Sequence : 48; - ULONG_PTR Reserved : 4; - ULONG_PTR NextEntry : 60; - }; + //struct + //{ + // ULONG_PTR Depth : 16; // REDSTONE3 + // ULONG_PTR Sequence : 48; + // ULONG_PTR Reserved : 4; + // ULONG_PTR NextEntry : 60; + //}; }; } PS_MITIGATION_OPTIONS_MAP, *PPS_MITIGATION_OPTIONS_MAP; From 56b208f13187e67c73275eb593a94e687258da46 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 21 Oct 2017 10:09:15 +1100 Subject: [PATCH 520/839] Fix process module highlighting regression --- ProcessHacker/modlist.c | 4 ++-- ProcessHacker/settings.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index a72a167d1f60..b6a24c56edd8 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -172,7 +172,7 @@ VOID PhLoadSettingsModuleList( PPH_STRING settings; PPH_STRING sortSettings; - flags = PhGetIntegerSetting(L"ModuleListFlags"); + flags = PhGetIntegerSetting(L"ModuleTreeListFlags"); settings = PhGetStringSetting(L"ModuleTreeListColumns"); sortSettings = PhGetStringSetting(L"ModuleTreeListSort"); @@ -192,7 +192,7 @@ VOID PhSaveSettingsModuleList( settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); - PhSetIntegerSetting(L"ModuleListFlags", Context->Flags); + PhSetIntegerSetting(L"ModuleTreeListFlags", Context->Flags); PhSetStringSetting2(L"ModuleTreeListColumns", &settings->sr); PhSetStringSetting2(L"ModuleTreeListSort", &sortSettings->sr); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 5eaf3964eeda..a9609843c868 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -116,7 +116,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerPairSetting(L"MiniInfoWindowPosition", L"200,200"); PhpAddIntegerSetting(L"MiniInfoWindowRefreshAutomatically", L"1"); PhpAddScalableIntegerPairSetting(L"MiniInfoWindowSize", L"@96|10,200"); - PhpAddIntegerSetting(L"ModuleListFlags", L"0"); + PhpAddIntegerSetting(L"ModuleTreeListFlags", L"1"); PhpAddStringSetting(L"ModuleTreeListColumns", L""); PhpAddStringSetting(L"ModuleTreeListSort", L"0,0"); // 0, NoSortOrder PhpAddStringSetting(L"NetworkTreeListColumns", L""); From dc7bed9ac18e086e2123fc2beecd99ac3b849e1c Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:17:12 +1100 Subject: [PATCH 521/839] Remove PhHeapHandle export --- ProcessHacker/ProcessHacker.def | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 51007a4e1896..b0fbedefeb2e 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -38,7 +38,6 @@ EXPORTS ; phconfig PhGlobalDpi DATA - PhHeapHandle DATA PhIsExecutingInWow64 PhInstanceHandle DATA PhOsVersion DATA From 95e6f3773eee5cd2b1cd97d6b22ad505f2a7ec99 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:20:56 +1100 Subject: [PATCH 522/839] SetupTool: Add support for legacy commandline options --- .../CustomSetupTool/include/setup.h | 37 ++++-- tools/CustomSetupTool/CustomSetupTool/main.c | 122 +++++++++++------- .../CustomSetupTool/CustomSetupTool/update.c | 35 +++-- 3 files changed, 121 insertions(+), 73 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 204e4eaff6cf..5e025039333f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -71,10 +71,13 @@ typedef enum _SETUP_COMMAND_TYPE SETUP_COMMAND_UNINSTALL, SETUP_COMMAND_UPDATE, SETUP_COMMAND_REPAIR, + SETUP_COMMAND_SILENTINSTALL, } SETUP_COMMAND_TYPE; typedef struct _PH_SETUP_CONTEXT { + SETUP_COMMAND_TYPE SetupMode; + HWND DialogHandle; HWND PropSheetBackHandle; HWND PropSheetForwardHandle; @@ -91,19 +94,29 @@ typedef struct _PH_SETUP_CONTEXT HICON IconSmallHandle; HICON IconLargeHandle; - PPH_STRING SetupInstallPath; - BOOLEAN SetupCreateDesktopShortcut; - BOOLEAN SetupCreateDesktopShortcutAllUsers; - BOOLEAN SetupCreateDefaultTaskManager; - BOOLEAN SetupCreateSystemStartup; - BOOLEAN SetupCreateMinimizedSystemStartup; - BOOLEAN SetupInstallDebuggingTools; - BOOLEAN SetupInstallPeViewAssociations; - BOOLEAN SetupInstallKphService; - BOOLEAN SetupResetSettings; - BOOLEAN SetupStartAppAfterExit; + 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 Spare : 22; + }; + }; ULONG ErrorCode; + + PPH_STRING SetupInstallPath; + PPH_STRING FilePath; PPH_STRING RelDate; @@ -263,7 +276,7 @@ BOOLEAN SetupExtractBuild( // update.c VOID SetupShowUpdateDialog( - VOID + _In_ SETUP_COMMAND_TYPE SetupMode ); // updatesetup.c diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 0ef34e6c04b7..b1327e184520 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -43,6 +43,19 @@ BOOLEAN NTAPI MainPropSheetCommandLineCallback( { if (Option) SetupMode = Option->Id; + 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; } @@ -70,7 +83,7 @@ INT CALLBACK MainPropSheet_Callback( { PPH_SETUP_CONTEXT context; - context = PhAllocate(sizeof(PH_SETUP_CONTEXT)); + context = PhCreateAlloc(sizeof(PH_SETUP_CONTEXT)); memset(context, 0, sizeof(PH_SETUP_CONTEXT)); context->CurrentMajorVersion = PHAPP_VERSION_MAJOR; @@ -171,11 +184,8 @@ VOID SetupShowInstallDialog( PhModalPropertySheet(&propSheetHeader); } -INT WINAPI wWinMain( - _In_ HINSTANCE Instance, - _In_opt_ HINSTANCE PrevInstance, - _In_ PWSTR CmdLine, - _In_ INT CmdShow +VOID SetupParseCommandLine( + VOID ) { static PH_COMMAND_LINE_OPTION options[] = @@ -184,67 +194,79 @@ INT WINAPI wWinMain( { SETUP_COMMAND_UNINSTALL, L"uninstall", NoArgumentType }, { SETUP_COMMAND_UPDATE, L"update", NoArgumentType }, { SETUP_COMMAND_REPAIR, L"repair", NoArgumentType }, + { SETUP_COMMAND_SILENTINSTALL, L"silent", NoArgumentType }, }; - HANDLE mutantHandle; PPH_STRING commandLine; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - - if (!NT_SUCCESS(PhInitializePhLibEx(ULONG_MAX, Instance, 0, 0))) - return 1; - - PhApplicationName = L"Process Hacker - Setup"; - PhGuiSupportInitialization(); - SetupInitializeDpi(); - - if (!NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) - return 1; - - if (!PhParseCommandLine( - &commandLine->sr, - options, - ARRAYSIZE(options), - PH_COMMAND_LINE_IGNORE_FIRST_PART, - MainPropSheetCommandLineCallback, - NULL - )) + if (NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) { + PhParseCommandLine( + &commandLine->sr, + options, + ARRAYSIZE(options), + PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS | PH_COMMAND_LINE_IGNORE_FIRST_PART, + MainPropSheetCommandLineCallback, + NULL + ); + PhDereferenceObject(commandLine); - return 1; } +} - PhDereferenceObject(commandLine); +VOID SetupInitializeMutant( + VOID + ) +{ + HANDLE mutantHandle; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING mutantName; RtlInitUnicodeString(&mutantName, L"PhSetupMutant"); InitializeObjectAttributes( &oa, &mutantName, - 0, + OBJ_CASE_INSENSITIVE, PhGetNamespaceHandle(), NULL ); - if (NT_SUCCESS(NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE))) - { - switch (SetupMode) - { - case SETUP_COMMAND_INSTALL: - default: - SetupShowInstallDialog(); - break; - case SETUP_COMMAND_UNINSTALL: - SetupShowUninstallDialog(); - break; - case SETUP_COMMAND_UPDATE: - SetupShowUpdateDialog(); - break; - case SETUP_COMMAND_REPAIR: - break; - } + NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); +} - NtClose(mutantHandle); +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(ULONG_MAX, Instance, 0, 0))) + return 1; + + PhApplicationName = L"Process Hacker - Setup"; + PhGuiSupportInitialization(); + SetupInitializeDpi(); + + SetupInitializeMutant(); + SetupParseCommandLine(); + + switch (SetupMode) + { + case SETUP_COMMAND_INSTALL: + default: + SetupShowInstallDialog(); + break; + case SETUP_COMMAND_UNINSTALL: + SetupShowUninstallDialog(); + break; + case SETUP_COMMAND_UPDATE: + case SETUP_COMMAND_SILENTINSTALL: + SetupShowUpdateDialog(SetupMode); + break; + case SETUP_COMMAND_REPAIR: + break; } return ERROR_SUCCESS; diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 43ea2e386524..47f46f81e0ce 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -68,7 +68,7 @@ NTSTATUS SetupUpdateBuild( } PPH_SETUP_CONTEXT CreateUpdateContext( - VOID + _In_ SETUP_COMMAND_TYPE SetupMode ) { PPH_SETUP_CONTEXT context; @@ -76,6 +76,8 @@ PPH_SETUP_CONTEXT CreateUpdateContext( context = (PPH_SETUP_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_CONTEXT)); memset(context, 0, sizeof(PH_SETUP_CONTEXT)); + context->SetupMode = SetupMode; + return context; } @@ -236,12 +238,25 @@ VOID SetupShowUpdatingDialog( config.pfCallback = SetupUpdatingTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = PhApplicationName; - config.pszMainInstruction = PhaFormatString( - L"Updating to version %lu.%lu.%lu...", - PHAPP_VERSION_MAJOR, - PHAPP_VERSION_MINOR, - PHAPP_VERSION_REVISION - )->Buffer; + + if (Context->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); } @@ -280,10 +295,9 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( } VOID SetupShowUpdateDialog( - VOID + _In_ SETUP_COMMAND_TYPE SetupMode ) { - PVOID context; TASKDIALOGCONFIG config; PH_AUTO_POOL autoPool; @@ -291,13 +305,12 @@ VOID SetupShowUpdateDialog( PhInitializeAutoPool(&autoPool); - context = CreateUpdateContext(); config.cbSize = sizeof(TASKDIALOGCONFIG); config.dwFlags = 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(SetupMode); TaskDialogIndirect(&config, NULL, NULL, NULL); From c759e7d2cd2690dc6be572e0f50876b32978eb7d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:21:17 +1100 Subject: [PATCH 523/839] StartTool: Update SDK --- tools/CustomStartTool/CustomStartTool.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomStartTool/CustomStartTool.vcxproj b/tools/CustomStartTool/CustomStartTool.vcxproj index 787cb050f1df..202dc4bc4471 100644 --- a/tools/CustomStartTool/CustomStartTool.vcxproj +++ b/tools/CustomStartTool/CustomStartTool.vcxproj @@ -22,7 +22,7 @@ {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} CustomStartTool Win32Proj - 10.0.15063.0 + 10.0.16299.0 From 9dd7e4291a8fd35920ae82da5849b70571eb3fdc Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:24:21 +1100 Subject: [PATCH 524/839] Improve SDK gui support initialization --- ProcessHacker/include/phapp.h | 2 +- ProcessHacker/main.c | 18 ++---------------- ProcessHacker/mainwnd.c | 2 +- phlib/guisup.c | 11 +++++++++-- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index a23878fd7ae5..ab2ee920e15f 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -131,7 +131,7 @@ PhUnregisterMessageLoopFilter( // end_phapppub VOID PhInitializeFont( - _In_ HWND hWnd + VOID ); // plugin diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 2f42307995e5..3381dfd1f705 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -486,31 +486,17 @@ VOID PhInitializeCommonControls( } VOID PhInitializeFont( - _In_ HWND hWnd + VOID ) { 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 = PhCreateFont(L"Microsoft Sans Serif", 8, FW_NORMAL)) && !(PhApplicationFont = PhCreateFont(L"Tahoma", 8, FW_NORMAL)) ) { - if (success) + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) PhApplicationFont = CreateFontIndirect(&metrics.lfMessageFont); else PhApplicationFont = NULL; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index fa80643d7f64..d53367b77ed8 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -528,7 +528,7 @@ VOID PhMwpOnSettingChange( if (PhApplicationFont) DeleteObject(PhApplicationFont); - PhInitializeFont(PhMainWndHandle); + PhInitializeFont(); SendMessage(TabControlHandle, WM_SETFONT, (WPARAM)PhApplicationFont, FALSE); } diff --git a/phlib/guisup.c b/phlib/guisup.c index 4871ac46d1ec..c27f2bce0957 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -43,8 +43,15 @@ VOID PhGuiSupportInitialization( VOID ) { - HMODULE shell32Handle; - HMODULE shlwapiHandle; + HDC hdc; + PVOID shell32Handle; + PVOID shlwapiHandle; + + if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) + { + PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); + DeleteDC(hdc); + } shell32Handle = LoadLibrary(L"shell32.dll"); shlwapiHandle = LoadLibrary(L"shlwapi.dll"); From 60293b2cfd3498dcb9ea450ee5a9cb5bcba29cf7 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:26:12 +1100 Subject: [PATCH 525/839] HardwareDevices: Remove unused code --- plugins/HardwareDevices/disknotify.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index f212a04d456a..f4025a6cc345 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -77,16 +77,11 @@ LRESULT CALLBACK MainWndDevicesSubclassProc( } } } - - goto DefaultWndProc; } break; } return DefSubclassProc(hWnd, uMsg, wParam, lParam); - -DefaultWndProc: - return DefWindowProc(hWnd, uMsg, wParam, lParam); } VOID AddRemoveDeviceChangeCallback( From a6ad02b4b96759c31777e7e0805251c83af7cdb2 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 23 Oct 2017 21:26:55 +1100 Subject: [PATCH 526/839] peview: Remove duplicate SDK gui support initialization --- tools/peview/main.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tools/peview/main.c b/tools/peview/main.c index 87404e5258c6..2338bf6d2a59 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -37,19 +37,6 @@ static BOOLEAN NTAPI PvCommandLineCallback( return TRUE; } -static VOID PvpInitializeDpi( - VOID - ) -{ - HDC hdc; - - if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) - { - PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - DeleteDC(hdc); - } -} - INT WINAPI wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, @@ -69,7 +56,6 @@ INT WINAPI wWinMain( return 1; PhGuiSupportInitialization(); - PvpInitializeDpi(); PhSettingsInitialization(); PeInitializeSettings(); PvPropInitialization(); From 4d62221d6ab34d4bc029b44eb08049c5a4533145 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 26 Oct 2017 14:11:46 +1100 Subject: [PATCH 527/839] Fix RS3 type, Add JobObject comments --- phnt/include/ntexapi.h | 2 +- phnt/include/ntpsapi.h | 57 +++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index dcdad9fc1ba5..595381753cec 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -2937,7 +2937,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 diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 205d892c7f7a..513505172b0b 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1624,52 +1624,53 @@ NtCreateThreadEx( #if (PHNT_MODE != PHNT_MODE_KERNEL) // JOBOBJECTINFOCLASS -#define JobObjectBasicAccountingInformation 1 -#define JobObjectBasicLimitInformation 2 -#define JobObjectBasicProcessIdList 3 -#define JobObjectBasicUIRestrictions 4 -#define JobObjectSecurityLimitInformation 5 -#define JobObjectEndOfJobTimeInformation 6 -#define JobObjectAssociateCompletionPortInformation 7 -#define JobObjectBasicAndIoAccountingInformation 8 -#define JobObjectExtendedLimitInformation 9 -#define JobObjectJobSetInformation 10 -#define JobObjectGroupInformation 11 -#define JobObjectNotificationLimitInformation 12 -#define JobObjectLimitViolationInformation 13 -#define JobObjectGroupInformationEx 14 -#define JobObjectCpuRateControlInformation 15 +// 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 -#define JobObjectExtendedAccountingInformation 19 -#define JobObjectWakeInformation 20 +#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 +#define JobObjectInterferenceInformation 26 // JOBOBJECT_INTERFERENCE_INFORMATION #define JobObjectClearPeakJobMemoryUsed 27 -#define JobObjectMemoryUsageInformation 28 +#define JobObjectMemoryUsageInformation 28 // JOBOBJECT_MEMORY_USAGE_INFORMATION // JOBOBJECT_MEMORY_USAGE_INFORMATION_V2 #define JobObjectSharedCommit 29 #define JobObjectContainerId 30 #define JobObjectIoRateControlInformation 31 -#define JobObjectNetRateControlInformation 32 -#define JobObjectNotificationLimitInformation2 33 -#define JobObjectLimitViolationInformation2 34 +#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 -#define JobObjectSiloRootDirectory 37 -#define JobObjectServerSiloBasicInformation 38 -#define JobObjectServerSiloUserSharedData 39 +#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 +#define JobObjectEnergyTrackingState 46 // JOBOBJECT_ENERGY_TRACKING_STATE #define JobObjectThreadImpersonationInformation 47 #define MaxJobObjectInfoClass 48 From f05b831d5ba769ad209bf4dbe05e754644d99267 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 26 Oct 2017 14:16:33 +1100 Subject: [PATCH 528/839] Adjust sysinfo graph padding, Fix sysinfo fullscreen (F11) bleeding through multiple-monitors --- ProcessHacker/include/sysinfop.h | 4 ++-- ProcessHacker/sysinfo.c | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/include/sysinfop.h b/ProcessHacker/include/sysinfop.h index ccd8b02fd1df..2428889df949 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -5,8 +5,8 @@ #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_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 diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 55513f538b9a..d756db5111b8 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -580,15 +580,14 @@ VOID PhSipOnCommand( 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, + info.rcMonitor.left, + info.rcMonitor.top, + (info.rcMonitor.right - info.rcMonitor.left), + (info.rcMonitor.bottom - info.rcMonitor.top), SWP_NOOWNERZORDER | SWP_FRAMECHANGED ); } From ffb626cf0c5569590dc9dcafc4af75184b12c8aa Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 26 Oct 2017 16:36:44 +1100 Subject: [PATCH 529/839] Fix sysinfo fullscreen topmost bug --- ProcessHacker/sysinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index d756db5111b8..509a4b75a5ec 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -596,7 +596,7 @@ VOID PhSipOnCommand( { 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); + SetWindowPos(PhSipWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } } break; From f525b448d9289b5c64173326c142611651d386dc Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 30 Oct 2017 00:39:15 +1100 Subject: [PATCH 530/839] KPH: Fix macro usage --- KProcessHacker/dyndata.c | 6 ++---- KProcessHacker/include/kph.h | 9 +++++++++ KProcessHacker/object.c | 20 ++++++++++---------- KProcessHacker/qrydrv.c | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/KProcessHacker/dyndata.c b/KProcessHacker/dyndata.c index ff685369570b..be90cda839b0 100644 --- a/KProcessHacker/dyndata.c +++ b/KProcessHacker/dyndata.c @@ -23,8 +23,6 @@ #define _DYNDATA_PRIVATE #include -#define C_2sTo4(x) ((unsigned int)(signed short)(x)) - NTSTATUS KphpLoadDynamicConfiguration( _In_ PVOID Buffer, _In_ ULONG Length @@ -126,13 +124,13 @@ NTSTATUS KphpLoadDynamicConfiguration( config = Buffer; - if (Length < (ULONG)FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages)) + 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 < (ULONG)FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages) + config->NumberOfPackages * sizeof(KPH_DYN_PACKAGE)) + 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); diff --git a/KProcessHacker/include/kph.h b/KProcessHacker/include/kph.h index 4c5032411cf1..2afa4a4fc84a 100644 --- a/KProcessHacker/include/kph.h +++ b/KProcessHacker/include/kph.h @@ -8,6 +8,15 @@ #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 diff --git a/KProcessHacker/object.c b/KProcessHacker/object.c index 134a4d36e6db..704fd90dea58 100644 --- a/KProcessHacker/object.c +++ b/KProcessHacker/object.c @@ -91,7 +91,7 @@ PHANDLE_TABLE KphReferenceProcessHandleTable( // Prevent the process from terminating and get its handle table. if (NT_SUCCESS(PsAcquireProcessExitSynchronization(Process))) { - handleTable = *(PHANDLE_TABLE *)((ULONG_PTR)Process + KphDynEpObjectTable); + handleTable = *(PHANDLE_TABLE *)PTR_ADD_OFFSET(Process, KphDynEpObjectTable); if (!handleTable) PsReleaseProcessExitSynchronization(Process); @@ -133,7 +133,7 @@ VOID KphUnlockHandleTableEntry( // Allow waiters to wake up. - handleContentionEvent = (PEX_PUSH_LOCK)((ULONG_PTR)HandleTable + KphDynHtHandleContentionEvent); + handleContentionEvent = (PEX_PUSH_LOCK)PTR_ADD_OFFSET(HandleTable, KphDynHtHandleContentionEvent); if (*(PULONG_PTR)handleContentionEvent != 0) ExfUnblockPushLock(handleContentionEvent, NULL); @@ -167,13 +167,13 @@ BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( objectType = ObGetObjectType(handleInfo.Object); if (objectType && KphDynOtIndex != -1) - handleInfo.ObjectTypeIndex = (USHORT)*(PUCHAR)((ULONG_PTR)objectType + KphDynOtIndex); + 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 = (PVOID)((ULONG_PTR)context->CurrentEntry + sizeof(KPH_PROCESS_HANDLE)); + 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 @@ -291,7 +291,7 @@ NTSTATUS KpiEnumerateProcessHandles( // Initialize the enumeration context. context.Buffer = Buffer; - context.BufferLimit = (PVOID)((ULONG_PTR)Buffer + BufferLength); + context.BufferLimit = PTR_ADD_OFFSET(Buffer, BufferLength); context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles; context.Count = 0; context.Status = STATUS_SUCCESS; @@ -321,7 +321,7 @@ NTSTATUS KpiEnumerateProcessHandles( ObDereferenceObject(process); // Write the number of handles if we can. - if (BufferLength >= (ULONG)FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) + if (BufferLength >= UFIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) { if (AccessMode != KernelMode) { @@ -455,7 +455,7 @@ NTSTATUS KphQueryNameFileObject( // 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)); + 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. @@ -943,7 +943,7 @@ NTSTATUS KpiQueryInformationObject( if (objectType) { - objectTypeName = (PUNICODE_STRING)((ULONG_PTR)objectType + KphDynOtName); + objectTypeName = (PUNICODE_STRING)PTR_ADD_OFFSET(objectType, KphDynOtName); RtlInitUnicodeString(&etwRegistrationName, L"EtwRegistration"); if (!RtlEqualUnicodeString(objectTypeName, &etwRegistrationName, FALSE)) @@ -958,10 +958,10 @@ NTSTATUS KpiQueryInformationObject( if (NT_SUCCESS(status)) { - guidEntry = *(PVOID *)((ULONG_PTR)etwReg + KphDynEreGuidEntry); + guidEntry = *(PVOID *)PTR_ADD_OFFSET(etwReg, KphDynEreGuidEntry); if (guidEntry) - basicInfo.Guid = *(GUID *)((ULONG_PTR)guidEntry + KphDynEgeGuid); + basicInfo.Guid = *(GUID *)PTR_ADD_OFFSET(guidEntry, KphDynEgeGuid); else memset(&basicInfo.Guid, 0, sizeof(GUID)); diff --git a/KProcessHacker/qrydrv.c b/KProcessHacker/qrydrv.c index 641779fa0a52..cc3879e0e3fb 100644 --- a/KProcessHacker/qrydrv.c +++ b/KProcessHacker/qrydrv.c @@ -222,7 +222,7 @@ VOID KphpCopyInfoUnicodeString( if (UnicodeString) { - targetBuffer = (PWCHAR)((PCHAR)Information + sizeof(UNICODE_STRING)); + targetBuffer = (PWCHAR)PTR_ADD_OFFSET(Information, sizeof(UNICODE_STRING)); targetUnicodeString->Length = UnicodeString->Length; targetUnicodeString->MaximumLength = UnicodeString->Length; From 1e60fa3d87bf8c5df39aad276d2e58a93ba51b3c Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 31 Oct 2017 06:07:17 +1100 Subject: [PATCH 531/839] Fix hexedit control scrolling bug (If one line of text is off the screen you cannot scroll down to it - Raymond Lee) --- phlib/hexedit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phlib/hexedit.c b/phlib/hexedit.c index 82c1d614d83f..8e6fb97cff83 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -3,6 +3,7 @@ * hex editor control * * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -1289,12 +1290,12 @@ VOID PhpHexEditUpdateScrollbars( si.fMask = SIF_ALL; si.nMin = 0; - si.nMax = (Context->Length / Context->BytesPerRow) - 1; + 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) + if (si.nMax > (LONG)si.nPage - 1) EnableScrollBar(hwnd, SB_VERT, ESB_ENABLE_BOTH); // No horizontal scrollbar please. From 8d296ddfdec5d0f34299bc3f2415586e93c1f490 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 31 Oct 2017 06:36:35 +1100 Subject: [PATCH 532/839] Fix hexedit control pagedown scrolling bug, Add missing hexedit scrolbar right-click options --- phlib/hexedit.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/phlib/hexedit.c b/phlib/hexedit.c index 8e6fb97cff83..909e020d8c55 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -211,10 +211,15 @@ LRESULT CALLBACK PhpHexEditWndProc( 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 > context->Length - mult) - context->TopIndex = context->Length - mult; + if (context->TopIndex > pageEnd) + context->TopIndex = pageEnd; REDRAW_WINDOW(hwnd); } @@ -234,6 +239,15 @@ LRESULT CALLBACK PhpHexEditWndProc( 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); From 6a7c7dcb33c5b1a93592bcf615b8c3248e1f7b65 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 31 Oct 2017 08:10:07 +1100 Subject: [PATCH 533/839] Fix macro usage --- phlib/include/mapimg.h | 2 +- phlib/mapimg.c | 21 +++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index ba179e8ca83d..8590b1b50f67 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -79,7 +79,7 @@ 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 ); diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 9210e2e351c9..aa000600495b 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -117,11 +117,7 @@ NTSTATUS PhInitializeMappedImage( // 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 - ); + MappedImage->Sections = IMAGE_FIRST_SECTION(MappedImage->NtHeaders); return STATUS_SUCCESS; } @@ -153,7 +149,7 @@ NTSTATUS PhLoadMappedImage( if (!NT_SUCCESS(status)) { - NtUnmapViewOfSection(NtCurrentProcess(), MappedImage->ViewBase); + PhUnloadMappedImage(MappedImage); } } @@ -307,8 +303,8 @@ PVOID PhMappedImageRvaToVa( if (Section) *Section = section; - return (PVOID)( - (ULONG_PTR)MappedImage->ViewBase + + return PTR_ADD_OFFSET( + MappedImage->ViewBase, (Rva - section->VirtualAddress) + section->PointerToRawData ); @@ -316,7 +312,7 @@ PVOID PhMappedImageRvaToVa( BOOLEAN 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 ) @@ -324,7 +320,7 @@ BOOLEAN PhGetMappedImageSectionName( BOOLEAN result; SIZE_T returnCount; - result = PhCopyBytesZ( + result = PhCopyStringZFromBytes( Section->Name, IMAGE_SIZEOF_SHORT_NAME, Buffer, @@ -525,10 +521,7 @@ NTSTATUS PhLoadRemoteMappedImage( return status; } - RemoteMappedImage->Sections = (PIMAGE_SECTION_HEADER)( - (PCHAR)RemoteMappedImage->NtHeaders + - FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + ntHeaders.FileHeader.SizeOfOptionalHeader - ); + RemoteMappedImage->Sections = IMAGE_FIRST_SECTION(RemoteMappedImage->NtHeaders); return STATUS_SUCCESS; } From 89b0f55f35d88c544afeab5172b6f4dc15886067 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 10:06:56 +1100 Subject: [PATCH 534/839] Remove casts, Fix macro usage --- ProcessHacker/memprv.c | 2 +- ProcessHacker/phsvc/svcapi.c | 2 +- phlib/mapimg.c | 16 ++++++++-------- tools/peview/ldprp.c | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 881a5d2500e7..0b38809c1d1e 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -494,7 +494,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( PVOID cfgBitmapAddress = NULL; PVOID cfgBitmapWow64Address = NULL; - if (ldrInitBlock.Size >= (ULONG)FIELD_OFFSET(PS_SYSTEM_DLL_INIT_BLOCK, Wow64CfgBitMap)) + if (ldrInitBlock.Size >= UFIELD_OFFSET(PS_SYSTEM_DLL_INIT_BLOCK, Wow64CfgBitMap)) { cfgBitmapAddress = (PVOID)ldrInitBlock.CfgBitMap; cfgBitmapWow64Address = (PVOID)ldrInitBlock.Wow64CfgBitMap; diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index e9e614c84c91..be7a728ce830 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -239,7 +239,7 @@ NTSTATUS PhSvcCaptureSid( if (sid) { - if (String->Length < (ULONG)FIELD_OFFSET(struct _SID, IdentifierAuthority) || + if (String->Length < UFIELD_OFFSET(struct _SID, IdentifierAuthority) || String->Length < RtlLengthRequiredSid(((struct _SID *)sid)->SubAuthorityCount) || !RtlValidSid(sid)) { diff --git a/phlib/mapimg.c b/phlib/mapimg.c index aa000600495b..fbee31dec399 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1045,7 +1045,7 @@ NTSTATUS PhGetMappedImageImportEntry( if (IMAGE_SNAP_BY_ORDINAL32(entry.u1.Ordinal)) { Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL32(entry.u1.Ordinal); + Entry->Ordinal = IMAGE_ORDINAL32(entry.u1.Ordinal); return STATUS_SUCCESS; } @@ -1068,7 +1068,7 @@ NTSTATUS PhGetMappedImageImportEntry( if (IMAGE_SNAP_BY_ORDINAL64(entry.u1.Ordinal)) { Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL64(entry.u1.Ordinal); + Entry->Ordinal = IMAGE_ORDINAL64(entry.u1.Ordinal); return STATUS_SUCCESS; } @@ -1219,7 +1219,7 @@ NTSTATUS PhGetMappedImageCfg64( return status; // Not every load configuration defines CFG characteristics - if (config64->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardFlags)) + if (config64->Size < UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardFlags)) return STATUS_INVALID_VIEW_SIZE; CfgConfig->MappedImage = MappedImage; @@ -1262,7 +1262,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->GuardAdressIatTable = 0; if ( - config64->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardAddressTakenIatEntryTable) + + config64->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardAddressTakenIatEntryTable) + sizeof(config64->GuardAddressTakenIatEntryTable) + sizeof(config64->GuardAddressTakenIatEntryCount) ) @@ -1295,7 +1295,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->GuardLongJumpTable = 0; if ( - config64->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardLongJumpTargetTable) + + config64->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardLongJumpTargetTable) + sizeof(config64->GuardLongJumpTargetTable) + sizeof(config64->GuardLongJumpTargetCount) ) @@ -1339,7 +1339,7 @@ NTSTATUS PhGetMappedImageCfg32( return status; // Not every load configuration defines CFG characteristics - if (config32->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardFlags)) + if (config32->Size < UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardFlags)) return STATUS_INVALID_VIEW_SIZE; CfgConfig->MappedImage = MappedImage; @@ -1382,7 +1382,7 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->GuardAdressIatTable = 0; if ( - config32->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardAddressTakenIatEntryTable) + + config32->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardAddressTakenIatEntryTable) + sizeof(config32->GuardAddressTakenIatEntryTable) + sizeof(config32->GuardAddressTakenIatEntryCount) ) @@ -1415,7 +1415,7 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->GuardLongJumpTable = 0; if ( - config32->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardLongJumpTargetTable) + + config32->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardLongJumpTargetTable) + sizeof(config32->GuardLongJumpTargetTable) + sizeof(config32->GuardLongJumpTargetCount) ) diff --git a/tools/peview/ldprp.c b/tools/peview/ldprp.c index 826ec0d6a711..d19cb649d83b 100644 --- a/tools/peview/ldprp.c +++ b/tools/peview/ldprp.c @@ -85,21 +85,21 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( 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, GuardCFCheckFunctionPointer)) \ + if ((Config)->Size >= UFIELD_OFFSET(Type, GuardCFCheckFunctionPointer)) \ { \ 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) \ + if ((Config)->Size >= UFIELD_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) \ + if ((Config)->Size >= UFIELD_OFFSET(Type, GuardLongJumpTargetTable) \ + sizeof((Config)->GuardLongJumpTargetTable) \ + sizeof((Config)->GuardLongJumpTargetCount)) \ { \ From 2b8e69e0ac594718585fc3ef7aa809040eb99d00 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 10:07:38 +1100 Subject: [PATCH 535/839] Fix typo --- plugins/ToolStatus/toolstatus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index b905a915f8ff..fee12ad7a937 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -2,8 +2,8 @@ * Process Hacker ToolStatus - * toolstatus header * - * Copyright (C) 2011-2016 dmex * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2011-2016 dmex * * This file is part of Process Hacker. * From 374cec4c0ff8ed06ba9a7e4aa2e839223dec99a5 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 10:08:53 +1100 Subject: [PATCH 536/839] peview: query section names using builtin function --- tools/peview/peprp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index b2d8d744382f..bce767f5c460 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -531,11 +531,10 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( for (i = 0; i < PvMappedImage.NumberOfSections; i++) { INT lvItemIndex; - WCHAR sectionName[9]; + WCHAR sectionName[IMAGE_SIZEOF_SHORT_NAME + 1]; WCHAR pointer[PH_PTR_STR_LEN_1]; - if (PhCopyStringZFromBytes(PvMappedImage.Sections[i].Name, - IMAGE_SIZEOF_SHORT_NAME, sectionName, 9, NULL)) + if (PhGetMappedImageSectionName(&PvMappedImage.Sections[i], sectionName, ARRAYSIZE(sectionName), NULL)) { lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, sectionName, NULL); From c63ad12d63ef4c162d2146894bdf43e4a97c05bc Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 10:09:45 +1100 Subject: [PATCH 537/839] Remove duplicate ProcessPackage query --- ProcessHacker/procprv.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 296ba8fbdc6a..a55eefbc3460 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1171,21 +1171,13 @@ VOID PhpProcessQueryStage2( if (PhEnableProcessQueryStage2 && processItem->FileName) { - PPH_STRING packageFullName = NULL; - - if (processItem->QueryHandle) - packageFullName = PhGetProcessPackageFullName(processItem->QueryHandle); - Data->VerifyResult = PhVerifyFileCached( processItem->FileName, - packageFullName, + processItem->PackageFullName, &Data->VerifySignerName, FALSE ); - if (packageFullName) - PhDereferenceObject(packageFullName); - status = PhIsExecutablePacked( processItem->FileName->Buffer, &Data->IsPacked, From 15c68b93022efc8d7e6f3bb29f713d1fc80ee89d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 11:16:51 +1100 Subject: [PATCH 538/839] Fix typo --- phlib/util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/phlib/util.c b/phlib/util.c index 67fd7a565518..7d7f4950e9a0 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -380,7 +380,6 @@ INT PhShowMessage( va_list argptr; PPH_STRING message; - va_start(argptr, Format); va_start(argptr, Format); message = PhFormatString_V(Format, argptr); va_end(argptr); From 84f88facc554eb69e2390e4adaab1167c929d4ce Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 12:18:07 +1100 Subject: [PATCH 539/839] Fix memory tab address filtering --- ProcessHacker/include/procprpp.h | 2 ++ ProcessHacker/prpgmem.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index 500573f100f7..2e315c4db325 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -305,6 +305,8 @@ typedef struct _PH_MEMORY_CONTEXT NTSTATUS LastRunStatus; PPH_STRING ErrorMessage; + BOOLEAN UseSearchPointer; + ULONG64 SearchPointer; PPH_STRING SearchboxText; PPH_TN_FILTER_ENTRY AllocationFilterEntry; PPH_TN_FILTER_ENTRY FilterEntry; diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index adb9146f7418..b835125aca20 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -256,6 +256,15 @@ BOOLEAN PhpMemoryTreeFilterCallback( if (PhIsNullOrEmptyString(memoryContext->SearchboxText)) return TRUE; + if ( + memoryContext->UseSearchPointer && + (memoryNode->MemoryItem->BaseAddress == (PVOID)memoryContext->SearchPointer || + memoryNode->MemoryItem->AllocationBase == (PVOID)memoryContext->SearchPointer) + ) + { + return TRUE; + } + if (memoryNode->BaseAddressText[0]) { if (PhpWordMatchHandleStringZ(memoryContext->SearchboxText, memoryNode->BaseAddressText)) @@ -408,6 +417,9 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( 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. PhMoveReference(&memoryContext->SearchboxText, newSearchboxText); From 817bbecc7b766e3ea71491a4cba2876f656f0b0c Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 23:40:22 +1100 Subject: [PATCH 540/839] Remove ExtraPlugins --- plugins/ExtraPlugins/CHANGELOG.txt | 4 - plugins/ExtraPlugins/ExtraPlugins.rc | 170 - plugins/ExtraPlugins/ExtraPlugins.vcxproj | 108 - .../ExtraPlugins/ExtraPlugins.vcxproj.filters | 97 - plugins/ExtraPlugins/cloud.c | 373 -- plugins/ExtraPlugins/dialog.c | 709 --- plugins/ExtraPlugins/disabled.c | 195 - plugins/ExtraPlugins/main.c | 351 -- plugins/ExtraPlugins/main.h | 327 -- plugins/ExtraPlugins/miniz/miniz.c | 4190 ----------------- plugins/ExtraPlugins/miniz/miniz.h | 785 --- plugins/ExtraPlugins/plugin.c | 371 -- plugins/ExtraPlugins/resource.h | 38 - .../resources/cog_edit_modern.png | Bin 341 -> 0 bytes plugins/ExtraPlugins/setup/page3.c | 86 - plugins/ExtraPlugins/setup/page4.c | 78 - plugins/ExtraPlugins/setup/page5.c | 198 - plugins/ExtraPlugins/setup/uninstall.c | 158 - plugins/ExtraPlugins/setup/updater.c | 678 --- plugins/ExtraPlugins/setup/verify.c | 204 - plugins/ExtraPlugins/wndtree.c | 461 -- 21 files changed, 9581 deletions(-) delete mode 100644 plugins/ExtraPlugins/CHANGELOG.txt delete mode 100644 plugins/ExtraPlugins/ExtraPlugins.rc delete mode 100644 plugins/ExtraPlugins/ExtraPlugins.vcxproj delete mode 100644 plugins/ExtraPlugins/ExtraPlugins.vcxproj.filters delete mode 100644 plugins/ExtraPlugins/cloud.c delete mode 100644 plugins/ExtraPlugins/dialog.c delete mode 100644 plugins/ExtraPlugins/disabled.c delete mode 100644 plugins/ExtraPlugins/main.c delete mode 100644 plugins/ExtraPlugins/main.h delete mode 100644 plugins/ExtraPlugins/miniz/miniz.c delete mode 100644 plugins/ExtraPlugins/miniz/miniz.h delete mode 100644 plugins/ExtraPlugins/plugin.c delete mode 100644 plugins/ExtraPlugins/resource.h delete mode 100644 plugins/ExtraPlugins/resources/cog_edit_modern.png delete mode 100644 plugins/ExtraPlugins/setup/page3.c delete mode 100644 plugins/ExtraPlugins/setup/page4.c delete mode 100644 plugins/ExtraPlugins/setup/page5.c delete mode 100644 plugins/ExtraPlugins/setup/uninstall.c delete mode 100644 plugins/ExtraPlugins/setup/updater.c delete mode 100644 plugins/ExtraPlugins/setup/verify.c delete mode 100644 plugins/ExtraPlugins/wndtree.c 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 15bbc0c42f1f..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.16299.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 7a7d72bfec24..000000000000 --- a/plugins/ExtraPlugins/cloud.c +++ /dev/null @@ -1,373 +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; - ULONG xmlStringBufferLength = 0; - PSTR xmlStringBuffer = NULL; - PVOID rootJsonObject = NULL; - PWCT_CONTEXT context = Parameter; - - if (!(httpSessionHandle = WinHttpOpen( - L"ExtraPlugins_1.0", - 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 (!(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 = PhCreateJsonParser(xmlStringBuffer))) - goto CleanupExit; - - for (INT i = 0; i < PhGetJsonArrayLength(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 = PhGetJsonArrayIndexObject(rootJsonObject, i); - entry->Id = PhGetJsonValueAsString(jvalue, "plugin_id"); - entry->InternalName = PhGetJsonValueAsString(jvalue, "plugin_internal_name"); - entry->Name = PhGetJsonValueAsString(jvalue, "plugin_name"); - entry->Version = PhGetJsonValueAsString(jvalue, "plugin_version"); - entry->Author = PhGetJsonValueAsString(jvalue, "plugin_author"); - entry->Description = PhGetJsonValueAsString(jvalue, "plugin_description"); - entry->IconUrl = PhGetJsonValueAsString(jvalue, "plugin_icon"); - entry->Requirements = PhGetJsonValueAsString(jvalue, "plugin_requirements"); - entry->FeedbackUrl = PhGetJsonValueAsString(jvalue, "plugin_feedback"); - entry->Screenshots = PhGetJsonValueAsString(jvalue, "plugin_screenshots"); - entry->AddedTime = PhGetJsonValueAsString(jvalue, "plugin_datetime_added"); - entry->UpdatedTime = PhGetJsonValueAsString(jvalue, "plugin_datetime_updated"); - entry->Download_count = PhGetJsonValueAsString(jvalue, "plugin_download_count"); - entry->Download_link_32 = PhGetJsonValueAsString(jvalue, "plugin_download_link_32"); - entry->Download_link_64 = PhGetJsonValueAsString(jvalue, "plugin_download_link_64"); - entry->SHA2_32 = PhGetJsonValueAsString(jvalue, "plugin_hash_32"); - entry->SHA2_64 = PhGetJsonValueAsString(jvalue, "plugin_hash_64"); - entry->HASH_32 = PhGetJsonValueAsString(jvalue, "plugin_signed_32"); - entry->HASH_64 = PhGetJsonValueAsString(jvalue, "plugin_signed_64"); - entry->FileName = PhGetJsonValueAsString(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 (rootJsonObject) - PhFreeJsonParser(rootJsonObject); - - 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); - - PhCreateDirectory(fullSetupPath); - - 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)) - { - PhCreateDirectory(directoryPath); - - 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 2a1f9708ac07..000000000000 --- a/plugins/ExtraPlugins/dialog.c +++ /dev/null @@ -1,709 +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: - { - 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->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(); - - PhCreateSearchControl(hwndDlg, context->SearchHandle, L"Search Plugins (Ctrl+K)"); - context->NormalFontHandle = PhCreateCommonFont(-14, FW_NORMAL, NULL); - context->BoldFontHandle = PhCreateCommonFont(-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: - { - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - PPLUGIN_NODE selectedNode; - PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; - - 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); - } - - selectedItem = PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - contextMenuEvent->Location.x, - contextMenuEvent->Location.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 9d57f680ad6a..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_SetCheckState(Context->ListViewHandle, lvItemIndex, TRUE); - } - } - - 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 INDEXTOSTATEIMAGEMASK(2): // 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 INDEXTOSTATEIMAGEMASK(1): // 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 e9a8aff8c18c..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 - ))) - { - static 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; -} diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h deleted file mode 100644 index 36be49543ef1..000000000000 --- a/plugins/ExtraPlugins/main.h +++ /dev/null @@ -1,327 +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 -#include -#include "resource.h" - -#define IDD_WCT_MENUITEM 1000 -#define WM_ACTION (WM_APP + 601) -#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)) - -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 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 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 - ); -VOID ShowPluginUninstallWithoutPrompt( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// 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 d1298c84297f..000000000000 --- a/plugins/ExtraPlugins/plugin.c +++ /dev/null @@ -1,371 +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) - { - PH_FORMAT fileVersionFormat[7]; - - PhInitFormatU(&fileVersionFormat[0], info->dwFileVersionMS >> 16); - PhInitFormatC(&fileVersionFormat[1], '.'); - PhInitFormatU(&fileVersionFormat[2], info->dwFileVersionMS & 0xffff); - PhInitFormatC(&fileVersionFormat[3], '.'); - PhInitFormatU(&fileVersionFormat[4], info->dwFileVersionLS >> 16); - PhInitFormatC(&fileVersionFormat[5], '.'); - PhInitFormatU(&fileVersionFormat[6], info->dwFileVersionLS & 0xffff); - - version = PhFormat(fileVersionFormat, 7, 30); - } - } - - 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); - - 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 8cf8e4dcdd3a042f4cb9950a9efd00fe9ce8f5cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%3?x6Bmj(hUrvRT2S0Jrz6`wnSbH*^{ST+n&$gjr7NpKz9dug=. - */ - -#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); -} diff --git a/plugins/ExtraPlugins/setup/page4.c b/plugins/ExtraPlugins/setup/page4.c deleted file mode 100644 index 9f163d8c62cc..000000000000 --- a/plugins/ExtraPlugins/setup/page4.c +++ /dev/null @@ -1,78 +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); - - PhReferenceObject(context); - 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 17da99493fbd..000000000000 --- a/plugins/ExtraPlugins/setup/uninstall.c +++ /dev/null @@ -1,158 +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); - - if (PhGetIntegerSetting(L"EnableWarnings")) - ShowUninstallRestartDialog(context); - else - SendMessage(hwndDlg, WM_CLOSE, 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); -} - -HRESULT CALLBACK TaskDialogUninstallWithoutPromptCallback( - _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: - { - 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"); - - //PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupExtractBuild, context); - if (RtlDoesFileExists_U(PhGetString(fileNameString))) - { - MoveFileEx(PhGetString(fileNameString), PhGetString(bakNameString), MOVEFILE_REPLACE_EXISTING); - } - - PhDereferenceObject(bakNameString); - PhDereferenceObject(baseNameString); - PhDereferenceObject(fileNameString); - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - ShowUninstallRestartDialog(context); - } - { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - - return S_FALSE; - } - break; - } - - return S_OK; -} - -VOID ShowPluginUninstallWithoutPrompt( - _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"Uninstalling %s...", PhIsNullOrEmptyString(Context->Node->Name) ? PhGetStringOrEmpty(Context->Node->InternalName) : PhGetStringOrEmpty(Context->Node->Name))->Buffer; - config.dwFlags = TDF_USE_HICON_MAIN | TDF_CAN_BE_MINIMIZED | TDF_SHOW_MARQUEE_PROGRESS_BAR; - config.dwCommonButtons = TDCBF_CLOSE_BUTTON; - config.hMainIcon = context->IconLargeHandle; - config.pfCallback = TaskDialogUninstallWithoutPromptCallback; - 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 25fbd3ddb1f8..000000000000 --- a/plugins/ExtraPlugins/setup/updater.c +++ /dev/null @@ -1,678 +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 - ) -{ - PhDereferenceObject(Context); -} - -VOID TaskDialogCreateIcons( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - // Load the Process Hacker window icon - Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); - - // 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(); -} - -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; - BOOLEAN updateSuccess = FALSE; - HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - PPH_STRING downloadHostPath = NULL; - PPH_STRING downloadUrlPath = NULL; - PPH_STRING userAgentString = NULL; - PPH_STRING fileDownloadUrl = NULL; - PUPDATER_HASH_CONTEXT hashContext = NULL; - ULONG indexOfFileName = -1; - URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; - 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..."); - - context->SetupFilePath = PhCreateCacheFile(PhaFormatString( - L"%s.zip", - PhGetStringOrEmpty(context->Node->InternalName) - )); - - if (PhIsNullOrEmptyString(context->SetupFilePath)) - goto CleanupExit; - - 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; - } - - fileDownloadUrl = PhFormatString( - L"https://wj32.org/processhacker/plugins/download.php?id=%s&type=64", - PhGetStringOrEmpty(context->Node->Id) - ); - - // Set lengths to non-zero enabling these params to be cracked. - httpParts.dwSchemeLength = ULONG_MAX; - httpParts.dwHostNameLength = ULONG_MAX; - httpParts.dwUrlPathLength = ULONG_MAX; - - if (!WinHttpCrackUrl( - PhGetString(fileDownloadUrl), - 0, - 0, - &httpParts - )) - { - PhDereferenceObject(fileDownloadUrl); - goto CleanupExit; - } - - PhDereferenceObject(fileDownloadUrl); - - // Create the Host string. - if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( - httpParts.lpszHostName, - httpParts.dwHostNameLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - // Create the remote path string. - if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( - httpParts.lpszUrlPath, - httpParts.dwUrlPathLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - - 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 - ))) - { - 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), - httpParts.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) - { - goto CleanupExit; - } - - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - PhGetString(downloadUrlPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpParts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) - ))) - { - goto CleanupExit; - } - - WinHttpSetOption( - httpRequestHandle, - WINHTTP_OPTION_DISABLE_FEATURE, - &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, - 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(&downloadHostPath); - PhClearReference(&downloadUrlPath); - PhClearReference(&userAgentString); - - if (downloadSuccess && hashSuccess && signatureSuccess) - { - if (NT_SUCCESS(SetupExtractBuild(context))) - { - updateSuccess = TRUE; - } - } - - if (context->SetupFilePath) - { - PhDeleteCacheFile(context->SetupFilePath); - PhDereferenceObject(context->SetupFilePath); - } - - if (updateSuccess) - { - if (PhGetIntegerSetting(L"EnableWarnings")) - ShowUninstallRestartDialog(context); - else - SendMessage(context->DialogHandle, WM_CLOSE, 0, 0); - } - else - { - ShowUpdateFailedDialog(context, FALSE, FALSE); - } - - return STATUS_SUCCESS; -} - -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); - - switch (context->Action) - { - case PLUGIN_ACTION_INSTALL: - { - if (PhGetIntegerSetting(L"EnableWarnings")) - ShowAvailableDialog(context); - else - ShowProgressDialog(context); - } - break; - case PLUGIN_ACTION_UNINSTALL: - { - if (PhGetIntegerSetting(L"EnableWarnings")) - ShowPluginUninstallDialog(context); - else - ShowPluginUninstallWithoutPrompt(context); - } - break; - case PLUGIN_ACTION_RESTART: - { - if (PhGetIntegerSetting(L"EnableWarnings")) - { - ShowUninstallRestartDialog(context); - } - else - { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - } - 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 5c9858fd1b49..000000000000 --- a/plugins/ExtraPlugins/wndtree.c +++ /dev/null @@ -1,461 +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" - -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_CONTEXT_MENU contextMenuEvent = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, (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 = (PPLUGIN_NODE)customDraw->Node; - - switch (customDraw->Column->Id) - { - case TREE_COLUMN_ITEM_NAME: - { - PH_STRINGREF text; - SIZE nameSize; - SIZE textSize; - SIZE imageSize; - - imageSize.cx = PH_SCALE_DPI(17); - imageSize.cy = PH_SCALE_DPI(17); - - if (node->PluginOptions) - { - if (!node->Icon) - { - HBITMAP bitmapActive; - - if (bitmapActive = PhLoadPngImageFromResource(PluginInstance->DllBase, imageSize.cx, imageSize.cy, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) - { - node->Icon = CommonBitmapToIcon(bitmapActive, imageSize.cx, imageSize.cy); - DeleteObject(bitmapActive); - } - } - - if (node->Icon) - { - DrawIconEx( - customDraw->Dc, - rect.left + PH_SCALE_DPI(5), - rect.top + ((rect.bottom - rect.top) - imageSize.cy) / 2, - node->Icon, - imageSize.cx, - imageSize.cy, - 0, - NULL, - DI_NORMAL - ); - } - } - - rect.left += PH_SCALE_DPI(27); - 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)); - 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 = PhCreateCommonFont(-10, FW_NORMAL, NULL); - Context->TitleFontHandle = PhCreateCommonFont(-14, FW_BOLD, NULL); - - TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, Context); - TreeNew_SetRowHeight(TreeNewHandle, PH_SCALE_DPI(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 From cd68b46a757a3260410d81e1835a4ceaba726a9a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 2 Nov 2017 23:44:00 +1100 Subject: [PATCH 541/839] Add CommonUtil --- plugins/CommonUtil/CommonUtil.vcxitems | 23 ++ plugins/CommonUtil/http.c | 495 +++++++++++++++++++++++++ plugins/CommonUtil/http.h | 167 +++++++++ plugins/Plugins.sln | 21 +- 4 files changed, 696 insertions(+), 10 deletions(-) create mode 100644 plugins/CommonUtil/CommonUtil.vcxitems create mode 100644 plugins/CommonUtil/http.c create mode 100644 plugins/CommonUtil/http.h 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/http.c b/plugins/CommonUtil/http.c new file mode 100644 index 000000000000..e909ae5605c1 --- /dev/null +++ b/plugins/CommonUtil/http.c @@ -0,0 +1,495 @@ +/* + * Process Hacker - + * http socket 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 . + */ + +// dmex: This wrapper is utilizing a shared project until stable enough for inclusion with the base program. + +#include +#include +#include +#include + +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_ PWSTR Method, + _In_ PWSTR UrlPath, + _In_ ULONG Flags + ) +{ + ULONG httpFlags = 0; + + PhMapFlags1( + &httpFlags, + Flags, + PhpHttpRequestFlagMappings, + ARRAYSIZE(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_ PVOID RequestData, + _In_ ULONG RequestDataLength + ) +{ + return !!WinHttpSendRequest( + HttpContext->RequestHandle, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + RequestData, + RequestDataLength, + RequestDataLength, + 0 + ); +} + +BOOLEAN PhHttpSocketEndRequest( + _In_ PPH_HTTP_CONTEXT HttpContext + ) +{ + 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 : -1L, + 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, + ARRAYSIZE(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; + BYTE buffer[PAGE_SIZE]; + + allocatedLength = sizeof(buffer); + data = (PSTR)PhAllocate(allocatedLength); + dataLength = 0; + + while (WinHttpReadData(HttpContext->RequestHandle, 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); + } + + // Ensure that the buffer is null-terminated. + data[dataLength] = 0; + + if (Unicode) + result = PhConvertUtf8ToUtf16Ex(data, dataLength); + else + result = PhCreateBytesEx(data, dataLength); + + PhFree(data); + + return result; +} + +BOOLEAN PhHttpSocketSetFeature( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ ULONG Feature, + _In_ BOOLEAN Enable + ) +{ + ULONG featureValue = 0; + + PhMapFlags1( + &featureValue, + Feature, + PhpHttpFeatureMappings, + ARRAYSIZE(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; + + memset(&httpParts, 0, sizeof(URL_COMPONENTS)); + httpParts.dwStructSize = sizeof(URL_COMPONENTS); + httpParts.dwHostNameLength = ULONG_MAX; + httpParts.dwUrlPathLength = ULONG_MAX; + + if (!WinHttpCrackUrl( + Url->Buffer, + (ULONG)Url->Length / sizeof(WCHAR), + 0, + &httpParts + )) + { + return FALSE; + } + + if (HostPart && httpParts.dwHostNameLength) + *HostPart = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); + + if (PathPart && httpParts.dwUrlPathLength) + *PathPart = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); + + 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); + } + + // 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))); + } + + return message; +} \ No newline at end of file diff --git a/plugins/CommonUtil/http.h b/plugins/CommonUtil/http.h new file mode 100644 index 000000000000..5636f4f1523d --- /dev/null +++ b/plugins/CommonUtil/http.h @@ -0,0 +1,167 @@ +#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_ PWSTR Method, + _In_ PWSTR UrlPath, + _In_ ULONG Flags + ); + +BOOLEAN +NTAPI +PhHttpSocketSendRequest( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PVOID RequestData, + _In_ 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_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 + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 87cbceb668f5..6dd3d8b25eff 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.16 +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 @@ -32,9 +32,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserNotes", "UserNotes\User EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HardwareDevices", "HardwareDevices\HardwareDevices.vcxproj", "{5F0D72C4-8319-4B61-9E13-6084B680EB90}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtraPlugins", "ExtraPlugins\ExtraPlugins.vcxproj", "{96549A3E-D1BD-470E-A5B3-A64803C4DEF5}" +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 @@ -130,16 +136,11 @@ Global {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 - {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 + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F936C56D-6A19-47EB-973F-535DF252210E} + EndGlobalSection EndGlobal From 7c92c16d9e93686e3d07fffd62157ff7d9daf316 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 01:59:53 +1100 Subject: [PATCH 542/839] Updater: Use http wrapper --- plugins/Updater/Updater.vcxproj | 3 + plugins/Updater/page5.c | 24 +-- plugins/Updater/updater.c | 354 +++++++------------------------- plugins/Updater/updater.h | 7 +- 4 files changed, 89 insertions(+), 299 deletions(-) diff --git a/plugins/Updater/Updater.vcxproj b/plugins/Updater/Updater.vcxproj index 1bb143343361..a906bc3ca17c 100644 --- a/plugins/Updater/Updater.vcxproj +++ b/plugins/Updater/Updater.vcxproj @@ -47,6 +47,9 @@ v141 + + + diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index aac9d196786f..3cf00b67ff49 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -213,7 +213,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) { @@ -227,22 +227,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 { diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index ea940d5b0030..6e593160c14f 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2011-2016 dmex + * Copyright (C) 2011-2017 dmex * * This file is part of Process Hacker. * @@ -21,8 +21,6 @@ */ #include "updater.h" -#include -#include HWND UpdateDialogHandle = NULL; HANDLE UpdateDialogThreadHandle = NULL; @@ -87,18 +85,6 @@ VOID TaskDialogLinkClicked( } } -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 ) @@ -248,174 +234,78 @@ ULONG64 ParseVersionString( ); } -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; - ULONG stringBufferLength = 0; - PSTR stringBuffer = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_BYTES jsonString = NULL; PVOID jsonObject = NULL; - mxml_node_t* xmlNode = NULL; - PPH_STRING versionHeader = UpdateVersionString(); - PPH_STRING windowsHeader = UpdateWindowsString(); - PSTR tempValue = 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 - ))) + + if (!PhHttpSocketCreate(&httpContext, NULL)) { 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 - ))) + if (!PhHttpSocketConnect( + httpContext, + L"wj32.org", + PH_HTTP_DEFAULT_HTTPS_PORT + )) { Context->ErrorCode = GetLastError(); goto CleanupExit; } - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - L"/processhacker/nightly.php?phupdater", - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE - ))) + if (!PhHttpSocketBeginRequest( + httpContext, + NULL, + L"/processhacker/nightly.php?phupdater", + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) { Context->ErrorCode = GetLastError(); goto CleanupExit; } - - 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 - ); - } + PPH_STRING versionHeader; + PPH_STRING windowsHeader; - if (windowsHeader) - { - WinHttpAddRequestHeaders( - httpRequestHandle, - windowsHeader->Buffer, - (ULONG)windowsHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); + 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 (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) { Context->ErrorCode = GetLastError(); goto CleanupExit; } - if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) + if (!PhHttpSocketEndRequest(httpContext)) { Context->ErrorCode = GetLastError(); goto CleanupExit; } - if (!ReadRequestString(httpRequestHandle, &stringBuffer, &stringBufferLength)) - goto CleanupExit; - - if (stringBuffer == NULL || stringBuffer[0] == '\0') + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + Context->ErrorCode = GetLastError(); goto CleanupExit; + } - if (!(jsonObject = PhCreateJsonParser(stringBuffer))) + if (!(jsonObject = PhCreateJsonParser(jsonString->Buffer))) goto CleanupExit; Context->Version = PhGetJsonValueAsString(jsonObject, "version"); @@ -447,25 +337,13 @@ BOOLEAN QueryUpdateData( CleanupExit: - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + if (jsonString) + PhDereferenceObject(jsonString); - if (xmlNode) - mxmlDelete(xmlNode); - - if (stringBuffer) - PhFree(stringBuffer); - - PhClearReference(&versionHeader); - PhClearReference(&windowsHeader); - - if (!PhIsNullOrEmptyString(Context->BuildMessage)) + if (success && !PhIsNullOrEmptyString(Context->BuildMessage)) { PH_STRING_BUILDER sb; @@ -629,14 +507,11 @@ NTSTATUS UpdateDownloadThread( BOOLEAN hashSuccess = FALSE; BOOLEAN signatureSuccess = FALSE; HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; PPH_STRING downloadHostPath = NULL; PPH_STRING downloadUrlPath = NULL; - PPH_STRING userAgentString = NULL; PUPDATER_HASH_CONTEXT hashContext = NULL; - URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; + USHORT httpPort = 0; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; ULONG64 timeTicks = 0; @@ -645,51 +520,19 @@ NTSTATUS UpdateDownloadThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); - // Create a user agent string. - userAgentString = PhFormatString( - L"PH_%s", - PhGetStringOrEmpty(context->CurrentVersionString) - ); - if (PhIsNullOrEmptyString(userAgentString)) - goto CleanupExit; - - // Set lengths to non-zero enabling these params to be cracked. - httpParts.dwSchemeLength = ULONG_MAX; - httpParts.dwHostNameLength = ULONG_MAX; - httpParts.dwUrlPathLength = ULONG_MAX; - - if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->SetupFileDownloadUrl), - 0, - 0, - &httpParts + if (!PhHttpSocketParseUrl( + context->SetupFileDownloadUrl, + &downloadHostPath, + &downloadUrlPath, + &httpPort )) { context->ErrorCode = GetLastError(); goto CleanupExit; } - // Create the Host string. - if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( - httpParts.lpszHostName, - httpParts.dwHostNameLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - // Create the remote path string. - if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( - httpParts.lpszUrlPath, - httpParts.dwUrlPathLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - // Create the local path string. context->SetupFilePath = UpdaterParseDownloadFileName(downloadUrlPath); - if (PhIsNullOrEmptyString(context->SetupFilePath)) goto CleanupExit; @@ -709,72 +552,36 @@ NTSTATUS UpdateDownloadThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - // Open the HTTP session with the system proxy configuration if available - 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 - ))) + if (!PhHttpSocketCreate(&httpContext, NULL)) { 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), - httpParts.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) + if (!PhHttpSocketConnect( + httpContext, + PhGetString(downloadHostPath), + httpPort + )) { context->ErrorCode = GetLastError(); goto CleanupExit; } - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - PhGetString(downloadUrlPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpParts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) - ))) + 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; } - WinHttpSetOption( - httpRequestHandle, - WINHTTP_OPTION_DISABLE_FEATURE, - &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, - 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)) { context->ErrorCode = GetLastError(); goto CleanupExit; @@ -782,7 +589,7 @@ NTSTATUS UpdateDownloadThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response..."); - if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) + if (!PhHttpSocketEndRequest(httpContext)) { context->ErrorCode = GetLastError(); goto CleanupExit; @@ -791,7 +598,6 @@ NTSTATUS UpdateDownloadThread( { ULONG bytesDownloaded = 0; ULONG downloadedBytes = 0; - ULONG contentLengthSize = sizeof(ULONG); ULONG contentLength = 0; PPH_STRING status; IO_STATUS_BLOCK isb; @@ -803,16 +609,10 @@ NTSTATUS UpdateDownloadThread( 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 + if (!PhHttpSocketQueryHeaderUlong( + httpContext, + PH_HTTP_QUERY_CONTENT_LENGTH, + &contentLength )) { context->ErrorCode = GetLastError(); @@ -826,8 +626,11 @@ NTSTATUS UpdateDownloadThread( // Zero the buffer. memset(buffer, 0, PAGE_SIZE); + // Start the clock. + PhQuerySystemTime(&timeStart); + // Download the data. - 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) @@ -912,30 +715,21 @@ NTSTATUS UpdateDownloadThread( CleanupExit: + if (httpContext) + PhHttpSocketDestroy(httpContext); + if (hashContext) UpdaterDestroyHash(hashContext); if (tempFileHandle) NtClose(tempFileHandle); - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - if (downloadHostPath) PhDereferenceObject(downloadHostPath); if (downloadUrlPath) PhDereferenceObject(downloadUrlPath); - if (userAgentString) - WinHttpCloseHandle(userAgentString); - if (UpdateDialogThreadHandle) { if (downloadSuccess && hashSuccess && signatureSuccess) diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index b207301f2ab2..ebc867f041a1 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -29,12 +29,15 @@ #include #include #include -#include #include #include #include + #include -#include +#include + +#include +#include #include "resource.h" From 4b266416d5add7a4552fefed0e21ad60c092e2ce Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 02:00:24 +1100 Subject: [PATCH 543/839] Update exports --- ProcessHacker/ProcessHacker.def | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index b0fbedefeb2e..f36b044488fa 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -359,6 +359,7 @@ EXPORTS PhGetKnownLocation PhGetMessage PhGetNtMessage + PhGetStatusMessage PhGetSystemDirectory PhGetSystemRoot PhGetWin32Message @@ -629,6 +630,7 @@ EXPORTS PhGetJsonObjectLength PhGetJsonObjectBool PhAddJsonObject + PhGetJsonObjectAsArrayList PhCreateJsonArray PhAddJsonArrayObject PhGetJsonArrayString From 5bc8e1400175214c4b372d96bb0a1eaeaa5fffe9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 02:03:22 +1100 Subject: [PATCH 544/839] NetworkTools: Use http wrapper --- plugins/NetworkTools/NetworkTools.rc | 4 +- plugins/NetworkTools/NetworkTools.vcxproj | 3 + plugins/NetworkTools/nettools.h | 5 +- plugins/NetworkTools/update.c | 325 ++++++---------------- 4 files changed, 93 insertions(+), 244 deletions(-) diff --git a/plugins/NetworkTools/NetworkTools.rc b/plugins/NetworkTools/NetworkTools.rc index 66fdaa394f83..ba5d9ab2068c 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -93,7 +93,7 @@ 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 + CONTROL "",IDC_NETOUTPUTEDIT,"RICHEDIT50W",WS_VSCROLL | WS_TABSTOP | 0x4,2,2,387,212 END IDD_OPTIONS DIALOGEX 0, 0, 201, 43 @@ -648,9 +648,7 @@ BEGIN IDD_WHOIS, DIALOG BEGIN LEFTMARGIN, 2 - RIGHTMARGIN, 387 TOPMARGIN, 2 - BOTTOMMARGIN, 212 END IDD_OPTIONS, DIALOG diff --git a/plugins/NetworkTools/NetworkTools.vcxproj b/plugins/NetworkTools/NetworkTools.vcxproj index 64ed76316c01..2df98cd00960 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj +++ b/plugins/NetworkTools/NetworkTools.vcxproj @@ -47,6 +47,9 @@ v141 + + + diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index b00ba77ec247..4255eaa3d2f9 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -26,22 +26,23 @@ #define CINTERFACE #define COBJMACROS -#include #include #include #include #include #include + #include #include #include #include #include #include -#include #include #include +#include + #include "resource.h" #define PLUGIN_NAME L"ProcessHacker.NetworkTools" diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index e53977a5e2b8..e30b76700eb9 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -34,16 +34,19 @@ 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); - //PhClearReference(&Context); + if (Context->FileDownloadUrl) + PhDereferenceObject(Context->FileDownloadUrl); + + if (Context->FileDownloadUrl) + PhDereferenceObject(Context->FileDownloadUrl); + + if (Context->FileDownloadUrl) + PhDereferenceObject(Context->FileDownloadUrl); + + if (Context->FileDownloadUrl) + PhDereferenceObject(Context->FileDownloadUrl); + + PhDereferenceObject(Context); } VOID TaskDialogCreateIcons( @@ -65,7 +68,7 @@ VOID TaskDialogLinkClicked( } PPH_STRING UpdateVersionString( - _In_ PWSTR UserAgent + VOID ) { ULONG majorVersion; @@ -78,7 +81,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); } @@ -126,155 +129,61 @@ PPH_STRING QueryFwLinkUrl( ) { PPH_STRING redirectUrl = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - 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; - 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; - } + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); - } + if (!PhHttpSocketCreate(&httpContext, PhGetString(userAgentString))) + goto CleanupExit; - 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) + if (versionHeader = UpdateVersionString()) { - WinHttpAddRequestHeaders( - httpRequestHandle, - versionHeader->Buffer, - (ULONG)versionHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); + PhHttpSocketAddRequestHeaders(httpContext, versionHeader->Buffer, (ULONG)versionHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(versionHeader); } - if (windowsHeader) + if (windowsHeader = UpdateWindowsString()) { - WinHttpAddRequestHeaders( - httpRequestHandle, - windowsHeader->Buffer, - (ULONG)windowsHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); + PhHttpSocketAddRequestHeaders(httpContext, windowsHeader->Buffer, (ULONG)windowsHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(windowsHeader); } - WinHttpSetOption( - httpRequestHandle, - WINHTTP_OPTION_DISABLE_FEATURE, - &(ULONG){ WINHTTP_DISABLE_REDIRECTS }, - sizeof(ULONG) - ); - - 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 - )) - { + 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; } @@ -285,19 +194,18 @@ NTSTATUS GeoIPUpdateThread( { BOOLEAN success = FALSE; HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; PPH_STRING fwLinkUrl = NULL; - PPH_STRING downloadHostPath = NULL; - PPH_STRING downloadUrlPath = NULL; - URL_COMPONENTS httpParts = { sizeof(URL_COMPONENTS) }; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING httpHostName = NULL; + PPH_STRING httpHostPath = 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..."); @@ -305,10 +213,10 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; context->SetupFilePath = PhCreateCacheFile(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); + if (PhIsNullOrEmptyString(context->SetupFilePath)) goto CleanupExit; - // Create output file if (!NT_SUCCESS(PhCreateFileWin32( &tempFileHandle, PhGetString(context->SetupFilePath), @@ -324,112 +232,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. - httpParts.dwSchemeLength = ULONG_MAX; - httpParts.dwHostNameLength = ULONG_MAX; - httpParts.dwUrlPathLength = ULONG_MAX; - - if (!WinHttpCrackUrl( - fwLinkUrl->Buffer, - 0, - 0, - &httpParts + if (!PhHttpSocketParseUrl( + fwLinkUrl, + &httpHostName, + &httpHostPath, + &httpHostPort )) { goto CleanupExit; } - // Create the Host string. - if (PhIsNullOrEmptyString(downloadHostPath = PhCreateStringEx( - httpParts.lpszHostName, - httpParts.dwHostNameLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); - // Create the remote path string. - if (PhIsNullOrEmptyString(downloadUrlPath = PhCreateStringEx( - httpParts.lpszUrlPath, - httpParts.dwUrlPathLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - 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 - ))) + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgentString) + )) { 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), - httpParts.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, - NULL, - PhGetString(downloadUrlPath), + if (!PhHttpSocketBeginRequest( + httpContext, NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpParts.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; } - WinHttpSetOption( - httpRequestHandle, - WINHTTP_OPTION_DISABLE_FEATURE, - &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, - 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; @@ -442,21 +295,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) @@ -610,17 +461,13 @@ NTSTATUS GeoIPUpdateThread( if (tempFileHandle) NtClose(tempFileHandle); - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); if (userAgentString) PhDereferenceObject(userAgentString); + if (versionString) + PhDereferenceObject(versionString); if (context->SetupFilePath) { From 732a141a1f0d3734aeb616669f04d6d35c52db17 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 04:10:26 +1100 Subject: [PATCH 545/839] Add workaround for PhGetMessage and insert sequences --- phlib/util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phlib/util.c b/phlib/util.c index 7d7f4950e9a0..379d835d2620 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -280,6 +280,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); From b2ed625dea4072e7db6004898e451a673274990e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 04:11:49 +1100 Subject: [PATCH 546/839] NetworkTools: Add missing file --- plugins/NetworkTools/update.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index e30b76700eb9..774069ad8e55 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -37,14 +37,14 @@ VOID FreeUpdateContext( if (Context->FileDownloadUrl) PhDereferenceObject(Context->FileDownloadUrl); - if (Context->FileDownloadUrl) - PhDereferenceObject(Context->FileDownloadUrl); + if (Context->RevVersion) + PhDereferenceObject(Context->RevVersion); - if (Context->FileDownloadUrl) - PhDereferenceObject(Context->FileDownloadUrl); + if (Context->Size) + PhDereferenceObject(Context->Size); - if (Context->FileDownloadUrl) - PhDereferenceObject(Context->FileDownloadUrl); + if (Context->SetupFilePath) + PhDereferenceObject(Context->SetupFilePath); PhDereferenceObject(Context); } From 60a10900b917e9a03c98b762dbc708df1a2037a3 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 13:10:55 +1100 Subject: [PATCH 547/839] OnlineChecks: Use http wrapper --- plugins/OnlineChecks/OnlineChecks.rc | 22 + plugins/OnlineChecks/OnlineChecks.vcxproj | 3 + plugins/OnlineChecks/main.c | 3 +- plugins/OnlineChecks/onlnchk.h | 105 ++-- plugins/OnlineChecks/page2.c | 4 +- plugins/OnlineChecks/resource.h | 8 +- plugins/OnlineChecks/upload.c | 527 ++++++++------------ plugins/OnlineChecks/virustotal.c | 563 ++++++++++++---------- 8 files changed, 631 insertions(+), 604 deletions(-) diff --git a/plugins/OnlineChecks/OnlineChecks.rc b/plugins/OnlineChecks/OnlineChecks.rc index 0cdef0b36dac..e5f2b6e780c4 100644 --- a/plugins/OnlineChecks/OnlineChecks.rc +++ b/plugins/OnlineChecks/OnlineChecks.rc @@ -103,6 +103,15 @@ BEGIN 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -119,6 +128,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 80 END + + IDD_VIRUSTOTAL, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 257 + TOPMARGIN, 2 + BOTTOMMARGIN, 259 + END END #endif // APSTUDIO_INVOKED @@ -133,6 +150,11 @@ BEGIN 0 END +IDD_VIRUSTOTAL AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/plugins/OnlineChecks/OnlineChecks.vcxproj b/plugins/OnlineChecks/OnlineChecks.vcxproj index f6a7ecd4e1b2..a041a2316bb4 100644 --- a/plugins/OnlineChecks/OnlineChecks.vcxproj +++ b/plugins/OnlineChecks/OnlineChecks.vcxproj @@ -47,6 +47,9 @@ v141 + + + diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 6f9677e1b99c..cbc39b076775 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -720,7 +720,8 @@ LOGICAL DllMain( PH_SETTING_CREATE settings[] = { { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, L"0" }, - { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS, L"0" } + { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS, L"0" }, + { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_DEFAULT_ACTION, L"0" } }; PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 808f1282d8b2..1940fc053220 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -29,19 +29,21 @@ #include #include #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) @@ -55,7 +57,7 @@ typedef struct _SERVICE_INFO { ULONG Id; PWSTR HostName; - INTERNET_PORT HostPort; + USHORT HostPort; ULONG HostFlags; PWSTR UploadObjectName; PWSTR FileNameFieldName; @@ -124,7 +126,7 @@ typedef struct _UPLOAD_CONTEXT HANDLE UploadThreadHandle; HICON IconLargeHandle; HICON IconSmallHandle; - HINTERNET HttpHandle; + ITaskbarList3* TaskbarListClass; PPH_STRING FileSize; @@ -141,6 +143,9 @@ typedef struct _UPLOAD_CONTEXT PPH_STRING LastAnalysisDate; PPH_STRING LastAnalysisUrl; PPH_STRING LastAnalysisAgo; + + PPH_STRING FileHash; + } UPLOAD_CONTEXT, *PUPLOAD_CONTEXT; // options.c @@ -160,6 +165,10 @@ NTSTATUS UploadCheckThreadStart( _In_ PVOID Parameter ); +NTSTATUS UploadRecheckThreadStart( + _In_ PVOID Parameter + ); + VOID ShowVirusTotalUploadDialog( _In_ PUPLOAD_CONTEXT Context ); @@ -176,32 +185,6 @@ 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 @@ -259,6 +242,68 @@ PPH_BYTES VirusTotalGetCachedDbHash( VOID ); +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 + ); + +PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( + _In_ PPH_STRING FileHash + ); + + VOID InitializeVirusTotalProcessMonitor( VOID ); diff --git a/plugins/OnlineChecks/page2.c b/plugins/OnlineChecks/page2.c index aff040808c29..dbee6f4d88b5 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) diff --git a/plugins/OnlineChecks/resource.h b/plugins/OnlineChecks/resource.h index 6b43b8c86cbf..bc4a9fd77e37 100644 --- a/plugins/OnlineChecks/resource.h +++ b/plugins/OnlineChecks/resource.h @@ -3,19 +3,23 @@ // 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 103 +#define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1013 +#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 77a6f619f96c..4c6309239c69 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -21,98 +21,30 @@ */ #include "onlnchk.h" -#include #include PPH_OBJECT_TYPE UploadContextType = NULL; PH_INITONCE UploadContextTypeInitOnce = PH_INITONCE_INIT; SERVICE_INFO UploadServiceInfo[] = { - { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"???", L"file" }, - { MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, 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[]" }, - { MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, + { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"???", L"file" }, + { MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"???", L"file" }, + { MENUITEM_JOTTI_UPLOAD, L"virusscan.jotti.org", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, + { MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_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(PhGetDllHandle(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) + if (message = PhHttpSocketGetErrorMessage(ErrorCode)) { PhMoveReference(&Context->ErrorString, PhFormatString( L"[%lu] %s", @@ -132,7 +64,6 @@ VOID RaiseUploadError( } PostMessage(Context->DialogHandle, UM_ERROR, 0, 0); - } PSERVICE_INFO GetUploadServiceInfo( @@ -169,12 +100,6 @@ VOID UploadContextDeleteProcedure( context->UploadThreadHandle = NULL; } - if (context->HttpHandle) - { - WinHttpCloseHandle(context->HttpHandle); - context->HttpHandle = NULL; - } - PhClearReference(&context->ErrorString); PhClearReference(&context->FileName); PhClearReference(&context->BaseFileName); @@ -329,104 +254,75 @@ NTSTATUS HashFileAndResetPosition( PPH_BYTES PerformSubRequest( _In_ PUPLOAD_CONTEXT Context, _In_ PWSTR HostName, - _In_ INTERNET_PORT HostPort, + _In_ USHORT HostPort, _In_ ULONG HostFlags, _In_ PWSTR ObjectName ) { PPH_BYTES result = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING phVersion = NULL; + PPH_STRING userAgent = NULL; + + // Create a user agent string. + phVersion = PhGetPhVersion(); + userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - if (!(connectHandle = WinHttpConnect( - Context->HttpHandle, + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgent) + )) + { + RaiseUploadError(Context, L"Unable to create the http socket.", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketConnect( + httpContext, HostName, - HostPort, - 0 - ))) + HostPort + )) { - RaiseUploadError(Context, L"Unable to connect to the service", GetLastError()); + RaiseUploadError(Context, L"Unable to connect to the service.", GetLastError()); goto CleanupExit; } - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, + if (!PhHttpSocketBeginRequest( + httpContext, NULL, ObjectName, - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | HostFlags - ))) + PH_HTTP_FLAG_REFRESH | HostFlags + )) { - RaiseUploadError(Context, L"Unable to create the request", GetLastError()); + 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 - )) + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) { - RaiseUploadError(Context, L"Unable to send the request", GetLastError()); + RaiseUploadError(Context, L"Unable to send the request.", GetLastError()); goto CleanupExit; } - if (WinHttpReceiveResponse(requestHandle, NULL)) + if (!PhHttpSocketEndRequest(httpContext)) { - 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); + RaiseUploadError(Context, L"Unable to receive the request.", GetLastError()); + goto CleanupExit; } - else + + if (!(result = PhHttpSocketDownloadString(httpContext, FALSE))) { - RaiseUploadError(Context, L"Unable to receive the response", GetLastError()); + RaiseUploadError(Context, L"Unable to download the response.", GetLastError()); goto CleanupExit; } CleanupExit: - if (requestHandle) - WinHttpCloseHandle(requestHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); - if (connectHandle) - WinHttpCloseHandle(connectHandle); + PhClearReference(&phVersion); + PhClearReference(&userAgent); return result; } @@ -437,7 +333,6 @@ NTSTATUS UploadFileThreadStart( { NTSTATUS status = STATUS_SUCCESS; ULONG httpStatus = 0; - ULONG httpStatusLength = sizeof(ULONG); ULONG httpPostSeed = 0; ULONG totalUploadLength = 0; ULONG totalUploadedLength = 0; @@ -449,12 +344,13 @@ NTSTATUS UploadFileThreadStart( 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_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; @@ -466,39 +362,6 @@ NTSTATUS UploadFileThreadStart( serviceInfo = GetUploadServiceInfo(context->Service); - // Set lengths to non-zero enabling these params to be cracked. - httpComponents.dwSchemeLength = ULONG_MAX; - httpComponents.dwHostNameLength = ULONG_MAX; - httpComponents.dwUrlPathLength = ULONG_MAX; - - if (!WinHttpCrackUrl( - PhGetString(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, PhGetString(context->FileName), @@ -513,26 +376,47 @@ NTSTATUS UploadFileThreadStart( goto CleanupExit; } - if (!(connectHandle = WinHttpConnect( - context->HttpHandle, - serviceInfo->HostName, - serviceInfo->HostPort, - 0 - ))) + // 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 (!(requestHandle = WinHttpOpenRequest( - connectHandle, + if (!PhHttpSocketBeginRequest( + httpContext, L"POST", PhGetString(httpHostPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | serviceInfo->HostFlags - ))) + 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; @@ -618,11 +502,10 @@ NTSTATUS UploadFileThreadStart( } // add headers - if (!WinHttpAddRequestHeaders( - requestHandle, + if (!PhHttpSocketAddRequestHeaders( + httpContext, httpRequestHeaders.String->Buffer, - -1L, - WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD + (ULONG)httpRequestHeaders.String->Length / sizeof(WCHAR) )) { RaiseUploadError(context, L"Unable to add request headers", GetLastError()); @@ -633,11 +516,10 @@ NTSTATUS UploadFileThreadStart( { PPH_STRING ajaxHeader = PhCreateString(L"X-Requested-With: XMLHttpRequest"); - WinHttpAddRequestHeaders( - requestHandle, + PhHttpSocketAddRequestHeaders( + httpContext, ajaxHeader->Buffer, - (ULONG)ajaxHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD + (ULONG)ajaxHeader->Length / sizeof(WCHAR) ); PhDereferenceObject(ajaxHeader); @@ -647,15 +529,7 @@ NTSTATUS UploadFileThreadStart( 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 - )) + if (!PhHttpSocketSendRequest(httpContext, NULL, totalUploadLength)) { RaiseUploadError(context, L"Unable to send the request", GetLastError()); goto CleanupExit; @@ -669,8 +543,8 @@ NTSTATUS UploadFileThreadStart( PhQuerySystemTime(&timeStart); // Write the header - if (!WinHttpWriteData( - requestHandle, + if (!PhHttpSocketWriteData( + httpContext, asciiPostData->Buffer, (ULONG)asciiPostData->Length, &totalPostHeaderWritten @@ -716,7 +590,7 @@ NTSTATUS UploadFileThreadStart( break; } - if (!WinHttpWriteData(requestHandle, buffer, (ULONG)isb.Information, &totalWriteLength)) + if (!PhHttpSocketWriteData(httpContext, buffer, (ULONG)isb.Information, &totalWriteLength)) { RaiseUploadError(context, L"Unable to upload the file data", GetLastError()); goto CleanupExit; @@ -762,8 +636,8 @@ NTSTATUS UploadFileThreadStart( } // Write the footer bytes - if (!WinHttpWriteData( - requestHandle, + if (!PhHttpSocketWriteData( + httpContext, asciiFooterData->Buffer, (ULONG)asciiFooterData->Length, &totalPostFooterWritten @@ -773,99 +647,92 @@ NTSTATUS UploadFileThreadStart( goto CleanupExit; } - if (!WinHttpReceiveResponse(requestHandle, NULL)) + if (!PhHttpSocketEndRequest(httpContext)) { 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 + if (!PhHttpSocketQueryHeaderUlong( + httpContext, + PH_HTTP_QUERY_STATUS_CODE, + &httpStatus )) { 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) + if (httpStatus == PH_HTTP_STATUS_OK || httpStatus == PH_HTTP_STATUS_REDIRECT_METHOD || httpStatus == PH_HTTP_STATUS_REDIRECT) { switch (context->Service) { case MENUITEM_VIRUSTOTAL_UPLOAD: case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: { - PSTR buffer = NULL; - ULONG bufferLength; + PPH_BYTES jsonString; PVOID jsonRootObject; - if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) { RaiseUploadError(context, L"Unable to complete the request", GetLastError()); goto CleanupExit; } - if (jsonRootObject = PhCreateJsonParser(buffer)) + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) { - if (PhGetJsonValueAsLong64(jsonRootObject, "response_code") != 0) - goto CleanupExit; - - // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "resource")); - // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "scan_id")); - // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "sha256")); - // PhZeroExtendToUtf16(PhGetJsonValueAsString(jsonRootObject, "verbose_msg")); + INT64 errorCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + //PhGetJsonValueAsString(jsonRootObject, "scan_id"); + //PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); - PhMoveReference(&context->LaunchCommand, PhGetJsonValueAsString(jsonRootObject, "permalink")); + if (errorCode != 1) + { + RaiseUploadError(context, L"VirusTotal API error", (ULONG)errorCode); + PhDereferenceObject(jsonString); + goto CleanupExit; + } + else + { + PhMoveReference(&context->LaunchCommand, PhGetJsonValueAsString(jsonRootObject, "permalink")); + } PhFreeJsonParser(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); - } + RaiseUploadError(context, L"Unable to complete the request", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + goto CleanupExit; } + + PhDereferenceObject(jsonString); } break; case MENUITEM_JOTTI_UPLOAD: case MENUITEM_JOTTI_UPLOAD_SERVICE: { - PSTR buffer = NULL; - ULONG bufferLength; - PVOID rootJsonObject; + PPH_BYTES jsonString; + PVOID jsonRootObject; - if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) { RaiseUploadError(context, L"Unable to complete the request", GetLastError()); goto CleanupExit; } - if (rootJsonObject = PhCreateJsonParser(buffer)) + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) { PPH_STRING redirectUrl; - if (redirectUrl = PhGetJsonValueAsString(rootJsonObject, "redirecturl")) + if (redirectUrl = PhGetJsonValueAsString(jsonRootObject, "redirecturl")) { PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%s", redirectUrl->Buffer)); PhDereferenceObject(redirectUrl); } - PhFreeJsonParser(rootJsonObject); + PhFreeJsonParser(jsonRootObject); } + + PhDereferenceObject(jsonString); } break; } @@ -929,17 +796,11 @@ NTSTATUS UploadCheckThreadStart( { 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; PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; //context->Extension = VirusTotalGetCachedResult(context->FileName); @@ -988,51 +849,28 @@ NTSTATUS UploadCheckThreadStart( context->TotalFileLength = fileSize64.LowPart; } - // Create a user agent string. - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - - if (!(context->HttpHandle = WinHttpOpen( - userAgent->Buffer, - 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( - context->HttpHandle, - WINHTTP_OPTION_DECOMPRESSION, - &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_ALL }, - sizeof(ULONG) - ); - } - switch (context->Service) { 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, &hashString))) + if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, Sha256HashAlgorithm, &tempHashString))) { RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); goto CleanupExit; } - subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", hashString->Buffer); + context->FileHash = tempHashString; + subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", PhGetString(context->FileHash)); PhMoveReference(&context->LaunchCommand, PhFormatString( L"https://www.virustotal.com/file/%s/analysis/", - PhGetString(hashString) + PhGetString(context->FileHash) )); if (!(subRequestBuffer = PerformSubRequest( @@ -1048,9 +886,7 @@ NTSTATUS UploadCheckThreadStart( if (rootJsonObject = PhCreateJsonParser(subRequestBuffer->Buffer)) { - context->FileExists = PhGetJsonObjectBool(rootJsonObject, "file_exists"); - - if (context->FileExists) + if (context->FileExists = PhGetJsonObjectBool(rootJsonObject, "file_exists")) { INT64 detected = 0; INT64 detectedMax = 0; @@ -1088,11 +924,11 @@ NTSTATUS UploadCheckThreadStart( PPH_BYTES resource = VirusTotalGetCachedDbHash(); PhMoveReference(&context->UploadUrl, PhFormatString( - L"%s%s?apikey=%S&resource=%s", + 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(hashString) + PhGetString(context->FileHash) )); PhClearReference(&resource); @@ -1100,9 +936,12 @@ NTSTATUS UploadCheckThreadStart( if (!PhIsNullOrEmptyString(context->UploadUrl)) { - success = TRUE; PostMessage(context->DialogHandle, UM_EXISTS, 0, 0); } + else + { + RaiseUploadError(context, L"Unable to parse the UploadUrl.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } } else { @@ -1111,13 +950,20 @@ NTSTATUS UploadCheckThreadStart( // No file found... Start the upload. if (!PhIsNullOrEmptyString(context->UploadUrl)) { - success = TRUE; PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); } + else + { + RaiseUploadError(context, L"Received invalid response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } } PhFreeJsonParser(rootJsonObject); } + else + { + RaiseUploadError(context, L"Unable to parse the response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } } break; case MENUITEM_JOTTI_UPLOAD: @@ -1129,31 +975,20 @@ NTSTATUS UploadCheckThreadStart( // No file found... Start the upload. if (!PhIsNullOrEmptyString(context->UploadUrl)) { - success = TRUE; PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); } + else + { + RaiseUploadError(context, L"Unable to parse the response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } } 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); + PhClearReference(&subRequestBuffer); if (fileHandle) NtClose(fileHandle); @@ -1163,6 +998,34 @@ NTSTATUS UploadCheckThreadStart( return status; } +NTSTATUS UploadRecheckThreadStart( + _In_ PVOID Parameter + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + PVIRUSTOTAL_API_RESPONSE response; + + response = VirusTotalRequestFileReScan(context->FileHash); + + if (response->ResponseCode == 1) + PhMoveReference(&context->ReAnalyseUrl, response->PermaLink); + else + RaiseUploadError(context, L"VirusTotal API error", (ULONG)response->ResponseCode); + + if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) + { + PhShellExecute(NULL, PhGetString(context->ReAnalyseUrl), NULL); + //PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0); + } + else + { + RaiseUploadError(context, L"Unable to complete the ReAnalyse request (please try again after a few minutes)", ERROR_INVALID_DATA); + } + + return STATUS_SUCCESS; +} + + LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -1190,7 +1053,33 @@ LRESULT CALLBACK TaskDialogSubclassProc( break; case UM_EXISTS: { - ShowFileFoundDialog(context); + switch (PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_DEFAULT_ACTION)) + { + //default: + case 1: + { + ShowVirusTotalProgressDialog(context); + } + break; + case 2: + { + if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) + PhShellExecute(hwndDlg, PhGetString(context->ReAnalyseUrl), NULL); + } + break; + case 3: + { + if (!PhIsNullOrEmptyString(context->LaunchCommand)) + { + PhShellExecute(hwndDlg, PhGetString(context->LaunchCommand), NULL); + } + } + break; + default: + { + ShowFileFoundDialog(context); + } + } } break; case UM_LAUNCH: diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 3d2f2d5e80fb..630575214429 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -290,341 +290,404 @@ VOID VirusTotalBuildJsonArray( } } -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 urlString = NULL; - - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - - if (!(httpSessionHandle = WinHttpOpen( - userAgent->Buffer, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, - 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 (!PhHttpSocketConnect(httpContext, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT)) goto CleanupExit; - } - if (WindowsVersion >= WINDOWS_8_1) { - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, - sizeof(ULONG) + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + + 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; } - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) + goto CleanupExit; - urlString = PhFormatString( - L"%s%s%s%s%S", - L"/partners", - L"/sysinternals", - L"/file-reports", - L"?apikey=", - resourceString->Buffer - ); + if (!PhHttpSocketSendRequest(httpContext, JsonArray->Buffer, (ULONG)JsonArray->Length)) + goto CleanupExit; - PhClearReference(&resourceString); + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, - L"POST", - PhGetString(urlString), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE - ))) - { - PhClearReference(&urlString); + if (!(subRequestBuffer = PhHttpSocketDownloadString(httpContext, FALSE))) goto CleanupExit; - } - PhClearReference(&urlString); +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + PhClearReference(&urlPathString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); - if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) + 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(); + + 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 + ); - allocatedLength = sizeof(buffer); - subRequestBuffer = PhAllocate(allocatedLength); - dataLength = 0; + PhClearReference(&resourceString); + } - while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) + 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 = PhAllocate(sizeof(VIRUSTOTAL_FILE_REPORT)); + memset(result, 0, 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; + + 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 = PhAllocate(sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); + memset(entry, 0, sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + 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); + + 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( +PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( _In_ PPH_STRING FileHash ) { - 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 urlString = NULL; - - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - - if (!(httpSessionHandle = WinHttpOpen( - userAgent->Buffer, - WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, - 0 - ))) + 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 (WindowsVersion >= WINDOWS_8_1) - { - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, - sizeof(ULONG) - ); - } - - if (!(connectHandle = WinHttpConnect( - httpSessionHandle, + if (!PhHttpSocketConnect( + httpContext, L"www.virustotal.com", - INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) + PH_HTTP_DEFAULT_HTTPS_PORT + )) { goto CleanupExit; } - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); - - urlString = PhFormatString( - L"%s%s%s%s%S%s%s", - L"/vtapi", - L"/v2", - L"/file", - L"/report", - L"?apikey=", - resourceString->Buffer, - L"&resource=", - PhGetString(FileHash) - ); + { + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + + 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); + PhClearReference(&resourceString); + } - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, + if (!PhHttpSocketBeginRequest( + httpContext, L"POST", - PhGetString(urlString), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE - ))) + PhGetString(urlPathString), + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) { goto CleanupExit; } - if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) goto CleanupExit; - if (!WinHttpSendRequest( - requestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) goto CleanupExit; - } - if (WinHttpReceiveResponse(requestHandle, NULL)) - { - BYTE buffer[PAGE_SIZE]; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + goto CleanupExit; - allocatedLength = sizeof(buffer); - subRequestBuffer = PhAllocate(allocatedLength); - dataLength = 0; + if (!(jsonRootObject = PhCreateJsonParser(jsonString->Buffer))) + goto CleanupExit; - while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; + result = PhAllocate(sizeof(VIRUSTOTAL_API_RESPONSE)); + memset(result, 0, sizeof(VIRUSTOTAL_API_RESPONSE)); - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + result->ResponseCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + result->PermaLink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); - memcpy(subRequestBuffer + dataLength, buffer, returnLength); - dataLength += returnLength; - } +CleanupExit: - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + if (httpContext) + PhHttpSocketDestroy(httpContext); - subRequestBuffer[dataLength] = 0; - } + if (jsonRootObject) + PhFreeJsonParser(jsonRootObject); -CleanupExit: + PhClearReference(&jsonString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); - PhClearReference(&urlString); + return result; +} - if (requestHandle) - WinHttpCloseHandle(requestHandle); +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 (connectHandle) - WinHttpCloseHandle(connectHandle); + if (!PhHttpSocketConnect( + httpContext, + L"www.virustotal.com", + PH_HTTP_DEFAULT_HTTPS_PORT + )) + { + goto CleanupExit; + } - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + { + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + + 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); + } + + 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; - PVOID jsonRootObject; - //PVOID jsonScanObject; - PVIRUSTOTAL_FILE_REPORT_RESULT result; + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + goto CleanupExit; - if (!(jsonRootObject = PhCreateJsonParser(subRequestBuffer))) + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) goto CleanupExit; - if (PhGetJsonValueAsLong64(jsonRootObject, "response_code") != 0) + if (!(jsonRootObject = PhCreateJsonParser(jsonString->Buffer))) goto CleanupExit; - result = PhAllocate(sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); - memset(result, 0, sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); + result = PhAllocate(sizeof(VIRUSTOTAL_API_RESPONSE)); + memset(result, 0, sizeof(VIRUSTOTAL_API_RESPONSE)); - result->Total = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "total"), FALSE); - result->Positives = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "positives"), FALSE); - result->Resource = PhGetJsonValueAsString(jsonRootObject, "resource"); - result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); - result->Md5 = PhGetJsonValueAsString(jsonRootObject, "md5"); - result->Sha1 = PhGetJsonValueAsString(jsonRootObject, "sha1"); - result->Sha256 = PhGetJsonValueAsString(jsonRootObject, "sha256"); - result->ScanDate = PhGetJsonValueAsString(jsonRootObject, "scan_date"); - result->Permalink = PhGetJsonValueAsString(jsonRootObject, "permalink"); - result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + //result->ResponseCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + //result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + //result->PermaLink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + //result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); + +CleanupExit: - //if (jsonScanObject = PhGetJsonObject(jsonRootObject, "scans")) - //{ - // PPH_LIST jsonArrayList; - // - // if (jsonArrayList = JsonGetObjectArrayList(jsonScanObject)) - // { - // result->ScanResults = PhCreateList(jsonArrayList->Count); - // - // 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 = PhGetJsonValueAsString(object->Entry, "version"); - // //PSTR result = PhGetJsonValueAsString(object->Entry, "result"); - // //PSTR update = PhGetJsonValueAsString(object->Entry, "update"); - // - // PhFree(object); - // } - // - // PhDereferenceObject(jsonArrayList); - // } - //} + if (httpContext) + PhHttpSocketDestroy(httpContext); + + if (jsonRootObject) + PhFreeJsonParser(jsonRootObject); + + PhClearReference(&jsonString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); return result; } @@ -649,7 +712,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( { ULONG i; INT64 resultLength; - PSTR jsonApiResult = NULL; + PPH_BYTES jsonApiResult = NULL; PVOID jsonArray; PVOID rootJsonObject = NULL; PVOID dataJsonObject; @@ -696,7 +759,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (!(jsonApiResult = VirusTotalSendHttpRequest(PhConvertUtf16ToUtf8(jsonArrayToSendString->Buffer)))) goto CleanupExit; - if (!(rootJsonObject = PhCreateJsonParser(jsonApiResult))) + if (!(rootJsonObject = PhCreateJsonParser(jsonApiResult->Buffer))) goto CleanupExit; if (!(dataJsonObject = PhGetJsonObject(rootJsonObject, "data"))) @@ -747,7 +810,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( // PhClearReference(&result->Permalink); // PhClearReference(&result->FileHash); // PhClearReference(&result->DetectionRatio); - // + PhFree(result); } @@ -769,7 +832,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (jsonApiResult) { - PhFree(jsonApiResult); + PhDereferenceObject(jsonApiResult); } if (resultTempList) From 0297b865a3ed838b0413b9af5aa81ec249dbbdeb Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 13:18:41 +1100 Subject: [PATCH 548/839] Update plugin manager window --- ProcessHacker/plugman.c | 1250 +++++++++++++++++++++++++++++++-------- 1 file changed, 994 insertions(+), 256 deletions(-) diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index 65d84a0767ed..b05852f3bb21 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -3,6 +3,7 @@ * plugins * * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -21,265 +22,596 @@ */ #include - +#include +#include #include +#include #include -#include +#include -#define IS_PLUGIN_LOADED(Plugin) (!!(Plugin)->AppContext.AppName.Buffer) -#define STR_OR_DEFAULT(String, Default) ((String) ? (String) : (Default)) +#define WM_PH_PLUGINS_SHOWDIALOG (WM_APP + 401) +#define WM_PH_PLUGINS_SHOWPROPERTIES (WM_APP + 402) -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 +static HANDLE PhPluginsThreadHandle = NULL; +static HWND PhPluginsWindowHandle = NULL; +static PH_EVENT PhPluginsInitializedEvent = PH_EVENT_INIT; -INT_PTR CALLBACK PhpPluginsDlgProc( +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 ); -VOID PhShowPluginsDialog( - _In_ HWND ParentWindowHandle +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 ) { - 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."); - } + PPH_STRING settings; + + settings = PhGetStringSetting(L"PluginManagerTreeListColumns"); + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); } -PWSTR PhpGetPluginBaseName( +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 ) { - if (Plugin->FileName) - { - PH_STRINGREF pathNamePart; - PH_STRINGREF baseNamePart; + PPH_PLUGIN_TREE_ROOT_NODE pluginNode; + PH_IMAGE_VERSION_INFO versionInfo; - if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, '\\', &pathNamePart, &baseNamePart)) - return baseNamePart.Buffer; - else - return Plugin->FileName->Buffer; - } - else + 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)) { - // Fake disabled plugin. - return Plugin->Name.Buffer; + pluginNode->Version = PhReferenceObject(versionInfo.FileVersion); + PhDeleteImageVersionInfo(&versionInfo); } + + PhAddEntryHashtable(Context->NodeHashtable, &pluginNode); + PhAddItemList(Context->NodeList, pluginNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); + + return pluginNode; } -PWSTR PhpGetPluginDisableButtonText( - _In_ PWSTR BaseName +PPH_PLUGIN_TREE_ROOT_NODE FindPluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_STRING InternalName ) { - PH_STRINGREF baseName; + PH_PLUGIN_TREE_ROOT_NODE lookupPluginsNode; + PPH_PLUGIN_TREE_ROOT_NODE lookupPluginsNodePtr = &lookupPluginsNode; + PPH_PLUGIN_TREE_ROOT_NODE *pluginsNode; + + lookupPluginsNode.InternalName = InternalName; - PhInitializeStringRefLongHint(&baseName, BaseName); + pluginsNode = (PPH_PLUGIN_TREE_ROOT_NODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupPluginsNodePtr + ); - if (PhIsPluginDisabled(&baseName)) - return L"Enable"; + if (pluginsNode) + return *pluginsNode; else - return L"Disable"; + return NULL; } -VOID PhpRefreshPluginDetails( - _In_ HWND hwndDlg +VOID RemovePluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_PLUGIN_TREE_ROOT_NODE Node ) { - PPH_STRING fileName; - PH_IMAGE_VERSION_INFO versionInfo; + ULONG index = 0; - if (SelectedPlugin && SelectedPlugin->FileName) // if there's no FileName, then it's a fake disabled plugin instance + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != -1) { - fileName = SelectedPlugin->FileName; + PhRemoveItemList(Context->NodeList, index); + } - 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); + 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); - if (PhInitializeImageVersionInfo(&versionInfo, fileName->Buffer)) + 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: { - SetDlgItemText(hwndDlg, IDC_VERSION, PhGetStringOrDefault(versionInfo.FileVersion, L"Unknown")); - PhDeleteImageVersionInfo(&versionInfo); + 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; + } } - else + return TRUE; + case TreeNewIsLeaf: { - SetDlgItemText(hwndDlg, IDC_VERSION, L"Unknown"); + 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; - 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"); + 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; + } - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SW_HIDE); + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + node = (PPH_PLUGIN_TREE_ROOT_NODE)getNodeColor->Node; - if (SelectedPlugin) + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: { - // This is a disabled plugin. - EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), TRUE); - SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(SelectedPlugin->Name.Buffer)); + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); } - else + 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: { - EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), FALSE); - SetDlgItemText(hwndDlg, IDC_DISABLE, L"Disable"); + 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; - EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), FALSE); + 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)); + 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; } -BOOLEAN PhpIsPluginLoadedByBaseName( - _In_ PPH_STRINGREF BaseName +VOID ClearPluginsTree( + _In_ PPH_PLUGMAN_CONTEXT Context ) { - PPH_AVL_LINKS links; + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyPluginsNode(Context->NodeList->Items[i]); - // Extremely inefficient code follows. - // TODO: Make this better. + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); - for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) - { - PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); - PH_STRINGREF pluginBaseName; + TreeNew_NodesStructured(Context->TreeNewHandle); +} - PhInitializeStringRefLongHint(&pluginBaseName, PhpGetPluginBaseName(plugin)); +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 (PhEqualStringRef(&pluginBaseName, BaseName, TRUE)) - return TRUE; + if (windowNode->Node.Selected) + return windowNode; } - return FALSE; + return NULL; } -PPH_PLUGIN PhpCreateDisabledPlugin( - _In_ PPH_STRINGREF BaseName +VOID GetSelectedPluginsNodes( + _In_ PPH_PLUGMAN_CONTEXT Context, + _Out_ PPH_PLUGIN_TREE_ROOT_NODE **PluginsNodes, + _Out_ PULONG NumberOfPluginsNodes ) { - PPH_PLUGIN plugin; + 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]; - plugin = PhAllocate(sizeof(PH_PLUGIN)); - memset(plugin, 0, sizeof(PH_PLUGIN)); + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } - 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; + *PluginsNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfPluginsNodes = list->Count; - return plugin; + PhDereferenceObject(list); } -VOID PhpFreeDisabledPlugin( - _In_ PPH_PLUGIN Plugin +VOID InitializePluginsTree( + _Inout_ PPH_PLUGMAN_CONTEXT Context ) { - PhFree(Plugin->Name.Buffer); - PhFree(Plugin); + 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 PhpAddDisabledPlugins( - VOID +VOID DeletePluginsTree( + _In_ PPH_PLUGMAN_CONTEXT Context ) { - PPH_STRING disabled; - PH_STRINGREF remainingPart; - PH_STRINGREF part; - PPH_PLUGIN disabledPlugin; - PPH_STRING displayText; - INT lvItemIndex; + if (Context->TitleFontHandle) + DeleteObject(Context->TitleFontHandle); + if (Context->NormalFontHandle) + DeleteObject(Context->NormalFontHandle); - disabled = PhGetStringSetting(L"DisabledPlugins"); - remainingPart = disabled->sr; + PluginsSaveSettingsTreeList(Context); - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyPluginsNode(Context->NodeList->Items[i]); - 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); + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); } -VOID PhpUpdateDisabledPlugin( - _In_ HWND hwndDlg, - _In_ INT ItemIndex, - _In_ PPH_PLUGIN Plugin, - _In_ BOOLEAN NewDisabledState +#pragma endregion + +PWSTR PhpGetPluginBaseName( + _In_ PPH_PLUGIN Plugin ) { - if (NewDisabledState) + if (Plugin->FileName) { - PhAddItemSimpleHashtable(DisabledPluginLookup, Plugin, NULL); + PH_STRINGREF pathNamePart; + PH_STRINGREF baseNamePart; + + if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, '\\', &pathNamePart, &baseNamePart)) + return baseNamePart.Buffer; + else + return Plugin->FileName->Buffer; } else { - PhRemoveItemSimpleHashtable(DisabledPluginLookup, Plugin); - } - - if (!IS_PLUGIN_LOADED(Plugin)) - { - assert(!NewDisabledState); - ListView_DeleteItem(PluginsLv, ItemIndex); + // Fake disabled plugin. + return Plugin->Name.Buffer; } - - InvalidateRect(PluginsLv, NULL, TRUE); - - ShowWindow(GetDlgItem(hwndDlg, IDC_INSTRUCTION), SW_SHOW); } -static COLORREF PhpPluginColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context +VOID PhpEnumerateLoadedPlugins( + _In_ PPH_PLUGMAN_CONTEXT Context ) { - PPH_PLUGIN plugin = Param; + PPH_AVL_LINKS links; - if (PhFindItemSimpleHashtable(DisabledPluginLookup, plugin)) - return RGB(0x77, 0x77, 0x77); // fake disabled plugin + for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) + { + PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); + PH_STRINGREF pluginBaseName; + + PhInitializeStringRefLongHint(&pluginBaseName, PhpGetPluginBaseName(plugin)); - return GetSysColor(COLOR_WINDOW); + if (PhIsPluginDisabled(&pluginBaseName)) + continue; + + AddPluginsNode(Context, plugin); + } } INT_PTR CALLBACK PhpPluginsDlgProc( @@ -289,112 +621,378 @@ INT_PTR CALLBACK PhpPluginsDlgProc( _In_ LPARAM lParam ) { + PPH_PLUGMAN_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(PH_PLUGMAN_CONTEXT)); + memset(context, 0, sizeof(PH_PLUGMAN_CONTEXT)); + + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPH_PLUGMAN_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + } + + if (!context) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { - PPH_AVL_LINKS links; + 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, PhMainWndHandle); + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_PLUGINTREE); - 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); + InitializePluginsTree(context); - DisabledPluginLookup = PhCreateSimpleHashtable(10); + 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); - 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 (PhGetIntegerPairSetting(L"PluginManagerWindowPosition").X != 0) + PhLoadWindowPlacementFromSetting(L"PluginManagerWindowPosition", L"PluginManagerWindowSize", hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); - if (plugin->Information.Author) - PhSetListViewSubItem(PluginsLv, lvItemIndex, 1, plugin->Information.Author); + context->MinimumSize.left = 0; + context->MinimumSize.top = 0; + context->MinimumSize.right = 300; + context->MinimumSize.bottom = 100; + MapDialogRect(hwndDlg, &context->MinimumSize); - PhInitializeStringRefLongHint(&baseNameSr, PhpGetPluginBaseName(plugin)); + PhpEnumerateLoadedPlugins(context); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(L"PluginManagerWindowPosition", L"PluginManagerWindowSize", hwndDlg); - if (PhIsPluginDisabled(&baseNameSr)) - PhAddItemSimpleHashtable(DisabledPluginLookup, plugin, NULL); - } + PhDeleteLayoutManager(&context->LayoutManager); - DisabledPluginInstances = PhCreateList(10); - PhpAddDisabledPlugins(); + DeletePluginsTree(context); - ExtendedListView_SortItems(PluginsLv); + PhFree(context); - SelectedPlugin = NULL; - PhpRefreshPluginDetails(hwndDlg); + PostQuitMessage(0); } break; - case WM_DESTROY: + case WM_PH_PLUGINS_SHOWDIALOG: { - ULONG i; - - for (i = 0; i < DisabledPluginInstances->Count; i++) - PhpFreeDisabledPlugin(DisabledPluginInstances->Items[i]); + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); - PhDereferenceObject(DisabledPluginInstances); - PhDereferenceObject(DisabledPluginLookup); + SetForegroundWindow(hwndDlg); } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { - case IDCANCEL: case IDOK: - EndDialog(hwndDlg, IDOK); + case IDCANCEL: + { + DestroyWindow(hwndDlg); + } break; - case IDC_DISABLE: + case IDC_DISABLED: { - 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); + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PLUGINSDISABLED), + hwndDlg, + PhpPluginsDisabledDlgProc + ); - SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(baseName)); - } + ClearPluginsTree(context); + PhpEnumerateLoadedPlugins(context); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); } break; - case IDC_OPTIONS: + case ID_SHOWCONTEXTMENU: { - if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) + 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), -1); + //PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_DISABLE, L"Disable", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_PROPERTIES, L"Properties", NULL, NULL), -1); + + //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 != -1) { - PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); + 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); + + ClearPluginsTree(context); + PhpEnumerateLoadedPlugins(context); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + SetWindowText(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 IDC_CLEANUP: + case WM_PH_PLUGINS_SHOWPROPERTIES: { - if (PhShowMessage2( + 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, - TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, - TD_INFORMATION_ICON, - L"Do you want to clean up unused plugin settings?", - L"" - ) == IDYES) - { - PhClearIgnoredSettings(); - } + PhpPluginPropertiesDlgProc, + (LPARAM)selectedNode->PluginInstance + ); } break; - case IDC_OPENURL: + } + } + 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 (!(PhPluginsThreadHandle = PhCreateThread(0, PhpPluginsDialogThreadStart, NULL))) + { + PhShowStatus(PhMainWndHandle, L"Unable to create the window.", 0, GetLastError()); + 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; + + 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, PH_AUTO_T(PH_STRING, PhGetBaseName(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_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; + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)selectedPlugin); + } + else + { + selectedPlugin = (PPH_PLUGIN)GetProp(hwndDlg, PhMakeContextAtom()); + + if (uMsg == WM_DESTROY) + { + RemoveProp(hwndDlg, PhMakeContextAtom()); + } + } + + if (selectedPlugin == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhpRefreshPluginDetails(hwndDlg, selectedPlugin); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (LPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_OPTIONS: { - NOTHING; + PhInvokeCallback(PhGetPluginCallback(selectedPlugin, PluginCallbackShowOptions), hwndDlg); } break; } @@ -406,45 +1004,185 @@ INT_PTR CALLBACK PhpPluginsDlgProc( 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); + PhShellExecute(hwndDlg, selectedPlugin->Information.Url, NULL); } } break; - case NM_DBLCLK: + } + } + 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)); + + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PPLUGIN_DISABLED_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + + if (uMsg == WM_DESTROY) + { + RemoveProp(hwndDlg, PhMakeContextAtom()); + 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); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (LPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); + } + 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) { - if (header->hwndFrom == PluginsLv) + switch (listView->uNewState & LVIS_STATEIMAGEMASK) { - if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) + case INDEXTOSTATEIMAGEMASK(2): // checked { - PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); + 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; } } break; } - REFLECT_MESSAGE_DLG(hwndDlg, PluginsLv, uMsg, wParam, lParam); - return FALSE; } From 13d553a33b557bd3117e160abcea501263c4480c Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 13:23:10 +1100 Subject: [PATCH 549/839] Add deprecated ExtraPlugins plugin to blocklist --- ProcessHacker/plugin.c | 44 +++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 3fd0cf20ffb6..788f5557b4dd 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -198,38 +198,42 @@ static BOOLEAN EnumPluginsDirectoryCallback( _In_opt_ PVOID Context ) { + static PCWSTR PhpPluginBlocklist[] = + { + L"CommonUtil.dll", + L"ExtraPlugins.dll" + }; + BOOLEAN blacklistedPlugin = FALSE; PH_STRINGREF baseName; PPH_STRING fileName; baseName.Buffer = Information->FileName; baseName.Length = Information->FileNameLength; - if (PhEndsWithStringRef2(&baseName, L".dll", TRUE)) + for (ULONG i = 0; i < ARRAYSIZE(PhpPluginBlocklist); i++) { - // Plugin blacklist - if (PhEndsWithStringRef2(&baseName, L"CommonUtil.dll", TRUE)) + if (PhEndsWithStringRef2(&baseName, PhpPluginBlocklist[i], TRUE)) { - fileName = PhCreateStringEx(NULL, PluginsDirectory->Length + Information->FileNameLength); - memcpy(fileName->Buffer, PluginsDirectory->Buffer, PluginsDirectory->Length); - memcpy(&fileName->Buffer[PluginsDirectory->Length / 2], Information->FileName, Information->FileNameLength); + blacklistedPlugin = TRUE; + break; + } + } - PhDeleteFileWin32(fileName->Buffer); + if (blacklistedPlugin) + { + fileName = PhConcatStringRef2(&PluginsDirectory->sr, &baseName); - PhDereferenceObject(fileName); - } - else - { - 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); + PhDeleteFileWin32(fileName->Buffer); - PhLoadPlugin(fileName); + PhDereferenceObject(fileName); + } + else if (!PhIsPluginDisabled(&baseName)) + { + fileName = PhConcatStringRef2(&PluginsDirectory->sr, &baseName); - PhDereferenceObject(fileName); - } - } + PhLoadPlugin(fileName); + + PhDereferenceObject(fileName); } return TRUE; From 203c557c243e7cf9f4d925677af9c34d0901d194 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 14:27:08 +1100 Subject: [PATCH 550/839] Update plugin window layout --- ProcessHacker/ProcessHacker.rc | 91 +++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 29 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index e37738d8f2f0..e44cfe89d3de 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1446,33 +1446,15 @@ BEGIN 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 +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 - 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 + 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 @@ -1862,6 +1844,41 @@ BEGIN 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -2259,12 +2276,12 @@ BEGIN BOTTOMMARGIN, 149 END - IDD_PLUGINS, DIALOG + IDD_PLUGINMANAGER, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 284 - TOPMARGIN, 7 - BOTTOMMARGIN, 265 + LEFTMARGIN, 2 + RIGHTMARGIN, 499 + TOPMARGIN, 2 + BOTTOMMARGIN, 270 END IDD_HANDLESTATS, DIALOG @@ -2400,6 +2417,22 @@ 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 END #endif // APSTUDIO_INVOKED From ed96e48367fa759a22a05efd2b4fb0b59acbfa9b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 14:27:33 +1100 Subject: [PATCH 551/839] Add missing file from previous commit --- ProcessHacker/resource.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index ffec44e7675d..5fe76c517a48 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -39,11 +39,9 @@ #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 @@ -74,6 +72,7 @@ #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 @@ -88,6 +87,7 @@ #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 @@ -125,6 +125,8 @@ #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 IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 @@ -414,7 +416,6 @@ #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 @@ -523,6 +524,9 @@ #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 ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -551,7 +555,6 @@ #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 @@ -728,9 +731,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 239 +#define _APS_NEXT_RESOURCE_VALUE 243 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1400 -#define _APS_NEXT_SYMED_VALUE 169 +#define _APS_NEXT_CONTROL_VALUE 1403 +#define _APS_NEXT_SYMED_VALUE 170 #endif #endif From fa21e5132c8d345633cd247d7aaacb102d93c9d4 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 20:24:50 +1100 Subject: [PATCH 552/839] Remove unused resources --- ProcessHacker/sysscmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/sysscmem.c b/ProcessHacker/sysscmem.c index f17d5ad36e05..2b7a9c4833cd 100644 --- a/ProcessHacker/sysscmem.c +++ b/ProcessHacker/sysscmem.c @@ -336,7 +336,7 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( 3, 3, MemoryDialog, - (HMENU)IDC_COMMIT, + NULL, PhInstanceHandle, NULL ); @@ -351,7 +351,7 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( 3, 3, MemoryDialog, - (HMENU)IDC_PHYSICAL, + NULL, PhInstanceHandle, NULL ); From 028128616fe5b6e6226a685da6079f253fd3817e Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 20:37:28 +1100 Subject: [PATCH 553/839] Move PhGetShieldBitmap, Fix bitmap flags --- ProcessHacker/appsup.c | 20 ++++++++++++++++++++ ProcessHacker/include/appsup.h | 4 ++++ ProcessHacker/mainwnd.c | 24 ++---------------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 8ac68a44e908..5cf37bc85463 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -2152,3 +2152,23 @@ PPH_STRING PhPcre2GetErrorMessage( 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/include/appsup.h b/ProcessHacker/include/appsup.h index 9df859d7fc9a..172c4ab55626 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -436,6 +436,10 @@ PPH_STRING PhPcre2GetErrorMessage( _In_ INT ErrorCode ); +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 diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index d53367b77ed8..46186dfe4219 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2459,26 +2459,6 @@ VOID PhMwpDispatchMenuCommand( SendMessage(PhMainWndHandle, WM_COMMAND, ItemId, 0); } -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 @@ -2500,7 +2480,7 @@ VOID PhMwpInitializeSubMenu( { HBITMAP shieldBitmap; - if (shieldBitmap = PhMwpGetShieldBitmap()) + if (shieldBitmap = PhGetShieldBitmap()) { if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_SHOWDETAILSFORALLPROCESSES)) menuItem->Bitmap = shieldBitmap; @@ -2642,7 +2622,7 @@ VOID PhMwpInitializeSubMenu( { HBITMAP shieldBitmap; - if (shieldBitmap = PhMwpGetShieldBitmap()) + if (shieldBitmap = PhGetShieldBitmap()) { if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_STARTTASKMANAGER)) menuItem->Bitmap = shieldBitmap; From 044a1f30d02d99c79d7ebbbd4ea66072889a1fa2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 3 Nov 2017 20:37:58 +1100 Subject: [PATCH 554/839] Add file from previous commit --- ProcessHacker/include/mainwndp.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 29fea32a3fc7..1f69c58882fb 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -202,10 +202,6 @@ VOID PhMwpDispatchMenuCommand( _In_ ULONG_PTR ItemData ); -HBITMAP PhMwpGetShieldBitmap( - VOID - ); - VOID PhMwpInitializeSubMenu( _In_ PPH_EMENU Menu, _In_ ULONG Index From e68b51dc15deb61b4e435637ad28477d4b8529b5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 4 Nov 2017 03:14:20 +1100 Subject: [PATCH 555/839] Fix warning --- ProcessHacker/plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 788f5557b4dd..a62dd703b31e 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -198,7 +198,7 @@ static BOOLEAN EnumPluginsDirectoryCallback( _In_opt_ PVOID Context ) { - static PCWSTR PhpPluginBlocklist[] = + static const PWSTR PhpPluginBlocklist[] = { L"CommonUtil.dll", L"ExtraPlugins.dll" From 9002b87e401dfddf58f5ca76a24fe931cfb71142 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 4 Nov 2017 04:39:49 +1100 Subject: [PATCH 556/839] CustomSignTool: Update to latest SDK --- tools/CustomSignTool/CustomSignTool.sln | 31 ++++++++++++++++++ tools/CustomSignTool/CustomSignTool.vcxproj | 2 +- .../bin/Release32/CustomSignTool.exe | Bin 118784 -> 119808 bytes .../bin/Release64/CustomSignTool.exe | Bin 137728 -> 140288 bytes tools/CustomSignTool/main.c | 2 +- 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tools/CustomSignTool/CustomSignTool.sln 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 c8a08527627b..24f83c05f5de 100644 --- a/tools/CustomSignTool/CustomSignTool.vcxproj +++ b/tools/CustomSignTool/CustomSignTool.vcxproj @@ -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 7af31bf7ba69b82c30b783f257113ac5fdd89724..d78fbf12720dccf665f26805a7d0e29709fe95cc 100644 GIT binary patch delta 54129 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%0ABrj}IL!8vCW0y+?~<$5Iig@01{Yy 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 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); From 997de755cacc01583ae2a4056dd74ef0fa37a8b8 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 4 Nov 2017 06:09:16 +1100 Subject: [PATCH 557/839] Add plugin manager settings --- ProcessHacker/settings.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index a9609843c868..33fc77524159 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -122,6 +122,9 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"NetworkTreeListColumns", L""); PhpAddStringSetting(L"NetworkTreeListSort", L"0,1"); // 0, AscendingSortOrder PhpAddIntegerSetting(L"NoPurgeProcessRecords", L"0"); + 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"ProcessTreeListColumns", L""); From 703e929b6de51a972ec13d6d1a855a3b8094fc86 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 5 Nov 2017 21:26:27 +1100 Subject: [PATCH 558/839] Remove duplicate MAKEINTRESOURCE macro --- phlib/guisup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index c27f2bce0957..7b83f1571132 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -710,9 +710,9 @@ VOID PhGetStockApplicationIcon( // Fallback icons if (!smallIcon) - smallIcon = PhLoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION), PH_LOAD_ICON_SIZE_SMALL, 0, 0); + smallIcon = PhLoadIcon(NULL, IDI_APPLICATION, PH_LOAD_ICON_SIZE_SMALL, 0, 0); if (!largeIcon) - largeIcon = PhLoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION), PH_LOAD_ICON_SIZE_LARGE, 0, 0); + largeIcon = PhLoadIcon(NULL, IDI_APPLICATION, PH_LOAD_ICON_SIZE_LARGE, 0, 0); PhEndInitOnce(&initOnce); } From ed493e3f4d17373213c35e418b558e6b716b80cd Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 5 Nov 2017 22:32:53 +1100 Subject: [PATCH 559/839] Improve Win10 application icons --- phlib/guisup.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index 7b83f1571132..0578d65e8de2 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -678,34 +678,28 @@ VOID PhGetStockApplicationIcon( 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()) + if (WindowsVersion < WINDOWS_10) { - PH_STRINGREF dllBaseName; - ULONG index; + PPH_STRING systemDirectory; + PPH_STRING dllFileName; - // TODO: Find a better solution. - if (WindowsVersion >= WINDOWS_10) - { - PhInitializeStringRef(&dllBaseName, L"\\imageres.dll"); - index = 11; - } - else + // 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; + PhInitializeStringRef(&dllBaseName, L"\\user32.dll"); index = 0; - } - dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName); - PhDereferenceObject(systemDirectory); + dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName); + PhDereferenceObject(systemDirectory); - ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1); - PhDereferenceObject(dllFileName); + ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1); + PhDereferenceObject(dllFileName); + } } // Fallback icons From 01e76274a7414adb689047def02987d8d4970f1d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 6 Nov 2017 08:46:23 +1100 Subject: [PATCH 560/839] Update PhGetSystemDirectory --- phlib/util.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 379d835d2620..76903763b2bb 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2000,40 +2000,18 @@ 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) - 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; - } + if (cachedSystemDirectory) + return PhReferenceObject(cachedSystemDirectory); - PhTrimToNullTerminatorString(systemDirectory); + PhGetSystemRoot(&systemRootString); + systemDirectory = PhConcatStringRef2(&systemRootString, &system32String); // Try to cache the value. if (_InterlockedCompareExchangePointer( From 3b393029fb093e0349c3450805635e27351c743d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 6 Nov 2017 19:11:31 +1100 Subject: [PATCH 561/839] Update macro usage --- ProcessHacker/extmgr.c | 4 ++-- ProcessHacker/phsvc/svcapi.c | 2 +- ProcessHacker/phsvc/svcapiport.c | 6 ++--- ProcessHacker/procmtgn.c | 2 +- ProcessHacker/runas.c | 2 +- phlib/basesup.c | 39 ++++++++++++++++---------------- phlib/filepool.c | 10 ++++---- phlib/filestream.c | 8 +++---- phlib/guisup.c | 2 +- phlib/include/hndlinfo.h | 4 ++-- phlib/include/phbasesup.h | 6 ++--- phlib/include/phnative.h | 4 ++-- phlib/kph.c | 2 +- phlib/maplib.c | 6 ++--- phlib/native.c | 8 +++---- phlib/util.c | 2 +- 16 files changed, 54 insertions(+), 53 deletions(-) diff --git a/ProcessHacker/extmgr.c b/ProcessHacker/extmgr.c index d86051f0bb61..950d483b095d 100644 --- a/ProcessHacker/extmgr.c +++ b/ProcessHacker/extmgr.c @@ -126,7 +126,7 @@ PVOID PhEmGetObjectExtension( if (!objectExtension) return NULL; - return (PCHAR)Object + PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset; + return PTR_ADD_OFFSET(Object, PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset); } /** @@ -178,7 +178,7 @@ VOID PhEmCallObjectOperation( objectExtension->Callbacks[Operation]( Object, ObjectType, - (PCHAR)Object + objectTypeState->InitialSize + objectExtension->ExtensionOffset + PTR_ADD_OFFSET(Object, objectTypeState->InitialSize + objectExtension->ExtensionOffset) ); } diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index be7a728ce830..9aba65bd33e3 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -108,7 +108,7 @@ PVOID PhSvcValidateString( PPHSVC_CLIENT client = PhSvcGetCurrentClient(); PVOID address; - address = (PCHAR)client->ClientViewBase + String->Offset; + address = PTR_ADD_OFFSET(client->ClientViewBase, String->Offset); if ((ULONG_PTR)address + String->Length < (ULONG_PTR)address || (ULONG_PTR)address < (ULONG_PTR)client->ClientViewBase || diff --git a/ProcessHacker/phsvc/svcapiport.c b/ProcessHacker/phsvc/svcapiport.c index 7bfb8ebf49b8..62278ed630cf 100644 --- a/ProcessHacker/phsvc/svcapiport.c +++ b/ProcessHacker/phsvc/svcapiport.c @@ -64,7 +64,7 @@ NTSTATUS PhSvcApiPortInitialization( RtlLengthSid(&PhSeEveryoneSid); securityDescriptor = PhAllocate(sdAllocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); @@ -296,12 +296,12 @@ VOID PhSvcHandleConnectionRequest( if (PhIsExecutingInWow64()) { client->ClientViewBase = (PVOID)clientView64.ViewBase; - client->ClientViewLimit = (PCHAR)clientView64.ViewBase + (ULONG)clientView64.ViewSize; + client->ClientViewLimit = PTR_ADD_OFFSET(clientView64.ViewBase, clientView64.ViewSize); } else { client->ClientViewBase = clientView.ViewBase; - client->ClientViewLimit = (PCHAR)clientView.ViewBase + clientView.ViewSize; + client->ClientViewLimit = PTR_ADD_OFFSET(clientView.ViewBase, clientView.ViewSize); } NtCompleteConnectPort(portHandle); diff --git a/ProcessHacker/procmtgn.c b/ProcessHacker/procmtgn.c index e002fe1beba4..cf72c479d837 100644 --- a/ProcessHacker/procmtgn.c +++ b/ProcessHacker/procmtgn.c @@ -52,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; diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index e84d047429df..954f4fb811eb 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -741,7 +741,7 @@ VOID PhSetDesktopWinStaAccess( (ULONG)sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(allAppPackagesSid); securityDescriptor = PhAllocate(allocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); diff --git a/phlib/basesup.c b/phlib/basesup.c index 358bcbb9fe07..fd8eb7c8d92d 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -922,7 +922,7 @@ BOOLEAN PhCopyStringZFromMultiByte( if (NT_SUCCESS(status)) { // RtlMultiByteToUnicodeN doesn't null terminate the string. - *(PWCHAR)((PCHAR)OutputBuffer + unicodeBytes) = 0; + *(PWCHAR)PTR_ADD_OFFSET(OutputBuffer, unicodeBytes) = 0; copied = TRUE; } else @@ -1151,7 +1151,7 @@ LONG PhCompareStringRef( s1 = String1->Buffer; s2 = String2->Buffer; - end = (PWCHAR)((PCHAR)s1 + (l1 <= l2 ? l1 : l2)); + end = (PWCHAR)PTR_ADD_OFFSET(s1, l1 <= l2 ? l1 : l2); if (!IgnoreCase) { @@ -1440,7 +1440,7 @@ ULONG_PTR PhFindLastCharInStringRef( PWCHAR buffer; SIZE_T length; - buffer = (PWCHAR)((PCHAR)String->Buffer + String->Length); + buffer = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length); length = String->Length / sizeof(WCHAR); if (!IgnoreCase) @@ -1622,7 +1622,7 @@ BOOLEAN PhSplitStringRefAtChar( FirstPart->Buffer = input.Buffer; FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + 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; @@ -1669,7 +1669,7 @@ BOOLEAN PhSplitStringRefAtLastChar( FirstPart->Buffer = input.Buffer; FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + 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; @@ -1718,7 +1718,7 @@ BOOLEAN PhSplitStringRefAtString( FirstPart->Buffer = input.Buffer; FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + Separator->Length); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, index * sizeof(WCHAR) + Separator->Length); SecondPart->Length = input.Length - index * sizeof(WCHAR) - Separator->Length; return TRUE; @@ -1855,7 +1855,7 @@ BOOLEAN PhSplitStringRefEx( } else { - s = (PWCHAR)((PCHAR)input.Buffer + input.Length - sizeof(WCHAR)); + s = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, input.Length - sizeof(WCHAR)); direction = -1; } @@ -1945,7 +1945,7 @@ BOOLEAN PhSplitStringRefEx( SeparatorFound: FirstPart->Buffer = input.Buffer; FirstPart->Length = separatorIndex * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + separatorIndex * sizeof(WCHAR) + separatorLength); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, separatorIndex * sizeof(WCHAR) + separatorLength); SecondPart->Length = input.Length - separatorIndex * sizeof(WCHAR) - separatorLength; if (SeparatorPart) @@ -2016,7 +2016,7 @@ VOID PhTrimStringRef( { trimCount = 0; count = String->Length / sizeof(WCHAR); - s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR)); + s = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length - sizeof(WCHAR)); while (count-- != 0) { @@ -2085,7 +2085,7 @@ VOID PhTrimStringRef( { trimCount = 0; count = String->Length / sizeof(WCHAR); - s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR)); + s = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length - sizeof(WCHAR)); while (count-- != 0) { @@ -2146,7 +2146,7 @@ PPH_STRING PhCreateStringEx( assert(!(Length & 1)); string->Length = Length; string->Buffer = string->Data; - *(PWCHAR)((PCHAR)string->Buffer + Length) = 0; + *(PWCHAR)PTR_ADD_OFFSET(string->Buffer, Length) = 0; if (Buffer) { @@ -2260,7 +2260,7 @@ PPH_STRING PhConcatStrings_V( stringLength = PhCountStringZ(arg) * sizeof(WCHAR); memcpy( - (PCHAR)string->Buffer + totalLength, + PTR_ADD_OFFSET(string->Buffer, totalLength), arg, stringLength ); @@ -2294,7 +2294,7 @@ PPH_STRING PhConcatStrings2( length1 ); memcpy( - (PCHAR)string->Buffer + length1, + PTR_ADD_OFFSET(string->Buffer, length1), String2, length2 ); @@ -2320,7 +2320,7 @@ PPH_STRING PhConcatStringRef2( string = PhCreateStringEx(NULL, String1->Length + String2->Length); memcpy(string->Buffer, String1->Buffer, String1->Length); - memcpy((PCHAR)string->Buffer + String1->Length, String2->Buffer, String2->Length); + memcpy(PTR_ADD_OFFSET(string->Buffer, String1->Length), String2->Buffer, String2->Length); return string; } @@ -3474,7 +3474,7 @@ FORCEINLINE VOID PhpWriteNullTerminatorStringBuilder( ) { assert(!(StringBuilder->String->Length & 1)); - *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = 0; + *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = 0; } /** @@ -3540,7 +3540,7 @@ VOID PhAppendStringBuilderEx( if (String) { memcpy( - (PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length, + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), String, Length ); @@ -3566,7 +3566,7 @@ VOID PhAppendCharStringBuilder( PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + sizeof(WCHAR)); } - *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = Character; + *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = Character; StringBuilder->String->Length += sizeof(WCHAR); PhpWriteNullTerminatorStringBuilder(StringBuilder); } @@ -3594,7 +3594,7 @@ VOID PhAppendCharStringBuilder2( } wmemset( - (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length), + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), Character, Count ); @@ -3619,6 +3619,7 @@ VOID PhAppendFormatStringBuilder( va_start(argptr, Format); PhAppendFormatStringBuilder_V(StringBuilder, Format, argptr); + va_end(argptr); } VOID PhAppendFormatStringBuilder_V( @@ -3641,7 +3642,7 @@ VOID PhAppendFormatStringBuilder_V( PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + lengthInBytes); _vsnwprintf( - (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length), + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), length, Format, ArgPtr diff --git a/phlib/filepool.c b/phlib/filepool.c index 9b76e6fa44a2..58790ae17f55 100644 --- a/phlib/filepool.c +++ b/phlib/filepool.c @@ -223,7 +223,7 @@ NTSTATUS PhCreateFilePool( // Set up the first segment properly. pool->FirstBlockOfFirstSegment->Span = pool->FileHeaderBlockSpan; - segmentHeaderBlock = (PPH_FP_BLOCK_HEADER)((PCHAR)pool->FirstBlockOfFirstSegment + (pool->FileHeaderBlockSpan << pool->BlockShift)); + segmentHeaderBlock = (PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(pool->FirstBlockOfFirstSegment, (pool->FileHeaderBlockSpan << pool->BlockShift)); PhFppInitializeSegment(pool, segmentHeaderBlock, pool->FileHeaderBlockSpan); pool->Header->FreeLists[1] = 0; @@ -576,7 +576,7 @@ BOOLEAN PhFreeFilePoolByRva( if (!firstBlock) return FALSE; - PhpFreeFilePool(Pool, segmentIndex, firstBlock, (PCHAR)firstBlock + offset); + PhpFreeFilePool(Pool, segmentIndex, firstBlock, PTR_ADD_OFFSET(firstBlock, offset)); PhFppDereferenceSegment(Pool, segmentIndex); return TRUE; @@ -639,7 +639,7 @@ PVOID PhReferenceFilePoolByRva( if (!firstBlock) return NULL; - return (PCHAR)firstBlock + offset; + return PTR_ADD_OFFSET(firstBlock, offset); } /** @@ -877,7 +877,7 @@ PPH_FP_SEGMENT_HEADER PhFppGetHeaderSegment( 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; + return (PPH_FP_SEGMENT_HEADER)&((PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(FirstBlock, (Pool->FileHeaderBlockSpan << Pool->BlockShift)))->Body; } } @@ -1248,7 +1248,7 @@ PPH_FP_BLOCK_HEADER PhFppAllocateBlocks( SegmentHeader->FreeBlocks -= NumberOfBlocks; - blockHeader = (PPH_FP_BLOCK_HEADER)((PCHAR)FirstBlock + (foundIndex << Pool->BlockShift)); + blockHeader = (PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(FirstBlock, (foundIndex << Pool->BlockShift)); blockHeader->Flags = 0; blockHeader->Span = NumberOfBlocks; diff --git a/phlib/filestream.c b/phlib/filestream.c index 3e7552d40ce5..d38da05ec6a0 100644 --- a/phlib/filestream.c +++ b/phlib/filestream.c @@ -332,7 +332,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 +348,7 @@ NTSTATUS PhReadFileStream( if (NT_SUCCESS(status = PhpReadFileStream( FileStream, - (PCHAR)Buffer + readLength, + PTR_ADD_OFFSET(Buffer, readLength), Length - readLength, &readLength2 ))) @@ -454,7 +454,7 @@ NTSTATUS PhWriteFileStream( writtenLength = Length; memcpy( - (PCHAR)FileStream->Buffer + FileStream->WritePosition, + PTR_ADD_OFFSET(FileStream->Buffer, FileStream->WritePosition), Buffer, writtenLength ); @@ -466,7 +466,7 @@ NTSTATUS PhWriteFileStream( return status; } - Buffer = (PCHAR)Buffer + writtenLength; + Buffer = PTR_ADD_OFFSET(Buffer, writtenLength); Length -= writtenLength; } diff --git a/phlib/guisup.c b/phlib/guisup.c index 0578d65e8de2..a2d3f18aa4b3 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -822,7 +822,7 @@ VOID PhSetClipboardString( memory = GlobalLock(data); memcpy(memory, String->Buffer, String->Length); - *(PWCHAR)((PCHAR)memory + String->Length) = 0; + *(PWCHAR)PTR_ADD_OFFSET(memory, String->Length) = 0; GlobalUnlock(memory); diff --git a/phlib/include/hndlinfo.h b/phlib/include/hndlinfo.h index c92bd4b8de57..9a79c013d6c1 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)) + (POBJECT_TYPE_INFORMATION)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) + \ + (POBJECT_TYPE_INFORMATION)PTR_ADD_OFFSET(ObjectType, sizeof(OBJECT_TYPE_INFORMATION) + \ ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) PHLIBAPI diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index 7dd9bb837a28..5fc24b13821a 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -1028,7 +1028,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); @@ -1056,7 +1056,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; } @@ -2242,7 +2242,7 @@ PhItemArray( _In_ SIZE_T Index ) { - return (PCHAR)Array->Items + Index * Array->ItemSize; + return PTR_ADD_OFFSET(Array->Items, Index * Array->ItemSize); } PHLIBAPI diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 4e752ae3a347..a0c4e45bfed2 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -633,7 +633,7 @@ 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 \ ) @@ -700,7 +700,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 \ ) diff --git a/phlib/kph.c b/phlib/kph.c index 43935b3f7d28..75ccd9d2c3b2 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -372,7 +372,7 @@ VOID KphSetServiceSecurity( RtlLengthSid(&PhSeInteractiveSid); securityDescriptor = PhAllocate(sdAllocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); diff --git a/phlib/maplib.c b/phlib/maplib.c index 4600b375909f..c46477a3a5aa 100644 --- a/phlib/maplib.c +++ b/phlib/maplib.c @@ -49,9 +49,9 @@ NTSTATUS PhInitializeMappedArchive( ) { NTSTATUS status; - PCHAR start; + PVOID start; - start = (PCHAR)ViewBase; + start = ViewBase; memset(MappedArchive, 0, sizeof(PH_MAPPED_ARCHIVE)); MappedArchive->ViewBase = ViewBase; @@ -78,7 +78,7 @@ NTSTATUS PhInitializeMappedArchive( status = PhpGetMappedArchiveMemberFromHeader( MappedArchive, - (PIMAGE_ARCHIVE_MEMBER_HEADER)(start + IMAGE_ARCHIVE_START_SIZE), + PTR_ADD_OFFSET(start, IMAGE_ARCHIVE_START_SIZE), &MappedArchive->FirstLinkerMember ); diff --git a/phlib/native.c b/phlib/native.c index 7f3c630d2d9a..260c2c4313b7 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4307,12 +4307,12 @@ BOOLEAN NTAPI PhpIsDotNetEnumProcessModulesCallback( if (RtlPrefixUnicodeString(&systemRoot, &fileName, TRUE)) { - fileName.Buffer = (PWCHAR)((PCHAR)fileName.Buffer + systemRoot.Length); + fileName.Buffer = (PWCHAR)PTR_ADD_OFFSET(fileName.Buffer, systemRoot.Length); fileName.Length -= systemRoot.Length; if (RtlPrefixUnicodeString(frameworkPart, &fileName, TRUE)) { - fileName.Buffer = (PWCHAR)((PCHAR)fileName.Buffer + frameworkPart->Length); + fileName.Buffer = (PWCHAR)PTR_ADD_OFFSET(fileName.Buffer, frameworkPart->Length); fileName.Length -= frameworkPart->Length; if (fileName.Length >= 4 * sizeof(WCHAR)) // vx.x @@ -5077,7 +5077,7 @@ PPH_STRING PhGetFileName( 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); + memcpy(PTR_ADD_OFFSET(newFileName->Buffer, systemRoot.Length), &FileName->Buffer[11], FileName->Length - 11 * 2); } // "system32\" means "C:\Windows\system32\". else if (PhStartsWithString2(FileName, L"system32\\", TRUE)) @@ -5088,7 +5088,7 @@ PPH_STRING PhGetFileName( 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); + memcpy(PTR_ADD_OFFSET(newFileName->Buffer, systemRoot.Length + 2), FileName->Buffer, FileName->Length); } else if (FileName->Length != 0 && FileName->Buffer[0] == '\\') { diff --git a/phlib/util.c b/phlib/util.c index 76903763b2bb..979a6a5ca74f 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5153,7 +5153,7 @@ HANDLE PhGetNamespaceHandle( RtlLengthSid(&PhSeInteractiveSid); securityDescriptor = PhAllocate(sdAllocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); From cb4f919ddfabd52ba69d231ca5a5bc79657b00e5 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 7 Nov 2017 20:24:21 +1100 Subject: [PATCH 562/839] Return PhCreateThread2 ntstatus --- phlib/basesup.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/phlib/basesup.c b/phlib/basesup.c index fd8eb7c8d92d..93a08cfe4c43 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -267,11 +267,12 @@ HANDLE PhCreateThread( } } -VOID PhCreateThread2( +NTSTATUS PhCreateThread2( _In_ PUSER_THREAD_START_ROUTINE StartAddress, _In_opt_ PVOID Parameter ) { + NTSTATUS status; HANDLE threadHandle; PPHP_BASE_THREAD_CONTEXT context; @@ -279,7 +280,7 @@ VOID PhCreateThread2( context->StartAddress = StartAddress; context->Parameter = Parameter; - if (NT_SUCCESS(RtlCreateUserThread( + status = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, @@ -290,7 +291,9 @@ VOID PhCreateThread2( context, &threadHandle, NULL - ))) + ); + + if (NT_SUCCESS(status)) { PHLIB_INC_STATISTIC(BaseThreadsCreated); NtClose(threadHandle); @@ -300,6 +303,8 @@ VOID PhCreateThread2( PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); } + + return status; } /** From ee79647fcdbb4b27299ae01e1bc55f9094c79d57 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 7 Nov 2017 20:24:36 +1100 Subject: [PATCH 563/839] Add missing type --- phlib/include/phbasesup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index 5fc24b13821a..9b2c871b6ae2 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -41,7 +41,7 @@ PhCreateThread( ); PHLIBAPI -VOID +NTSTATUS NTAPI PhCreateThread2( _In_ PUSER_THREAD_START_ROUTINE StartAddress, From 9cffba8eaa18cd5ec8f261815386d942cf6ea4fc Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 9 Nov 2017 14:07:36 +1100 Subject: [PATCH 564/839] Remove unused code --- plugins/DotNetTools/main.c | 15 --------------- plugins/OnlineChecks/main.c | 2 +- plugins/WindowExplorer/main.c | 15 --------------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/plugins/DotNetTools/main.c b/plugins/DotNetTools/main.c index 759559574503..c54131347ba7 100644 --- a/plugins/DotNetTools/main.c +++ b/plugins/DotNetTools/main.c @@ -26,7 +26,6 @@ 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; @@ -56,14 +55,6 @@ VOID NTAPI UnloadCallback( NOTHING; } -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - VOID NTAPI MenuItemCallback( _In_opt_ PVOID Parameter, _In_opt_ PVOID Context @@ -237,12 +228,6 @@ LOGICAL DllMain( NULL, &PluginUnloadCallbackRegistration ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); //PhRegisterCallback( // PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), // MenuItemCallback, diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index cbc39b076775..c199c8070458 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -264,7 +264,7 @@ VOID NTAPI MainMenuInitializingCallback( 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(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_QUEUE, L"Upload unknown files to VirusTotal...", NULL), -1); PhInsertEMenuItem(menuInfo->Menu, onlineMenuItem, -1); if (VirusTotalScanningEnabled) diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index e0fbcffdc876..e6a4aadfa86f 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -28,7 +28,6 @@ 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; @@ -50,14 +49,6 @@ VOID NTAPI UnloadCallback( WeHookServerUninitialization(); } -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - BOOL CALLBACK WepEnumDesktopProc( _In_ LPTSTR lpszDesktop, _In_ LPARAM lParam @@ -276,12 +267,6 @@ LOGICAL DllMain( NULL, &PluginUnloadCallbackRegistration ); - //PhRegisterCallback( - // PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - // ShowOptionsCallback, - // NULL, - // &PluginShowOptionsCallbackRegistration - // ); PhRegisterCallback( PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), MenuItemCallback, From da3c18b7bdc7d26cc194d433247394fff9d5aab9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 9 Nov 2017 14:08:50 +1100 Subject: [PATCH 565/839] Enable assert, Remove cast --- phlib/include/hndlinfo.h | 4 ++-- phnt/include/ntexapi.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/phlib/include/hndlinfo.h b/phlib/include/hndlinfo.h index 9a79c013d6c1..fb0849f524b9 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)PTR_ADD_OFFSET(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)PTR_ADD_OFFSET(ObjectType, sizeof(OBJECT_TYPE_INFORMATION) + \ + PTR_ADD_OFFSET(ObjectType, sizeof(OBJECT_TYPE_INFORMATION) + \ ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) PHLIBAPI diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 595381753cec..f7bea46bf049 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -3530,7 +3530,7 @@ 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) == 0x704); // Visual Studio has a problem with this +C_ASSERT(sizeof(KUSER_SHARED_DATA) == 0x708); #define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)0x7ffe0000) From a24dffdccf9094a5e14c5a3eed39723ce6301c1c Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 12 Nov 2017 02:05:58 +1100 Subject: [PATCH 566/839] NetworkTools: Fix tracert handle leak --- plugins/NetworkTools/tracetree.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 0423c418332e..919e7074f906 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -51,6 +51,9 @@ VOID NTAPI TracertTreeNodeItemDeleteProcedure( if (tracertNode->PingString[i]) PhDereferenceObject(tracertNode->PingString[i]); } + + if (tracertNode->CountryIcon) + DestroyIcon(tracertNode->CountryIcon); } PTRACERT_ROOT_NODE TracertTreeCreateNode( From a19360b645e495242b6be5992e9554107ab117a4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 12 Nov 2017 19:14:05 +1100 Subject: [PATCH 567/839] Update help text --- ProcessHacker/mwpgproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 619dbe6c12ee..142240387119 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -243,7 +243,7 @@ VOID PhMwpToggleSignedProcessTreeFilter( PhShowInformation( PhMainWndHandle, 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." ); } From 8be9996c91b9a49ce461714098da8b4a2f2760be Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 12 Nov 2017 19:16:56 +1100 Subject: [PATCH 568/839] Improve consistency, Tidy up comments --- ProcessHacker/include/netprv.h | 2 +- ProcessHacker/netprv.c | 58 +++++++++++++++++++--------------- ProcessHacker/ntobjprp.c | 2 +- ProcessHacker/procprv.c | 6 ++-- phnt/include/ntmmapi.h | 4 +-- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/ProcessHacker/include/netprv.h b/ProcessHacker/include/netprv.h index beaecea2b4a1..7b26038ed53f 100644 --- a/ProcessHacker/include/netprv.h +++ b/ProcessHacker/include/netprv.h @@ -25,7 +25,7 @@ typedef struct _PH_NETWORK_ITEM BOOLEAN ProcessIconValid; PPH_STRING OwnerName; - BOOLEAN JustResolved; + ULONG JustResolved; WCHAR LocalAddressString[65]; WCHAR LocalPortString[PH_INT32_STR_LEN_1]; diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index 07968e356c33..8930c1ec20fb 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -456,6 +456,35 @@ VOID PhpUpdateNetworkItemOwner( } } +VOID PhFlushNetworkQueryData( + VOID + ) +{ + PSLIST_ENTRY entry; + PPH_NETWORK_ITEM_QUERY_DATA data; + + if (RtlQueryDepthSList(&PhNetworkItemQueryListHead) == 0) + 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 ) @@ -476,6 +505,7 @@ VOID PhNetworkProviderUpdate( if (!PhGetNetworkConnections(&connections, &numberOfConnections)) return; + // Look for closed connections. { PPH_LIST connectionsToRemove = NULL; PH_HASHTABLE_ENUM_CONTEXT enumContext; @@ -527,29 +557,9 @@ VOID PhNetworkProviderUpdate( } // 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); - } - } + PhFlushNetworkQueryData(); + // Look for new network connections and update existing ones. for (i = 0; i < numberOfConnections; i++) { PPH_NETWORK_ITEM networkItem; @@ -673,7 +683,7 @@ VOID PhNetworkProviderUpdate( BOOLEAN modified = FALSE; PPH_PROCESS_ITEM processItem; - if (networkItem->JustResolved) + if (InterlockedExchange(&networkItem->JustResolved, 0) != 0) modified = TRUE; if (networkItem->State != connections[i].State) @@ -705,8 +715,6 @@ VOID PhNetworkProviderUpdate( } } - networkItem->JustResolved = FALSE; - if (modified) { // Raise the network item modified event. diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index b2e892f6550f..e7aa709fa6e8 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -393,7 +393,7 @@ static VOID PhpRefreshMutantPageInfo( { PPH_STRING name; - if (ownerInfo.ClientId.UniqueProcess != NULL) + if (ownerInfo.ClientId.UniqueProcess) { name = PhGetClientIdName(&ownerInfo.ClientId); SetDlgItemText(hwndDlg, IDC_OWNER, name->Buffer); diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index a55eefbc3460..dc516afedcd9 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1808,7 +1808,7 @@ VOID PhFlushProcessQueryData( PPH_PROCESS_QUERY_DATA data; BOOLEAN processed; - if (!RtlFirstEntrySList(&PhProcessQueryDataListHead)) + if (RtlQueryDepthSList(&PhProcessQueryDataListHead) == 0) return; entry = RtlInterlockedFlushSList(&PhProcessQueryDataListHead); @@ -1847,7 +1847,7 @@ VOID PhFlushProcessQueryData( } else { - data->ProcessItem->JustProcessed = 1; + data->ProcessItem->JustProcessed = TRUE; } } @@ -2323,7 +2323,7 @@ VOID PhProcessProviderUpdate( // Max. values - if (processItem->ProcessId != NULL) + if (processItem->ProcessId) { if (maxCpuValue < newCpuUsage) { diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 02a3e19e74b4..2ae1234f5551 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -308,8 +308,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 From bcfb0c6a30744b04a50ad70e2aff97320e8a66c3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 12 Nov 2017 19:27:25 +1100 Subject: [PATCH 569/839] Fix typo --- ProcessHacker/main.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 3381dfd1f705..74c1e506236b 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -233,6 +233,19 @@ INT WINAPI wWinMain( PhLoadPlugins(); } + 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)); + } + if (PhStartupParameters.PhSvc) { MSG message; @@ -548,7 +561,7 @@ VOID PhInitializeMitigationPolicy( KEY_WRITE | DELETE, PH_KEY_LOCAL_MACHINE, &policyKeyName, - 0, + OBJ_OPENIF, 0, NULL ))) From d3cd9ddafb128e0786d086870bda0ec31f493bcf Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 12 Nov 2017 19:40:54 +1100 Subject: [PATCH 570/839] Partial revert commit 6e38952c --- ProcessHacker/notifico.c | 4 ++-- ProcessHacker/searchbox.c | 8 ++++---- phlib/guisup.c | 4 ++-- phlib/icotobmp.c | 4 ++-- phlib/settings.c | 4 ++-- tools/peview/searchbox.c | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 15f244a30e4c..4b72394b0adf 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -754,7 +754,7 @@ VOID PhNfpBeginBitmap2( { HDC screenHdc; - screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenHdc = GetDC(NULL); Context->Hdc = CreateCompatibleDC(screenHdc); memset(&Context->Header, 0, sizeof(BITMAPINFOHEADER)); @@ -765,7 +765,7 @@ VOID PhNfpBeginBitmap2( Context->Header.biBitCount = 32; Context->Bitmap = CreateDIBSection(screenHdc, (BITMAPINFO *)&Context->Header, DIB_RGB_COLORS, &Context->Bits, NULL, 0); - DeleteDC(screenHdc); + ReleaseDC(NULL, screenHdc); Context->Initialized = TRUE; } diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index c60f2d6780f0..6fd37ed6c719 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -496,7 +496,7 @@ HICON PhpSearchBitmapToIcon( HBITMAP screenBitmap; ICONINFO iconInfo = { 0 }; - screenDc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenDc = GetDC(NULL); screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); iconInfo.fIcon = TRUE; @@ -506,7 +506,7 @@ HICON PhpSearchBitmapToIcon( icon = CreateIconIndirect(&iconInfo); DeleteObject(screenBitmap); - DeleteDC(screenDc); + ReleaseDC(NULL, screenDc); return icon; } @@ -644,7 +644,7 @@ HBITMAP PhLoadPngImageFromResource( bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biCompression = BI_RGB; - screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenHdc = GetDC(NULL); bufferDc = CreateCompatibleDC(screenHdc); bitmapHandle = CreateDIBSection(screenHdc, &bitmapInfo, DIB_RGB_COLORS, &bitmapBuffer, NULL, 0); @@ -668,7 +668,7 @@ HBITMAP PhLoadPngImageFromResource( DeleteDC(bufferDc); if (screenHdc) - DeleteDC(screenHdc); + ReleaseDC(NULL, screenHdc); if (wicBitmapSource) IWICBitmapSource_Release(wicBitmapSource); diff --git a/phlib/guisup.c b/phlib/guisup.c index a2d3f18aa4b3..e223e14d9d43 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -47,10 +47,10 @@ VOID PhGuiSupportInitialization( PVOID shell32Handle; PVOID shlwapiHandle; - if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) + if (hdc = GetDC(NULL)) { PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - DeleteDC(hdc); + ReleaseDC(NULL, hdc); } shell32Handle = LoadLibrary(L"shell32.dll"); diff --git a/phlib/icotobmp.c b/phlib/icotobmp.c index b70d72c5b1af..9d4ce2878c84 100644 --- a/phlib/icotobmp.c +++ b/phlib/icotobmp.c @@ -160,10 +160,10 @@ HBITMAP PhIconToBitmap( iconRectangle.right = Width; iconRectangle.bottom = Height; - screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenHdc = GetDC(NULL); hdc = CreateCompatibleDC(screenHdc); bitmap = PhpCreateBitmap32(screenHdc, Width, Height, NULL); - DeleteDC(screenHdc); + ReleaseDC(NULL, screenHdc); oldBitmap = SelectObject(hdc, bitmap); paintParams.dwFlags = BPPF_ERASE; diff --git a/phlib/settings.c b/phlib/settings.c index a068218c07b2..fd5ce5fd3e2f 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -117,10 +117,10 @@ static ULONG PhpGetCurrentScale( { HDC hdc; - if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) + if (hdc = GetDC(NULL)) { dpi = GetDeviceCaps(hdc, LOGPIXELSY); - DeleteDC(hdc); + ReleaseDC(NULL, hdc); } PhEndInitOnce(&initOnce); diff --git a/tools/peview/searchbox.c b/tools/peview/searchbox.c index e7774bb44573..1d51bf7c061b 100644 --- a/tools/peview/searchbox.c +++ b/tools/peview/searchbox.c @@ -252,7 +252,7 @@ HBITMAP PhLoadPngImageFromResource( bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biCompression = BI_RGB; - screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenHdc = GetDC(NULL); bufferDc = CreateCompatibleDC(screenHdc); bitmapHandle = CreateDIBSection(screenHdc, &bitmapInfo, DIB_RGB_COLORS, &bitmapBuffer, NULL, 0); @@ -276,7 +276,7 @@ HBITMAP PhLoadPngImageFromResource( DeleteDC(bufferDc); if (screenHdc) - DeleteDC(screenHdc); + ReleaseDC(NULL, screenHdc); if (wicBitmapSource) IWICBitmapSource_Release(wicBitmapSource); @@ -671,7 +671,7 @@ HICON PhpSearchBitmapToIcon( HBITMAP screenBitmap; ICONINFO iconInfo = { 0 }; - screenDc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenDc = GetDC(NULL); screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); iconInfo.fIcon = TRUE; @@ -681,7 +681,7 @@ HICON PhpSearchBitmapToIcon( icon = CreateIconIndirect(&iconInfo); DeleteObject(screenBitmap); - DeleteDC(screenDc); + ReleaseDC(NULL, screenDc); return icon; } From 442945d7ec65979937f3a1a2cca5a19886ec4008 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 13 Nov 2017 03:39:17 +1100 Subject: [PATCH 571/839] Update handle enumeration, Add PhEnumHandlesEx2 --- ProcessHacker/hndlprv.c | 56 +++++++++++++++++++-------------- ProcessHacker/include/hndlprv.h | 1 + phlib/include/phnative.h | 8 +++++ phlib/native.c | 53 +++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 24 deletions(-) diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 75d548132531..d831817461b9 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -3,6 +3,7 @@ * handle provider * * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -84,7 +85,7 @@ PPH_HANDLE_PROVIDER PhCreateHandleProvider( handleProvider->RunStatus = PhOpenProcess( &handleProvider->ProcessHandle, - PROCESS_DUP_HANDLE, + PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, ProcessId ); @@ -133,11 +134,13 @@ PPH_HANDLE_ITEM PhCreateHandleItem( 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; + handleItem->TypeIndex = Handle->ObjectTypeIndex; + + PhPrintPointer(handleItem->HandleString, (PVOID)handleItem->Handle); + PhPrintPointer(handleItem->ObjectString, handleItem->Object); PhPrintPointer(handleItem->GrantedAccessString, UlongToPtr(handleItem->GrantedAccess)); } @@ -299,40 +302,34 @@ NTSTATUS PhEnumHandlesGeneric( 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()) + // * 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 (WindowsVersion >= WINDOWS_8) { - PKPH_PROCESS_HANDLE_INFORMATION handles; + PPROCESS_HANDLE_SNAPSHOT_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))) + 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->HandleCount + sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * handles->NumberOfHandles ); - convertedHandles->NumberOfHandles = handles->HandleCount; + convertedHandles->NumberOfHandles = handles->NumberOfHandles; - for (i = 0; i < handles->HandleCount; i++) + for (i = 0; i < handles->NumberOfHandles; i++) { - convertedHandles->Handles[i].Object = handles->Handles[i].Object; + convertedHandles->Handles[i].Object = 0; 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].HandleValue = (ULONG_PTR)handles->Handles[i].HandleValue; + convertedHandles->Handles[i].GrantedAccess = handles->Handles[i].GrantedAccess; convertedHandles->Handles[i].CreatorBackTraceIndex = 0; - convertedHandles->Handles[i].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex; + convertedHandles->Handles[i].ObjectTypeIndex = (USHORT)handles->Handles[i].ObjectTypeIndex; convertedHandles->Handles[i].HandleAttributes = handles->Handles[i].HandleAttributes; } @@ -503,10 +500,21 @@ VOID PhHandleProviderUpdate( // 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) + 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) diff --git a/ProcessHacker/include/hndlprv.h b/ProcessHacker/include/hndlprv.h index 7238239efc06..d2bb0afd9677 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; diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index a0c4e45bfed2..a3773c94dfeb 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -691,6 +691,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. diff --git a/phlib/native.c b/phlib/native.c index 260c2c4313b7..3a332dcd5bd5 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4141,6 +4141,59 @@ NTSTATUS PhEnumHandlesEx( return status; } +/** + * Enumerates all open handles. + * + * \param ProcessHandle A handle to the process. The handle must have PROCESS_LIMITED_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 = 0x1000; + ULONG returnLength = 0; + + buffer = PhAllocate(bufferSize); + + while ((status = NtQueryInformationProcess( + ProcessHandle, + ProcessHandleInformation, + 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)) + { + *Handles = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + /** * Enumerates all pagefiles. * From aaacd0f7f69d6f18faac220a561c7bc57d07f464 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 13 Nov 2017 03:50:33 +1100 Subject: [PATCH 572/839] Fix typo, Improve PhEnumHandlesEx2 performance --- phlib/native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index 3a332dcd5bd5..f3796615e83e 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4144,7 +4144,7 @@ NTSTATUS PhEnumHandlesEx( /** * Enumerates all open handles. * - * \param ProcessHandle A handle to the process. The handle must have PROCESS_LIMITED_INFORMATION access. + * \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. * @@ -4159,7 +4159,7 @@ NTSTATUS PhEnumHandlesEx2( { NTSTATUS status; PVOID buffer; - ULONG bufferSize = 0x1000; + ULONG bufferSize = 0x8000; ULONG returnLength = 0; buffer = PhAllocate(bufferSize); From 4b8f8b44152ff3756f3938c79e256f993ec785b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 13 Nov 2017 11:33:37 +1100 Subject: [PATCH 573/839] NetworkTools: Fix memory leak --- plugins/NetworkTools/nettools.h | 1 - plugins/NetworkTools/pages.c | 1 + plugins/NetworkTools/update.c | 40 +++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index 4255eaa3d2f9..ff6aaa2f3427 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -274,7 +274,6 @@ typedef struct _PH_UPDATER_CONTEXT PPH_STRING FileDownloadUrl; PPH_STRING RevVersion; PPH_STRING Size; - PPH_STRING SetupFilePath; } PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; VOID TaskDialogLinkClicked( diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index d5fb5a254762..8479aa9886ff 100644 --- a/plugins/NetworkTools/pages.c +++ b/plugins/NetworkTools/pages.c @@ -83,6 +83,7 @@ HRESULT CALLBACK CheckingForUpdatesDbCallbackProc( 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; diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index 774069ad8e55..39c0ee32a5b5 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -43,9 +43,6 @@ VOID FreeUpdateContext( if (Context->Size) PhDereferenceObject(Context->Size); - if (Context->SetupFilePath) - PhDereferenceObject(Context->SetupFilePath); - PhDereferenceObject(Context); } @@ -196,10 +193,14 @@ NTSTATUS GeoIPUpdateThread( HANDLE tempFileHandle = NULL; PPH_STRING fwLinkUrl = NULL; 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; @@ -212,14 +213,14 @@ NTSTATUS GeoIPUpdateThread( if (!(fwLinkUrl = QueryFwLinkUrl(context))) goto CleanupExit; - context->SetupFilePath = PhCreateCacheFile(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); + zipFilePath = PhCreateCacheFile(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); - if (PhIsNullOrEmptyString(context->SetupFilePath)) + if (PhIsNullOrEmptyString(zipFilePath)) goto CleanupExit; 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_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -371,18 +372,11 @@ NTSTATUS GeoIPUpdateThread( } { - PPH_STRING dbpath; - PPH_BYTES mmdbGzPath; - gzFile gzfile; - dbpath = PhGetExpandStringSetting(SETTING_NAME_DB_LOCATION); if (PhIsNullOrEmptyString(dbpath)) PhMoveReference(&dbpath, PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\GeoLite2-Country.mmdb")); - PhAutoDereferenceObject(dbpath); - mmdbGzPath = PH_AUTO(PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath))); - if (RtlDoesFileExists_U(PhGetString(dbpath))) { if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(dbpath)))) @@ -395,13 +389,17 @@ NTSTATUS GeoIPUpdateThread( PPH_STRING fullPath; ULONG indexOfFileName; - if (fullPath = PH_AUTO(PhGetFullPath(dbpath->Buffer, &indexOfFileName))) + if (fullPath = PhGetFullPath(dbpath->Buffer, &indexOfFileName)) { if (indexOfFileName != -1) PhCreateDirectory(PhaSubstring(fullPath, 0, indexOfFileName)); + + PhDereferenceObject(fullPath); } } + mmdbGzPath = PhConvertUtf16ToUtf8(PhGetString(zipFilePath)); + if (gzfile = gzopen(mmdbGzPath->Buffer, "rb")) { HANDLE mmdbFileHandle; @@ -452,12 +450,20 @@ NTSTATUS GeoIPUpdateThread( } gzclose(gzfile); + gzfile = NULL; } } } CleanupExit: + if (gzfile) + gzclose(gzfile); + if (mmdbGzPath) + PhDereferenceObject(mmdbGzPath); + if (dbpath) + PhDereferenceObject(dbpath); + if (tempFileHandle) NtClose(tempFileHandle); @@ -469,10 +475,10 @@ NTSTATUS GeoIPUpdateThread( if (versionString) PhDereferenceObject(versionString); - if (context->SetupFilePath) + if (zipFilePath) { - PhDeleteCacheFile(context->SetupFilePath); - PhDereferenceObject(context->SetupFilePath); + PhDeleteCacheFile(zipFilePath); + PhDereferenceObject(zipFilePath); } if (context->DialogHandle) From 1f39694e5a79ea18e6c8f5a8ed56e9fd45042f17 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 13 Nov 2017 20:21:00 +1100 Subject: [PATCH 574/839] Add (experimental) EventQueue support, Improve process/service/network tab performance, Remove legacy process/service providor hacks --- ProcessHacker/hidnproc.c | 4 +- ProcessHacker/include/mainwnd.h | 18 ++-- ProcessHacker/include/mainwndp.h | 78 +++++------------ ProcessHacker/include/phuisup.h | 15 +--- ProcessHacker/include/procprv.h | 4 +- ProcessHacker/mainwnd.c | 121 ++------------------------ ProcessHacker/mwpgnet.c | 93 +++++++++++++------- ProcessHacker/mwpgproc.c | 70 +++++++++------ ProcessHacker/mwpgsrv.c | 142 +++++++++++-------------------- ProcessHacker/netprv.c | 2 +- ProcessHacker/procprv.c | 35 ++------ ProcessHacker/srvctl.c | 6 +- ProcessHacker/srvprv.c | 47 +++++----- 13 files changed, 236 insertions(+), 399 deletions(-) diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index f3b342c8c2fc..81745a5528fb 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -565,8 +565,8 @@ static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( processItem = PhCreateProcessItem(Entry->ProcessId); // Mark the process as terminated if necessary. - if (Entry->Type == TerminatedProcess) - processItem->State |= PH_PROCESS_ITEM_REMOVED; + //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)) diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index f1d96ac8c202..8c24ec999766 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -8,19 +8,19 @@ 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_PROCESS_ADDED_DEPRECATED (WM_APP + 101) +//#define WM_PH_PROCESS_MODIFIED_DEPRECATED (WM_APP + 102) +//#define WM_PH_PROCESS_REMOVED_DEPRECATED (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_SERVICE_ADDED_DEPRECATED (WM_APP + 105) +//#define WM_PH_SERVICE_MODIFIED_DEPRECATED (WM_APP + 106) +//#define WM_PH_SERVICE_REMOVED_DEPRECATED (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_ITEM_ADDED_DEPRECATED (WM_APP + 109) +//#define WM_PH_NETWORK_ITEM_MODIFIED_DEPRECATED (WM_APP + 110) +//#define WM_PH_NETWORK_ITEM_REMOVED_DEPRECATED (WM_APP + 111) #define WM_PH_NETWORK_ITEMS_UPDATED (WM_APP + 112) // begin_phapppub diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 1f69c58882fb..13d25a772259 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -121,28 +121,6 @@ ULONG_PTR PhMwpOnUserMessage( _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( @@ -276,6 +254,7 @@ 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, @@ -352,27 +331,15 @@ VOID NTAPI PhMwpProcessesUpdatedHandler( _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 + _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, @@ -420,27 +387,15 @@ VOID NTAPI PhMwpServicesUpdatedHandler( _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 + _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, @@ -469,21 +424,28 @@ VOID PhMwpInitializeNetworkMenu( _In_ ULONG NumberOfNetworkItems ); -VOID PhMwpOnNetworkItemAdded( - _In_ ULONG RunId, - _In_ _Assume_refs_(1) PPH_NETWORK_ITEM NetworkItem +VOID PhMwpNetworkItemAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpNetworkItemModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ); -VOID PhMwpOnNetworkItemModified( - _In_ PPH_NETWORK_ITEM NetworkItem +VOID PhMwpNetworkItemRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ); -VOID PhMwpOnNetworkItemRemoved( - _In_ PPH_NETWORK_ITEM NetworkItem +VOID PhMwpNetworkItemsUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ); VOID PhMwpOnNetworkItemsUpdated( - VOID + _In_ ULONG RunId ); // Users diff --git a/ProcessHacker/include/phuisup.h b/ProcessHacker/include/phuisup.h index 23f439a4cff8..d1860f1f49fa 100644 --- a/ProcessHacker/include/phuisup.h +++ b/ProcessHacker/include/phuisup.h @@ -44,7 +44,6 @@ FORCEINLINE VOID PhChangeShStateTn( ULONG64 tickCount; \ BOOLEAN preferFullInvalidate; \ HANDLE stateListHandle; \ - BOOLEAN redrawDisabled = FALSE; \ BOOLEAN needsFullInvalidate = FALSE; \ \ if (!StateList || StateList->Count == 0) \ @@ -72,21 +71,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 +87,6 @@ FORCEINLINE VOID PhChangeShStateTn( \ if (TreeNewHandleForUpdate) \ { \ - if (redrawDisabled) \ - TreeNew_SetRedraw((TreeNewHandleForUpdate), TRUE); \ if (needsFullInvalidate) \ { \ InvalidateRect((TreeNewHandleForUpdate), NULL, FALSE); \ diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 3e433d740952..7f82eabee7de 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -164,7 +164,7 @@ typedef struct _PH_PROCESS_ITEM ULONG IsImmersive : 1; ULONG IsWow64Valid : 1; ULONG IsPartiallySuspended : 1; - ULONG AddedEventSent : 1; + ULONG Unused : 1; ULONG IsProtectedProcess : 1; ULONG IsSecureProcess : 1; ULONG IsSubsystemProcess : 1; @@ -342,7 +342,7 @@ PhGetStatisticsTimeString( // end_phapppub VOID PhFlushProcessQueryData( - _In_ BOOLEAN SendModifiedEvent + VOID ); VOID PhProcessProviderUpdate( diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 46186dfe4219..9b039b2c7dc8 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -385,11 +385,6 @@ VOID PhMwpApplyUpdateInterval( { 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( @@ -1722,12 +1717,15 @@ VOID PhMwpOnTimer( KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); break; - default: - NOTHING; + case 3: + { + KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); + } break; } - PhFlushProcessQueryData(TRUE); + PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); + PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); } } @@ -2056,76 +2054,19 @@ ULONG_PTR PhMwpOnUserMessage( 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); + PhMwpOnProcessesUpdated((ULONG)WParam); } 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); + PhMwpOnServicesUpdated((ULONG)WParam); } break; case WM_PH_NETWORK_ITEMS_UPDATED: { - PhMwpOnNetworkItemsUpdated(); + PhMwpOnNetworkItemsUpdated((ULONG)WParam); } break; } @@ -2133,50 +2074,6 @@ ULONG_PTR PhMwpOnUserMessage( 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 ) diff --git a/ProcessHacker/mwpgnet.c b/ProcessHacker/mwpgnet.c index 68832d0a5438..e87dd26a01c9 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. * @@ -38,6 +39,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 +48,6 @@ static PH_CALLBACK_REGISTRATION NetworkItemsUpdatedRegistration; static BOOLEAN NetworkFirstTime = TRUE; static BOOLEAN NetworkTreeListLoaded = FALSE; -static BOOLEAN NetworkNeedsRedraw = FALSE; BOOLEAN PhMwpNetworkPageCallback( _In_ struct _PH_MAIN_TAB_PAGE *Page, @@ -61,6 +62,8 @@ BOOLEAN PhMwpNetworkPageCallback( { PhMwpNetworkPage = Page; + PhInitializeProviderEventQueue(&PhMwpNetworkEventQueue, 100); + PhRegisterCallback( &PhNetworkItemAddedEvent, PhMwpNetworkItemAddedHandler, @@ -309,52 +312,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; - - if (!NetworkNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, FALSE); - NetworkNeedsRedraw = TRUE; - } + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - 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 + ) +{ + PostMessage(PhMainWndHandle, WM_PH_NETWORK_ITEMS_UPDATED, PhGetRunIdProvider(&PhMwpNetworkProviderRegistration), 0); } VOID PhMwpOnNetworkItemsUpdated( - VOID + _In_ ULONG RunId ) { - PhTickNetworkNodes(); + PPH_PROVIDER_EVENT events; + ULONG count; + ULONG i; + + events = PhFlushProviderEventQueue(&PhMwpNetworkEventQueue, RunId, &count); - if (NetworkNeedsRedraw) + 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 142240387119..2d12dada4116 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 dmex * * This file is part of Process Hacker. * @@ -43,6 +44,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; @@ -50,9 +52,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; @@ -69,6 +69,8 @@ BOOLEAN PhMwpProcessesPageCallback( { PhMwpProcessesPage = Page; + PhInitializeProviderEventQueue(&PhMwpProcessEventQueue, 100); + PhRegisterCallback( &PhProcessAddedEvent, PhMwpProcessAddedHandler, @@ -709,12 +711,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( @@ -724,7 +721,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( @@ -736,7 +733,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( @@ -744,7 +741,7 @@ VOID NTAPI PhMwpProcessesUpdatedHandler( _In_opt_ PVOID Context ) { - PostMessage(PhMainWndHandle, WM_PH_PROCESSES_UPDATED, 0, 0); + PostMessage(PhMainWndHandle, WM_PH_PROCESSES_UPDATED, PhGetRunIdProvider(&PhMwpProcessProviderRegistration), 0); } VOID PhMwpOnProcessAdded( @@ -754,12 +751,6 @@ VOID PhMwpOnProcessAdded( { PPH_PROCESS_NODE processNode; - if (!ProcessesNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpProcessTreeNewHandle, FALSE); - ProcessesNeedsRedraw = TRUE; - } - processNode = PhAddProcessNode(ProcessItem, RunId); if (RunId != 1) @@ -840,12 +831,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) @@ -872,9 +857,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(); @@ -884,11 +901,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 8862e02ded39..c5ac2d0119f9 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,6 +61,8 @@ BOOLEAN PhMwpServicesPageCallback( { PhMwpServicesPage = Page; + PhInitializeProviderEventQueue(&PhMwpServiceEventQueue, 100); + PhRegisterCallback( &PhServiceAddedEvent, PhMwpServiceAddedHandler, @@ -163,22 +164,6 @@ VOID PhMwpNeedServiceTreeList( 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 +343,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 +356,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 +366,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 +374,7 @@ VOID NTAPI PhMwpServicesUpdatedHandler( _In_opt_ PVOID Context ) { - PostMessage(PhMainWndHandle, WM_PH_SERVICES_UPDATED, 0, 0); + PostMessage(PhMainWndHandle, WM_PH_SERVICES_UPDATED, PhGetRunIdProvider(&PhMwpServiceProviderRegistration), 0); } VOID PhMwpOnServiceAdded( @@ -404,24 +384,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 +408,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 +490,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 +508,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/netprv.c b/ProcessHacker/netprv.c index 8930c1ec20fb..a096ec281b85 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -463,7 +463,7 @@ VOID PhFlushNetworkQueryData( PSLIST_ENTRY entry; PPH_NETWORK_ITEM_QUERY_DATA data; - if (RtlQueryDepthSList(&PhNetworkItemQueryListHead) == 0) + if (!RtlFirstEntrySList(&PhNetworkItemQueryListHead)) return; entry = RtlInterlockedFlushSList(&PhNetworkItemQueryListHead); diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index dc516afedcd9..cf5b07e43b64 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -3,6 +3,7 @@ * process provider * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -1801,14 +1802,13 @@ PPH_STRING PhGetStatisticsTimeString( } VOID PhFlushProcessQueryData( - _In_ BOOLEAN SendModifiedEvent + VOID ) { PSLIST_ENTRY entry; PPH_PROCESS_QUERY_DATA data; - BOOLEAN processed; - if (RtlQueryDepthSList(&PhProcessQueryDataListHead) == 0) + if (!RtlFirstEntrySList(&PhProcessQueryDataListHead)) return; entry = RtlInterlockedFlushSList(&PhProcessQueryDataListHead); @@ -1817,39 +1817,18 @@ VOID PhFlushProcessQueryData( { 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 = TRUE; - } - } + data->ProcessItem->JustProcessed = TRUE; PhDereferenceObject(data->ProcessItem); PhFree(data); @@ -2106,10 +2085,7 @@ VOID PhProcessProviderUpdate( 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); @@ -2135,7 +2111,7 @@ VOID PhProcessProviderUpdate( } // Go through the queued process query data. - PhFlushProcessQueryData(FALSE); + PhFlushProcessQueryData(); if (sysTotalTime == 0) sysTotalTime = -1; // max. value @@ -2231,7 +2207,6 @@ VOID PhProcessProviderUpdate( // 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. diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 8b122289a3ff..4992df84a96e 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -41,6 +41,8 @@ typedef struct _PH_SERVICES_CONTEXT HWND WindowHandle; } 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 @@ -108,7 +110,7 @@ static VOID NTAPI ServiceModifiedHandler( copy = PhAllocateCopy(serviceModifiedData, sizeof(PH_SERVICE_MODIFIED_DATA)); - PostMessage(servicesContext->WindowHandle, WM_PH_SERVICE_MODIFIED, 0, (LPARAM)copy); + PostMessage(servicesContext->WindowHandle, WM_PH_SERVICE_PAGE_MODIFIED, 0, (LPARAM)copy); } VOID PhpFixProcessServicesControls( @@ -398,7 +400,7 @@ INT_PTR CALLBACK PhpServicesPageProc( PhLayoutManagerLayout(&servicesContext->LayoutManager); } break; - case WM_PH_SERVICE_MODIFIED: + case WM_PH_SERVICE_PAGE_MODIFIED: { PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)lParam; PPH_SERVICE_ITEM serviceItem = NULL; diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 1b8ea63d5b2e..137c1f58a4a8 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -521,6 +521,33 @@ VOID PhpQueueServiceQuery( PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryWorker, data, NULL, &environment); } +VOID PhFlushServiceQueryData( + VOID + ) +{ + PSLIST_ENTRY entry; + PPH_SERVICE_QUERY_DATA data; + + if (!RtlFirstEntrySList(&PhpServiceQueryListHead)) + return; + + entry = RtlInterlockedFlushSList(&PhpServiceQueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_SERVICE_QUERY_DATA, ListEntry); + entry = entry->Next; + + data->ServiceItem->VerifyResult = data->VerifyResult; + data->ServiceItem->VerifySignerName = data->VerifySignerName; + + data->ServiceItem->NeedsVerifyUpdate = TRUE; + + PhDereferenceObject(data->ServiceItem); + PhFree(data); + } +} + VOID PhServiceProviderUpdate( _In_ PVOID Object ) @@ -604,25 +631,7 @@ VOID PhServiceProviderUpdate( } // Go through the queued services query data. - { - PSLIST_ENTRY entry; - PPH_SERVICE_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&PhpServiceQueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_SERVICE_QUERY_DATA, ListEntry); - entry = entry->Next; - - data->ServiceItem->VerifyResult = data->VerifyResult; - data->ServiceItem->VerifySignerName = data->VerifySignerName; - data->ServiceItem->NeedsVerifyUpdate = TRUE; - - PhDereferenceObject(data->ServiceItem); - PhFree(data); - } - } + PhFlushServiceQueryData(); // Look for dead services. { From 9f6d17ddd3d43011c195821d9ea3c5df537d6056 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 14 Nov 2017 05:14:11 +1100 Subject: [PATCH 575/839] Fix crash --- phlib/native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phlib/native.c b/phlib/native.c index f3796615e83e..aa40f5acbe95 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4163,6 +4163,7 @@ NTSTATUS PhEnumHandlesEx2( ULONG returnLength = 0; buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); while ((status = NtQueryInformationProcess( ProcessHandle, @@ -4180,6 +4181,7 @@ NTSTATUS PhEnumHandlesEx2( return STATUS_INSUFFICIENT_RESOURCES; buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); } if (NT_SUCCESS(status)) From 889f317c35b0e1fb28a967b7be0517ff8964bc2a Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 14 Nov 2017 06:56:12 +1100 Subject: [PATCH 576/839] Fix incorrect extension layout --- phnt/include/ntexapi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index f7bea46bf049..033d7963f31a 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -2681,7 +2681,7 @@ typedef struct _PROCESS_DISK_COUNTERS } PROCESS_DISK_COUNTERS, *PPROCESS_DISK_COUNTERS; // private -typedef struct _ENERGY_STATE_DURATION +typedef union _ENERGY_STATE_DURATION { union { @@ -2715,8 +2715,8 @@ typedef struct _PROCESS_ENERGY_VALUES ULONG CompositionDirtyGenerated; ULONG CompositionDirtyPropagated; ULONG Reserved1; - ULONGLONG AttributedCycles[2][4]; - ULONGLONG WorkOnBehalfCycles[2][4]; + ULONGLONG AttributedCycles[4][2]; + ULONGLONG WorkOnBehalfCycles[4][2]; } PROCESS_ENERGY_VALUES, *PPROCESS_ENERGY_VALUES; typedef struct _TIMELINE_BITMAP From f5cef286069664cf1a26aeb5b398e53532b64e7e Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 14 Nov 2017 19:29:39 +1100 Subject: [PATCH 577/839] Update PhEnumHandlesEx2 --- phlib/native.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/phlib/native.c b/phlib/native.c index aa40f5acbe95..a1dbfb769808 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -4159,39 +4159,43 @@ NTSTATUS PhEnumHandlesEx2( { NTSTATUS status; PVOID buffer; - ULONG bufferSize = 0x8000; + ULONG bufferSize; ULONG returnLength = 0; + ULONG attempts = 0; + bufferSize = 0x8000; buffer = PhAllocate(bufferSize); memset(buffer, 0, bufferSize); - while ((status = NtQueryInformationProcess( + status = NtQueryInformationProcess( ProcessHandle, ProcessHandleInformation, buffer, bufferSize, &returnLength - )) == STATUS_INFO_LENGTH_MISMATCH) + ); + + while (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 8) { 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; - + 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; } From 03e2c805eb8e241c6a8cb68d06c136cd0b6e056f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 08:00:12 +1100 Subject: [PATCH 578/839] Add macro --- phlib/include/phnative.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index a3773c94dfeb..1ab3c6574068 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -638,6 +638,12 @@ PhGetKernelFileName( NULL \ ) +#define PH_PROCESS_EXTENSION(Process) \ + ((PSYSTEM_PROCESS_INFORMATION_EXTENSION)PTR_ADD_OFFSET((Process), \ + FIELD_OFFSET(SYSTEM_PROCESS_INFORMATION, Threads) + \ + sizeof(SYSTEM_THREAD_INFORMATION) * \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NumberOfThreads)) + PHLIBAPI NTSTATUS NTAPI From 8b448663671e59eba240f37e0ea6ebb620fbbb7d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 08:13:29 +1100 Subject: [PATCH 579/839] Remove deprecated symsrv flags --- phlib/symprv.c | 9 ++++++++- tools/peview/pdb.c | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/phlib/symprv.c b/phlib/symprv.c index 3be8511f4924..ac3a4da3045e 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -156,7 +156,14 @@ VOID PhSymbolProviderCompleteInitialization( UnDecorateSymbolNameW_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolNameW", 0); if (SymGetOptions_I && SymSetOptions_I) - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_UNDNAME); + { + 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 + ); + } } PPH_SYMBOL_PROVIDER PhCreateSymbolProvider( diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 78475e31ef3d..53239934e5f2 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -2328,7 +2328,12 @@ NTSTATUS PeDumpFileSymbols( SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); SymSetContext_I = PhGetProcedureAddress(dbghelpHandle, "SymSetContext", 0); - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEBUG | SYMOPT_UNDNAME); + 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 (!SymInitialize_I(NtCurrentProcess(), NULL, FALSE)) return 1; From 3dd162d92cbced563d7153b3fe899c6df1032c08 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 08:17:07 +1100 Subject: [PATCH 580/839] Add JobObjectId column to processes tab, Add ProcessSequenceNumber support, Fix timezone changes breaking process' parenting --- ProcessHacker/actions.c | 25 ++++-- ProcessHacker/hidnproc.c | 4 +- ProcessHacker/include/procprv.h | 11 +-- ProcessHacker/include/proctree.h | 4 +- ProcessHacker/mwpgproc.c | 6 +- ProcessHacker/procprv.c | 114 +++++++++++++++++++++------ ProcessHacker/procrec.c | 6 +- ProcessHacker/proctree.c | 28 ++++++- ProcessHacker/prpggen.c | 6 +- plugins/ExtendedNotifications/main.c | 6 +- 10 files changed, 151 insertions(+), 59 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 71ead0b015a2..868753e1df70 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1082,13 +1082,28 @@ BOOLEAN PhpUiTerminateTreeProcess( { if (processItem = PhReferenceProcessItem(process->UniqueProcessId)) { - // Check the creation time to make sure it is a descendant. - if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart) + if (WindowsVersion >= WINDOWS_10_RS3) { - if (!PhpUiTerminateTreeProcess(hWnd, processItem, Processes, Success)) + // Check the sequence number to make sure it is a descendant. + if (processItem->ProcessSequenceNumber >= Process->ProcessSequenceNumber) { - PhDereferenceObject(processItem); - return FALSE; + 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; + } } } diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index 81745a5528fb..f3b342c8c2fc 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -565,8 +565,8 @@ static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( processItem = PhCreateProcessItem(Entry->ProcessId); // Mark the process as terminated if necessary. - //if (Entry->Type == TerminatedProcess) - // processItem->State |= PH_PROCESS_ITEM_REMOVED; + 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)) diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 7f82eabee7de..1237d1c8995a 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -216,7 +216,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,7 +229,9 @@ typedef struct _PH_PROCESS_ITEM PH_UINTPTR_DELTA PrivateBytesDelta; PPH_STRING PackageFullName; - PH_QUEUED_LOCK RemoveLock; + ULONGLONG ProcessSequenceNumber; + PH_KNOWN_PROCESS_TYPE KnownProcessType; + ULONG JobObjectId; } PH_PROCESS_ITEM, *PPH_PROCESS_ITEM; // end_phapppub @@ -248,6 +250,7 @@ typedef struct _PH_PROCESS_RECORD HANDLE ProcessId; HANDLE ParentProcessId; ULONG SessionId; + ULONGLONG ProcessSequenceNumber; LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime; @@ -396,9 +399,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 3535bfbd605e..5ddd6ef7b0b5 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -90,8 +90,9 @@ #define PHPRTLC_FILEMODIFIEDTIME 77 #define PHPRTLC_FILESIZE 78 #define PHPRTLC_SUBPROCESSCOUNT 79 +#define PHPRTLC_JOBOBJECTID 80 -#define PHPRTLC_MAXIMUM 80 +#define PHPRTLC_MAXIMUM 81 #define PHPRTLC_IOGROUP_COUNT 9 #define PHPN_WSCOUNTERS 0x1 @@ -215,6 +216,7 @@ typedef struct _PH_PROCESS_NODE PPH_STRING FileModifiedTimeText; PPH_STRING FileSizeText; PPH_STRING SubprocessCountText; + WCHAR JobObjectIdText[PH_INT32_STR_LEN_1]; // Graph buffers PH_GRAPH_BUFFERS CpuGraphBuffers; diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 2d12dada4116..f0b222193d3d 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -759,11 +759,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; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index cf5b07e43b64..c7033fd3c584 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1316,6 +1316,22 @@ VOID PhpFillProcessItemStage2( processItem->ImportModules = Data->ImportModules; } +VOID PhpFillProcessItemExtension( + _Inout_ PPH_PROCESS_ITEM ProcessItem, + _In_ PSYSTEM_PROCESS_INFORMATION Process + ) +{ + PSYSTEM_PROCESS_INFORMATION_EXTENSION processExtension; + + if (WindowsVersion < WINDOWS_10_RS3) + 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 @@ -1413,6 +1429,14 @@ VOID PhpFillProcessItem( } } + // Known Process Type + { + ProcessItem->KnownProcessType = PhGetProcessKnownTypeEx( + ProcessItem->ProcessId, + ProcessItem->FileName + ); + } + NtClose(processHandle); } @@ -1759,7 +1783,7 @@ BOOLEAN PhGetStatisticsTime( { // 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; + index = PhTimeSequenceNumber - ProcessItem->TimeSequenceNumber + Index; if (index >= PhTimeHistory.Count) { @@ -1987,10 +2011,20 @@ VOID PhProcessProviderUpdate( { PPH_PROCESS_ITEM processItem; - if ((processItem = PhpLookupProcessItem(process->UniqueProcessId)) && processItem->CreateTime.QuadPart == process->CreateTime.QuadPart) - sysTotalCycleTime += process->CycleTime - processItem->CycleTimeDelta.Value; // existing process + if (WindowsVersion >= WINDOWS_10_RS3) + { + 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 - sysTotalCycleTime += process->CycleTime; // new process + { + 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)); @@ -2023,6 +2057,8 @@ VOID PhProcessProviderUpdate( { 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 @@ -2044,7 +2080,18 @@ VOID PhProcessProviderUpdate( processEntry = (PSYSTEM_PROCESS_INFORMATION)processEntry->UniqueProcessKey; } - if (!processEntry || processEntry->CreateTime.QuadPart != processItem->CreateTime.QuadPart) + if (WindowsVersion >= WINDOWS_10_RS3) + { + 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; @@ -2139,7 +2186,8 @@ VOID PhProcessProviderUpdate( // Create the process item and fill in basic information. processItem = PhCreateProcessItem(process->UniqueProcessId); PhpFillProcessItem(processItem, process); - processItem->SequenceNumber = PhTimeSequenceNumber; + PhpFillProcessItemExtension(processItem, process); + processItem->TimeSequenceNumber = PhTimeSequenceNumber; processRecord = PhpCreateProcessRecord(processItem); PhpAddProcessRecord(processRecord); @@ -2224,6 +2272,7 @@ VOID PhProcessProviderUpdate( PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); PhpUpdateDynamicInfoProcessItem(processItem, process); + PhpFillProcessItemExtension(processItem, process); // Update the deltas. PhUpdateDelta(&processItem->CpuKernelDelta, process->KernelTime.QuadPart); @@ -2239,7 +2288,7 @@ VOID PhProcessProviderUpdate( PhUpdateDelta(&processItem->CycleTimeDelta, process->CycleTime); PhUpdateDelta(&processItem->PrivateBytesDelta, process->PagefileUsage); - processItem->SequenceNumber++; + processItem->TimeSequenceNumber++; PhAddItemCircularBuffer_ULONG64(&processItem->IoReadHistory, processItem->IoReadDelta.Delta); PhAddItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, processItem->IoWriteDelta.Delta); PhAddItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, processItem->IoOtherDelta.Delta); @@ -2488,6 +2537,7 @@ PPH_PROCESS_RECORD PhpCreateProcessRecord( 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); @@ -2839,30 +2889,40 @@ VOID PhPurgeProcessRecords( } PPH_PROCESS_ITEM PhReferenceProcessItemForParent( - _In_ HANDLE ParentProcessId, - _In_ HANDLE ProcessId, - _In_ PLARGE_INTEGER CreateTime + _In_ PPH_PROCESS_ITEM ProcessItem ) { - PPH_PROCESS_ITEM processItem; + PPH_PROCESS_ITEM parentProcessItem; - if (ParentProcessId == ProcessId) // for cases where the parent PID = PID (e.g. System Idle Process) + if (ProcessItem->ParentProcessId == ProcessItem->ProcessId) // for cases where the parent PID = PID (e.g. System Idle Process) return NULL; PhAcquireQueuedLockShared(&PhProcessHashSetLock); - processItem = PhpLookupProcessItem(ParentProcessId); + parentProcessItem = PhpLookupProcessItem(ProcessItem->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); + if (WindowsVersion >= WINDOWS_10_RS3) + { + // 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->ProcessSequenceNumber <= ProcessItem->ProcessSequenceNumber) + PhReferenceObject(parentProcessItem); + else + parentProcessItem = NULL; + } else - processItem = NULL; + { + // 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 processItem; + return parentProcessItem; } PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( @@ -2875,10 +2935,20 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( processItem = PhpLookupProcessItem(Record->ProcessId); - if (processItem && processItem->CreateTime.QuadPart == Record->CreateTime.QuadPart) - PhReferenceObject(processItem); + if (WindowsVersion >= WINDOWS_10_RS3) + { + if (processItem && processItem->ProcessSequenceNumber == Record->ProcessSequenceNumber) + PhReferenceObject(processItem); + else + processItem = NULL; + } else - processItem = NULL; + { + if (processItem && processItem->CreateTime.QuadPart == Record->CreateTime.QuadPart) + PhReferenceObject(processItem); + else + processItem = NULL; + } PhReleaseQueuedLockShared(&PhProcessHashSetLock); diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 8332a7180d2d..fb654e1aa669 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -142,11 +142,7 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( { PPH_PROCESS_ITEM parentProcess; - if (parentProcess = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - )) + if (parentProcess = PhReferenceProcessItemForParent(processItem)) { CLIENT_ID clientId; diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index fcebcb953846..0d4e674de3ca 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -208,6 +208,7 @@ VOID PhInitializeProcessTreeList( 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); PhAddTreeNewColumnEx(hwnd, PHPRTLC_SUBPROCESSCOUNT, FALSE, L"Subprocesses", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_JOBOBJECTID, FALSE, L"Job Object ID", 50, PH_ALIGN_LEFT, -1, 0, TRUE); TreeNew_SetRedraw(hwnd, TRUE); @@ -296,9 +297,16 @@ FORCEINLINE BOOLEAN PhpValidateParentCreateTime( _In_ PPH_PROCESS_NODE Parent ) { - return - PH_IS_FAKE_PROCESS_ID(Child->ProcessId) || - Parent->ProcessItem->CreateTime.QuadPart <= Child->ProcessItem->CreateTime.QuadPart; + 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( @@ -1827,6 +1835,12 @@ BEGIN_SORT_FUNCTION(Subprocesses) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(JobObjectId) +{ + sortResult = int64cmp(processItem1->JobObjectId, processItem2->JobObjectId); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpProcessTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -1946,7 +1960,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( SORT_FUNCTION(TimeStamp), SORT_FUNCTION(FileModifiedTime), SORT_FUNCTION(FileSize), - SORT_FUNCTION(Subprocesses) + SORT_FUNCTION(Subprocesses), + SORT_FUNCTION(JobObjectId) }; int (__cdecl *sortFunction)(const void *, const void *); @@ -2639,6 +2654,11 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getCellText->Text = node->SubprocessCountText->sr; } break; + case PHPRTLC_JOBOBJECTID: + { + PhpFormatInt32GroupDigits(processItem->JobObjectId, node->JobObjectIdText, sizeof(node->JobObjectIdText), &getCellText->Text); + } + break; default: return FALSE; } diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 4d4ffca566eb..a6d288b7c7d9 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -284,11 +284,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( // Parent - if (parentProcess = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - )) + if (parentProcess = PhReferenceProcessItemForParent(processItem)) { clientId.UniqueProcess = parentProcess->ProcessId; clientId.UniqueThread = NULL; diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index 1ec31acc37d7..d8940a4879d5 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -487,11 +487,7 @@ VOID NotifyGrowl( notification = GrowlNotifications[0]; title = processItem->ProcessName; - parentProcessItem = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - ); + parentProcessItem = PhReferenceProcessItemForParent(processItem); message = PhaFormatString( L"The process %s (%lu) was started by %s.", From 70bed5c0debfff516ec9d9d8be8e68792abaa245 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 08:19:42 +1100 Subject: [PATCH 581/839] Add WMI Provider Host process properties page for WmiPrvSE.exe processes --- ProcessHacker/ProcessHacker.rc | 16 + ProcessHacker/ProcessHacker.vcxproj | 9 +- ProcessHacker/ProcessHacker.vcxproj.filters | 3 + ProcessHacker/include/procprpp.h | 7 + ProcessHacker/procprp.c | 11 + ProcessHacker/prpgwmi.c | 755 ++++++++++++++++++++ ProcessHacker/resource.h | 1 + ProcessHacker/settings.c | 2 + 8 files changed, 800 insertions(+), 4 deletions(-) create mode 100644 ProcessHacker/prpgwmi.c diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index e44cfe89d3de..02976aa4cd90 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1879,6 +1879,14 @@ BEGIN 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -2433,6 +2441,14 @@ BEGIN TOPMARGIN, 2 BOTTOMMARGIN, 174 END + + IDD_PROCWMIPROVIDERS, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END END #endif // APSTUDIO_INVOKED diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index b998678db459..9b4c0047495c 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -96,7 +96,7 @@ true - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX86 @@ -128,7 +128,7 @@ true - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows MachineX64 @@ -165,7 +165,7 @@ Guard - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -203,7 +203,7 @@ Guard - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) true Windows true @@ -322,6 +322,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index f93d2a3fb8b1..b969ed3f7d8f 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -381,6 +381,9 @@ Process Hacker + + Process Hacker + diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index 2e315c4db325..b38f340a7a58 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -178,6 +178,13 @@ INT_PTR CALLBACK PhpProcessServicesDlgProc( _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) diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 2380d83ec95b..a9909f48b557 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -650,6 +650,17 @@ NTSTATUS PhpProcessPropertiesThreadStart( 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) { diff --git a/ProcessHacker/prpgwmi.c b/ProcessHacker/prpgwmi.c new file mode 100644 index 000000000000..d24f7c4d63ca --- /dev/null +++ b/ProcessHacker/prpgwmi.c @@ -0,0 +1,755 @@ +/* + * Process Hacker - + * Process properties: WMI Providor page + * + * 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 + +#define CINTERFACE +#define COBJMACROS +#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 ( + 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))) + { + 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 (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = (PPH_WMI_CONTEXT)propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context = propPageContext->Context = PhAllocate(sizeof(PH_WMI_CONTEXT)); + memset(context, 0, 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); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + + PhpRefreshWmiProviders(hwndDlg, context, processItem); + + PhRegisterCallback( + &PhProcessesUpdatedEvent, + PhpWmiProviderUpdateHandler, + context, + &context->ProcessesUpdatedRegistration + ); + } + break; + case WM_DESTROY: + { + PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); + + PhSaveListViewColumnsToSetting(L"WmiProviderListViewColumns", context->ListViewHandle); + + PhpClearWmiProviderItems(context); + PhDereferenceObject(context->WmiProviderList); + + PhFree(context); + + 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, context->ListViewHandle, dialogItem, PH_ANCHOR_ALL); + + PhDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + 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"Resume", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 3, L"Unload", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + } + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 4, L"Open &file location", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 5, L"&Copy", NULL, NULL), -1); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + PhDestroyEMenu(menu); + + if (selectedItem && selectedItem->Id != -1) + { + PPH_WMI_ENTRY entry; + + 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) && RtlDoesFileExists_U(entry->FileName->Buffer)) + { + PhShellExploreFile(hwndDlg, entry->FileName->Buffer); + } + } + 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); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->ListViewHandle, TRUE); + } + 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 5fe76c517a48..b53551cec92b 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -127,6 +127,7 @@ #define IDD_OPTIONS 227 #define IDD_PLUGINPROPERTIES 228 #define IDD_PLUGINSDISABLED 241 +#define IDD_PROCWMIPROVIDERS 242 #define IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 33fc77524159..dad506a77231 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -164,6 +164,8 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); 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 From 0ac2199644f5fcf30655d1f6b2ae0fc8ed3c2f0f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 08:21:43 +1100 Subject: [PATCH 582/839] Update process tooltips with Microsoft Edge container names and WMI provider host process information --- ProcessHacker/appsup.c | 63 +++++++++++---- ProcessHacker/include/appsup.h | 10 +++ ProcessHacker/itemtips.c | 136 +++++++++++++++++++++++++++++++-- 3 files changed, 188 insertions(+), 21 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 5cf37bc85463..dcf264aca322 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -302,15 +302,9 @@ NTSTATUS PhGetProcessKnownType( ) { 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, @@ -324,8 +318,6 @@ NTSTATUS PhGetProcessKnownType( return STATUS_SUCCESS; } - PhGetSystemRoot(&systemRootPrefix); - if (!NT_SUCCESS(status = PhGetProcessImageFileName( ProcessHandle, &fileName @@ -336,7 +328,40 @@ NTSTATUS PhGetProcessKnownType( newFileName = PhGetFileName(fileName); PhDereferenceObject(fileName); - name = newFileName->sr; + + *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) + return SystemProcessType; + + if (PhIsNullOrEmptyString(FileName)) + return UnknownProcessType; + + PhGetSystemRoot(&systemRootPrefix); + + fileName = PhDuplicateString(FileName); + name = fileName->sr; knownProcessType = UnknownProcessType; @@ -394,19 +419,31 @@ NTSTATUS PhGetProcessKnownType( knownProcessType = TaskHostProcessType; else if (PhEqualStringRef2(&name, L"\\wudfhost.exe", TRUE)) knownProcessType = UmdfHostProcessType; + else if (PhEqualStringRef2(&name, L"\\wbem\\WmiPrvSE.exe", TRUE)) + knownProcessType = WmiProviderHostType; + } + else + { + // Microsoft Edge + if (PhEndsWithStringRef2(&name, L"\\MicrosoftEdgeCP.exe", TRUE)) + 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(newFileName); + PhDereferenceObject(fileName); #ifdef _WIN64 if (isWow64) knownProcessType |= KnownProcessWow64; #endif - *KnownProcessType = knownProcessType; - - return status; + return knownProcessType; } static BOOLEAN NTAPI PhpSvchostCommandLineCallback( diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 172c4ab55626..fd79e97e2bd3 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -48,6 +48,8 @@ typedef enum _PH_KNOWN_PROCESS_TYPE TaskHostProcessType, // taskeng, taskhost, taskhostex ExplorerProcessType, // explorer UmdfHostProcessType, // wudfhost + EdgeProcessType, // Microsoft Edge + WmiProviderHostType, MaximumProcessType, KnownProcessTypeMask = 0xffff, @@ -62,6 +64,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 diff --git a/ProcessHacker/itemtips.c b/ProcessHacker/itemtips.c index b022e2dbb036..6c7e536de13e 100644 --- a/ProcessHacker/itemtips.c +++ b/ProcessHacker/itemtips.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -43,6 +44,22 @@ VOID PhpFillRunningTasks( _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( @@ -101,7 +118,6 @@ PPH_STRING PhGetProcessTooltipText( PH_STRING_BUILDER stringBuilder; ULONG validForMs = 60 * 60 * 1000; // 1 hour PPH_STRING tempString; - PH_KNOWN_PROCESS_TYPE knownProcessType = UnknownProcessType; PhInitializeStringBuilder(&stringBuilder, 200); @@ -140,20 +156,17 @@ PPH_STRING PhGetProcessTooltipText( // 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( + if (Process->KnownProcessType != UnknownProcessType && PhaGetProcessKnownCommandLine( Process->CommandLine, - knownProcessType, + Process->KnownProcessType, &knownCommandLine )) { - switch (knownProcessType & KnownProcessTypeMask) + switch (Process->KnownProcessType & KnownProcessTypeMask) { case ServiceHostProcessType: PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n "); @@ -290,7 +303,7 @@ PPH_STRING PhGetProcessTooltipText( } // Tasks, Drivers - switch (knownProcessType & KnownProcessTypeMask) + switch (Process->KnownProcessType & KnownProcessTypeMask) { case TaskHostProcessType: { @@ -328,6 +341,42 @@ PPH_STRING PhGetProcessTooltipText( 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 @@ -636,6 +685,77 @@ VOID PhpFillRunningTasks( } } +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 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, &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 ) From 574e8571eab866ccc121cb4460ac27fd4e5d9c2f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 16 Nov 2017 22:34:47 +1100 Subject: [PATCH 583/839] Make JobObjectId column formatting the same as Task Manager --- ProcessHacker/proctree.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 0d4e674de3ca..a57c7792c777 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -3,6 +3,7 @@ * process tree list * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2016-2017 dmex * * This file is part of Process Hacker. * @@ -2656,7 +2657,11 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_JOBOBJECTID: { - PhpFormatInt32GroupDigits(processItem->JobObjectId, node->JobObjectIdText, sizeof(node->JobObjectIdText), &getCellText->Text); + if (processItem->JobObjectId != 0) + { + PhPrintInt32(node->JobObjectIdText, processItem->JobObjectId); + PhInitializeStringRefLongHint(&getCellText->Text, node->JobObjectIdText); + } } break; default: From 76447c5f2c798d42f64a499157aa8c364eb9c15b Mon Sep 17 00:00:00 2001 From: Matthijs Lavrijsen Date: Thu, 16 Nov 2017 13:11:22 +0100 Subject: [PATCH 584/839] Fix some TEB field offsets on 32 bit (#189) --- phnt/include/ntpebteb.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 37d4fbb43a55..7114a1bb7bb9 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -202,7 +202,11 @@ typedef struct _TEB LCID CurrentLocale; ULONG FpSoftwareStatusRegister; PVOID ReservedForDebuggerInstrumentation[16]; +#ifdef _WIN64 PVOID SystemReserved1[30]; +#else + PVOID SystemReserved1[26]; +#endif CHAR PlaceholderCompatibilityMode; CHAR PlaceholderReserved[11]; @@ -216,9 +220,15 @@ typedef struct _TEB 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; From 6674e5254773bfa1b342bbfac732be5170ad4048 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 17 Nov 2017 01:06:24 +1100 Subject: [PATCH 585/839] Improve service description query performance, Fix service state highlighting, Fix showing username for driver services --- ProcessHacker/srvlist.c | 69 ++++++++++++++++++++++++++++++++++++++--- ProcessHacker/srvprp.c | 15 +++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index e04ff39f66f8..c1ee3fd12dfe 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -3,6 +3,7 @@ * service list * * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -371,17 +372,75 @@ static VOID PhpUpdateServiceNodeDescription( { if (!(ServiceNode->ValidMask & PHSN_DESCRIPTION)) { - SC_HANDLE serviceHandle; + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + HANDLE keyHandle; + PPH_STRING keyName; - if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + keyName = PhConcatStringRef2(&servicesKeyName, &ServiceNode->ServiceItem->Name->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_QUERY_VALUE, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) { - PhMoveReference(&ServiceNode->Description, PhGetServiceDescription(serviceHandle)); + LSTATUS result; + PWSTR buffer; + ULONG bufferSize; + ULONG returnLength = 0; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + if ((result = RegLoadMUIString( + keyHandle, + L"Description", + buffer, + bufferSize, + &returnLength, + 0, + NULL + )) == ERROR_MORE_DATA) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + + result = RegLoadMUIString( + keyHandle, + L"Description", + buffer, + bufferSize, + &returnLength, + 0, + NULL + ); + } - CloseServiceHandle(serviceHandle); + if (result == ERROR_SUCCESS) + { + PhMoveReference(&ServiceNode->Description, PhCreateStringEx(buffer, returnLength)); + } + + PhFree(buffer); + NtClose(keyHandle); } + PhDereferenceObject(keyName); + ServiceNode->ValidMask |= PHSN_DESCRIPTION; } + + // NOTE: Querying the service description via RPC is extremely slow. + //SC_HANDLE serviceHandle; + // + //if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + //{ + // PhMoveReference(&ServiceNode->Description, PhGetServiceDescription(serviceHandle)); + // CloseServiceHandle(serviceHandle); + //} } static VOID PhpUpdateServiceNodeKey( @@ -758,7 +817,7 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( getNodeColor->Flags = TN_AUTO_FORECOLOR; getNodeColor->BackColor = PhCsColorUnknown; } - else if (PhCsUseColorServiceStop && serviceItem->State == SERVICE_STOPPED) + else if (PhCsUseColorServiceStop && serviceItem->StartType == SERVICE_DISABLED) { getNodeColor->ForeColor = PhCsColorServiceStop; } diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index 0aab0620ecae..c3edcfdb001f 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -184,6 +184,21 @@ static VOID PhpRefreshControls( { EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), FALSE); } + + if (PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_TYPE), L"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( From 2e645e3b1814a8e54664ce400cebfd4720bf1cf2 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 17 Nov 2017 01:08:04 +1100 Subject: [PATCH 586/839] peview: Query name for 'unnamed' DLL exports from symbols --- tools/peview/expprp.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tools/peview/expprp.c b/tools/peview/expprp.c index 9e50ec4e491e..1b521fb12754 100644 --- a/tools/peview/expprp.c +++ b/tools/peview/expprp.c @@ -110,7 +110,44 @@ INT_PTR CALLBACK PvpPeExportsDlgProc( } else { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + if (exportFunction.Function) + { + PPH_STRING exportName; + + // Try find the export name using symbols. + exportName = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders->OptionalHeader.ImageBase, exportFunction.Function), + NULL, + NULL, + NULL, + NULL + ); + + if (exportName) + { + static PH_STRINGREF unnamedText = PH_STRINGREF_INIT(L" (unnamed)"); + PH_STRINGREF exportNameText; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + + if (PhSplitStringRefAtLastChar(&exportName->sr, L'!', &firstPart, &secondPart)) + exportNameText = secondPart; + else + exportNameText = exportName->sr; + + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PH_AUTO_T(PH_STRING, PhConcatStringRef2(&exportNameText, &unnamedText))->Buffer); + PhDereferenceObject(exportName); + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } } PhPrintUInt32(number, exportEntry.Ordinal); From 02973e2d6212abec1f22bed8ac1994e848f2f795 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 17 Nov 2017 11:01:58 +1100 Subject: [PATCH 587/839] Tidy up code --- ProcessHacker/procprv.c | 4 +- ProcessHacker/prpgenv.c | 14 +++--- ProcessHacker/searchbox.c | 8 +--- ProcessHacker/tokprp.c | 5 +- phlib/graph.c | 5 +- phlib/hexedit.c | 11 +++-- phlib/lsasup.c | 2 +- phlib/native.c | 2 +- plugins/ToolStatus/ToolStatus.vcxproj | 1 - plugins/ToolStatus/ToolStatus.vcxproj.filters | 3 -- plugins/ToolStatus/searchbox.c | 48 ------------------- plugins/ToolStatus/toolbar.c | 21 ++++++-- tools/peview/searchbox.c | 8 +--- 13 files changed, 42 insertions(+), 90 deletions(-) delete mode 100644 plugins/ToolStatus/searchbox.c diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index c7033fd3c584..55d048fdd1ab 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -2903,8 +2903,8 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForParent( if (WindowsVersion >= WINDOWS_10_RS3) { - // 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. + // 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 diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index cb16b9606a21..d81c73a68264 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -27,6 +27,7 @@ #include #include #include + #include #include @@ -165,8 +166,7 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( PPH_ENVIRONMENT_CONTEXT environmentContext; HWND lvHandle; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { environmentContext = propPageContext->Context; @@ -184,8 +184,8 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( { environmentContext = propPageContext->Context = PhAllocate(sizeof(PH_ENVIRONMENT_CONTEXT)); memset(environmentContext, 0, sizeof(PH_ENVIRONMENT_CONTEXT)); - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - environmentContext->ListViewHandle = lvHandle; + + environmentContext->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); PhInitializeArray(&environmentContext->Items, sizeof(PH_ENVIRONMENT_ITEM), 100); PhSetListViewStyle(lvHandle, TRUE, TRUE); @@ -206,7 +206,7 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( break; case WM_DESTROY: { - PhSaveListViewColumnsToSetting(L"EnvironmentListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + PhSaveListViewColumnsToSetting(L"EnvironmentListViewColumns", lvHandle); PhpClearEnvironmentItems(environmentContext); PhDeleteArray(&environmentContext->Items); @@ -382,8 +382,8 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( PVOID *indices; ULONG numberOfIndices; - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); if (point.x == -1 && point.y == -1) PhGetListViewContextMenuPoint((HWND)wParam, &point); diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 6fd37ed6c719..b6ec86880b23 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -254,9 +254,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( _In_ ULONG_PTR dwRefData ) { - PEDIT_CONTEXT context; - - context = (PEDIT_CONTEXT)GetProp(hWnd, L"SearchBoxContext"); + PEDIT_CONTEXT context = (PEDIT_CONTEXT)dwRefData; switch (uMsg) { @@ -268,7 +266,6 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( DeleteObject(context->WindowFont); RemoveWindowSubclass(hWnd, PhpSearchWndSubclassProc, uIdSubclass); - RemoveProp(hWnd, L"SearchBoxContext"); PhFree(context); } break; @@ -531,9 +528,6 @@ VOID PhCreateSearchControl( if (BannerText) Edit_SetCueBannerText(context->WindowHandle, BannerText); - // Set our window context data. - SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); - // Subclass the Edit control window procedure. SetWindowSubclass(context->WindowHandle, PhpSearchWndSubclassProc, 0, (ULONG_PTR)context); diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index ec8401daba6a..3e6596d6b7f4 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -29,6 +29,7 @@ #include #include +#include #include typedef struct _ATTRIBUTE_NODE @@ -947,8 +948,8 @@ INT_PTR CALLBACK PhpTokenPageProc( { POINT point; - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); if (point.x == -1 && point.y == -1) PhGetListViewContextMenuPoint((HWND)wParam, &point); diff --git a/phlib/graph.c b/phlib/graph.c index b74eeab067c1..6bcc69ab67aa 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -22,6 +22,7 @@ #include +#include #include #include @@ -1129,8 +1130,8 @@ LRESULT CALLBACK PhpGraphWndProc( mouseEvent.Header.code = GCN_MOUSEEVENT; mouseEvent.Message = uMsg; mouseEvent.Keys = (ULONG)wParam; - mouseEvent.Point.x = LOWORD(lParam); - mouseEvent.Point.y = HIWORD(lParam); + 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; diff --git a/phlib/hexedit.c b/phlib/hexedit.c index 909e020d8c55..5fce14a1c383 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -23,11 +23,12 @@ #include #include - #include #include +#include + // Code originally from http://www.codeguru.com/Cpp/controls/editctrl/article.php/c539 BOOLEAN PhHexEditInitialization( @@ -300,8 +301,8 @@ LRESULT CALLBACK PhpHexEditWndProc( ULONG flags = (ULONG)wParam; POINT cursorPos; - cursorPos.x = (LONG)(SHORT)LOWORD(lParam); - cursorPos.y = (LONG)(SHORT)HIWORD(lParam); + cursorPos.x = GET_X_LPARAM(lParam); + cursorPos.y = GET_Y_LPARAM(lParam); SetFocus(hwnd); @@ -379,8 +380,8 @@ LRESULT CALLBACK PhpHexEditWndProc( ULONG flags = (ULONG)wParam; POINT cursorPos; - cursorPos.x = (LONG)(SHORT)LOWORD(lParam); - cursorPos.y = (LONG)(SHORT)HIWORD(lParam); + cursorPos.x = GET_X_LPARAM(lParam); + cursorPos.y = GET_Y_LPARAM(lParam); if ( context->Data && diff --git a/phlib/lsasup.c b/phlib/lsasup.c index b4ea741e4cea..d526ca2ffc67 100644 --- a/phlib/lsasup.c +++ b/phlib/lsasup.c @@ -461,7 +461,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( diff --git a/phlib/native.c b/phlib/native.c index a1dbfb769808..1ffe2d2a675b 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5705,7 +5705,7 @@ VOID PhpInitializePredefineKeys( HANDLE tokenHandle; PTOKEN_USER tokenUser; UNICODE_STRING stringSid; - WCHAR stringSidBuffer[MAX_UNICODE_STACK_BUFFER_LENGTH]; + WCHAR stringSidBuffer[SECURITY_MAX_SID_STRING_CHARACTERS]; PUNICODE_STRING currentUserKeyName; // Get the string SID of the current user. diff --git a/plugins/ToolStatus/ToolStatus.vcxproj b/plugins/ToolStatus/ToolStatus.vcxproj index 2931e6480717..17a8c126440c 100644 --- a/plugins/ToolStatus/ToolStatus.vcxproj +++ b/plugins/ToolStatus/ToolStatus.vcxproj @@ -78,7 +78,6 @@ - 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/searchbox.c b/plugins/ToolStatus/searchbox.c deleted file mode 100644 index 1c62529daddf..000000000000 --- a/plugins/ToolStatus/searchbox.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 - )) - { - PhCreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); - return TRUE; - } - - return FALSE; -} \ No newline at end of file diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index 838032616bbc..10c86c351482 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -161,11 +161,24 @@ VOID RebarLoadSettings( 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); + ProcessTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportProcessTreeList(), ProcessTreeFilterCallback, NULL); + ServiceTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), ServiceTreeFilterCallback, NULL); + NetworkTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportNetworkTreeList(), NetworkTreeFilterCallback, NULL); - CreateSearchboxControl(); + 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) diff --git a/tools/peview/searchbox.c b/tools/peview/searchbox.c index 1d51bf7c061b..c3e78f5ba471 100644 --- a/tools/peview/searchbox.c +++ b/tools/peview/searchbox.c @@ -429,9 +429,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( _In_ ULONG_PTR dwRefData ) { - PEDIT_CONTEXT context; - - context = (PEDIT_CONTEXT)GetProp(hWnd, L"SearchBoxContext"); + PEDIT_CONTEXT context = (PEDIT_CONTEXT)dwRefData; switch (uMsg) { @@ -443,7 +441,6 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( DeleteObject(context->WindowFont); RemoveWindowSubclass(hWnd, PhpSearchWndSubclassProc, uIdSubclass); - RemoveProp(hWnd, L"SearchBoxContext"); PhFree(context); } break; @@ -705,9 +702,6 @@ VOID PvCreateSearchControl( if (BannerText) Edit_SetCueBannerText(context->WindowHandle, BannerText); - // Set our window context data. - SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); - // Subclass the Edit control window procedure. SetWindowSubclass(context->WindowHandle, PhpSearchWndSubclassProc, 0, (ULONG_PTR)context); From d9f733bf6ff50380d09c6399d2e57f9448e1256a Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 18 Nov 2017 13:51:55 +1100 Subject: [PATCH 588/839] Fix WinVerifyTrust flags, Reuse existing file handle for catalog verification --- phlib/verify.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/phlib/verify.c b/phlib/verify.c index 1118bc495bf8..72e0213787cc 100644 --- a/phlib/verify.c +++ b/phlib/verify.c @@ -189,7 +189,6 @@ VOID PhpViewSignerInfo( VERIFY_RESULT PhpVerifyFile( _In_ PPH_VERIFY_FILE_INFO Information, - _In_ HANDLE FileHandle, _In_ ULONG UnionChoice, _In_ PVOID UnionData, _In_ PGUID ActionId, @@ -220,7 +219,7 @@ VERIFY_RESULT PhpVerifyFile( trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; } - status = WinVerifyTrust_I(NULL, ActionId, &trustData); + status = WinVerifyTrust_I(INVALID_HANDLE_VALUE, ActionId, &trustData); PhpGetSignaturesFromStateData(trustData.hWVTStateData, Signatures, NumberOfSignatures); if (status == 0 && (Information->Flags & PH_VERIFY_VIEW_PROPERTIES)) @@ -228,7 +227,7 @@ VERIFY_RESULT PhpVerifyFile( // Close the state data. trustData.dwStateAction = WTD_STATEACTION_CLOSE; - WinVerifyTrust_I(NULL, ActionId, &trustData); + WinVerifyTrust_I(INVALID_HANDLE_VALUE, ActionId, &trustData); return PhpStatusToVerifyResult(status); } @@ -365,11 +364,12 @@ VERIFY_RESULT PhpVerifyFileFromCatalog( 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, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &DriverActionVerify, &verInfo, &signatures, &numberOfSignatures); + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_CATALOG, &catalogInfo, &DriverActionVerify, &verInfo, &signatures, &numberOfSignatures); if (verInfo.pcSignerCertContext) CertFreeCertificateContext_I(verInfo.pcSignerCertContext); @@ -388,11 +388,12 @@ VERIFY_RESULT PhpVerifyFileFromCatalog( 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, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_CATALOG, &catalogInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); if (verifyResult == VrTrusted) break; @@ -451,7 +452,7 @@ NTSTATUS PhVerifyFileEx( &fileHandle, Information->FileName, FILE_GENERIC_READ, - 0, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT @@ -462,7 +463,7 @@ NTSTATUS PhVerifyFileEx( fileInfo.pcwszFilePath = Information->FileName; fileInfo.hFile = fileHandle; - verifyResult = PhpVerifyFile(Information, fileHandle, WTD_CHOICE_FILE, &fileInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_FILE, &fileInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); if (verifyResult == VrNoSignature) { From f3442d7b54a34dd0dbb424b0278a487b4e2e91e7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 18 Nov 2017 14:11:00 +1100 Subject: [PATCH 589/839] Remove extra handle from process provider --- ProcessHacker/procprv.c | 65 +++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 55d048fdd1ab..5307bdfdd948 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1154,8 +1154,14 @@ VOID PhpProcessQueryStage1( PhGetProcessConsoleHostProcessId(processHandleLimited, &Data->ConsoleHostProcessId); } + // Immersive + if (processHandleLimited && IsImmersiveProcess_I) + { + processItem->IsImmersive = !!IsImmersiveProcess_I(processHandleLimited); + } + // Package full name - if (processHandleLimited && WINDOWS_HAS_IMMERSIVE) + if (processHandleLimited && WINDOWS_HAS_IMMERSIVE && processItem->IsImmersive) { Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } @@ -1337,9 +1343,6 @@ VOID PhpFillProcessItem( _In_ PSYSTEM_PROCESS_INFORMATION Process ) { - NTSTATUS status; - HANDLE processHandle = NULL; - ProcessItem->ParentProcessId = Process->InheritedFromUniqueProcessId; ProcessItem->SessionId = Process->SessionId; ProcessItem->CreateTime = Process->CreateTime; @@ -1352,7 +1355,13 @@ VOID PhpFillProcessItem( PhPrintUInt32(ProcessItem->ParentProcessIdString, HandleToUlong(ProcessItem->ParentProcessId)); PhPrintUInt32(ProcessItem->SessionIdString, ProcessItem->SessionId); - PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessItem->ProcessId); + // Open a handle to the process for later usage. + { + PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId); + + if (!ProcessItem->QueryHandle) + PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessItem->ProcessId); + } // Process information { @@ -1363,9 +1372,9 @@ VOID PhpFillProcessItem( { PPH_STRING fileName; - if (processHandle) + if (ProcessItem->QueryHandle) { - PhGetProcessImageFileNameWin32(processHandle, &ProcessItem->FileName); + PhGetProcessImageFileNameWin32(ProcessItem->QueryHandle, &ProcessItem->FileName); } else { @@ -1392,27 +1401,20 @@ VOID PhpFillProcessItem( // Token-related information if ( - processHandle && + ProcessItem->QueryHandle && 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)) + if (NT_SUCCESS(PhOpenProcessToken(ProcessItem->QueryHandle, TOKEN_QUERY, &tokenHandle))) { - // User name - { - PTOKEN_USER user; + PTOKEN_USER user; - status = PhGetTokenUser(tokenHandle, &user); - - if (NT_SUCCESS(status)) - { - ProcessItem->UserName = PhpGetSidFullNameCached(user->User.Sid); - PhFree(user); - } + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user))) + { + ProcessItem->UserName = PhpGetSidFullNameCached(user->User.Sid); + PhFree(user); } NtClose(tokenHandle); @@ -1437,7 +1439,13 @@ VOID PhpFillProcessItem( ); } - NtClose(processHandle); + // On Windows 8.1 and above, processes without threads are reflected processes + // which will not terminate if we have a handle open. + if (Process->NumberOfThreads == 0) + { + NtClose(ProcessItem->QueryHandle); + ProcessItem->QueryHandle = NULL; + } } FORCEINLINE VOID PhpUpdateDynamicInfoProcessItem( @@ -2193,19 +2201,6 @@ VOID PhProcessProviderUpdate( 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 (!processItem->QueryHandle) - PhOpenProcess(&processItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, processItem->ProcessId); - } - PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); PhpUpdateDynamicInfoProcessItem(processItem, process); From 06189bbebce366c870332b192848f0da7ea4fe70 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 22 Nov 2017 13:43:12 +1100 Subject: [PATCH 590/839] Update ntrtl.h types --- phnt/include/ntrtl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 0cfd67a428d1..4d505b1f7602 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -4562,6 +4562,12 @@ typedef struct _RTL_BITMAP PULONG Buffer; } RTL_BITMAP, *PRTL_BITMAP; +typedef struct _RTL_BITMAP_EX +{ + ULONG64 SizeOfBitMap; + PULONG64 Buffer; +} RTL_BITMAP_EX, *PRTL_BITMAP_EX; + NTSYSAPI VOID NTAPI From bc7a9a29c903822cc7c7c95c7be045a124372a2d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 23 Nov 2017 07:24:36 +1100 Subject: [PATCH 591/839] peview: Fix symbol path --- tools/peview/peprp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index bce767f5c460..2e91afed8563 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -726,11 +726,11 @@ BOOLEAN PvpLoadDbgHelp( // 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); + symbolSearchPath = PhCreateStringFromUnicodeString(&symbolPathUs); } else { - symbolSearchPath = PhCreateString(L"SRV**http://msdl.microsoft.com/download/symbols"); + symbolSearchPath = PhCreateString(L"SRV*C:\\Symbols*http://msdl.microsoft.com/download/symbols"); } PhSetSearchPathSymbolProvider(symbolProvider, symbolSearchPath->Buffer); From 5818ba54e074d1d51ed1ac1aec557b43ca0a1712 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 23 Nov 2017 07:33:40 +1100 Subject: [PATCH 592/839] Fix #193 --- ProcessHacker/procprv.c | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 5307bdfdd948..7adacbcb2237 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -201,8 +201,8 @@ ULONG PhTotalProcesses; ULONG PhTotalThreads; ULONG PhTotalHandles; -SYSTEM_PROCESS_INFORMATION PhDpcsProcessInformation; -SYSTEM_PROCESS_INFORMATION PhInterruptsProcessInformation; +PSYSTEM_PROCESS_INFORMATION PhDpcsProcessInformation; +PSYSTEM_PROCESS_INFORMATION PhInterruptsProcessInformation; ULONG64 PhCpuTotalCycleDelta; // real cycle time delta for this period PLARGE_INTEGER PhCpuIdleCycleTime; // cycle time for Idle @@ -279,19 +279,19 @@ BOOLEAN PhProcessProviderInitialization( PhProcessRecordList = PhCreateList(40); - RtlInitUnicodeString( - &PhDpcsProcessInformation.ImageName, - L"DPCs" - ); - PhDpcsProcessInformation.UniqueProcessId = DPCS_PROCESS_ID; - PhDpcsProcessInformation.InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; + PhDpcsProcessInformation = PhAllocate(sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_PROCESS_INFORMATION_EXTENSION)); + memset(PhDpcsProcessInformation, 0, 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; + PhInterruptsProcessInformation = PhAllocate(sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_PROCESS_INFORMATION_EXTENSION)); + memset(PhInterruptsProcessInformation, 0, 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; + + RtlInitUnicodeString(&PhInterruptsProcessInformation->ImageName, L"Interrupts"); + PhInterruptsProcessInformation->UniqueProcessId = INTERRUPTS_PROCESS_ID; + PhInterruptsProcessInformation->InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; PhCpuInformation = PhAllocate( sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * @@ -2043,14 +2043,14 @@ VOID PhProcessProviderUpdate( if (PhEnableCycleCpuUsage) { - PhInterruptsProcessInformation.KernelTime.QuadPart = PhCpuTotals.DpcTime.QuadPart + PhCpuTotals.InterruptTime.QuadPart; - PhInterruptsProcessInformation.CycleTime = PhCpuSystemCycleDelta.Value; + 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; + PhDpcsProcessInformation->KernelTime = PhCpuTotals.DpcTime; + PhInterruptsProcessInformation->KernelTime = PhCpuTotals.InterruptTime; } // Look for dead processes. @@ -2074,11 +2074,11 @@ VOID PhProcessProviderUpdate( if (processItem->ProcessId == DPCS_PROCESS_ID) { - processEntry = &PhDpcsProcessInformation; + processEntry = PhDpcsProcessInformation; } else if (processItem->ProcessId == INTERRUPTS_PROCESS_ID) { - processEntry = &PhInterruptsProcessInformation; + processEntry = PhInterruptsProcessInformation; } else { @@ -2420,13 +2420,13 @@ VOID PhProcessProviderUpdate( // Trick ourselves into thinking that the fake processes // are on the list. - if (process == &PhInterruptsProcessInformation) + if (process == PhInterruptsProcessInformation) { process = NULL; } - else if (process == &PhDpcsProcessInformation) + else if (process == PhDpcsProcessInformation) { - process = &PhInterruptsProcessInformation; + process = PhInterruptsProcessInformation; } else { @@ -2435,9 +2435,9 @@ VOID PhProcessProviderUpdate( if (process == NULL) { if (PhEnableCycleCpuUsage) - process = &PhInterruptsProcessInformation; + process = PhInterruptsProcessInformation; else - process = &PhDpcsProcessInformation; + process = PhDpcsProcessInformation; } } } From 70676131234825ab2efcab48646722efd40b14d1 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 25 Nov 2017 02:53:07 +1100 Subject: [PATCH 593/839] Update macro, Fix dbghelp path --- ProcessHacker/dbgcon.c | 3 +- tools/peview/peprp.c | 64 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index d9a5c25e57d7..bcd6cab3ae10 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -677,8 +677,7 @@ NTSTATUS PhpDebugConsoleThreadStart( UNICODE_STRING var; PPH_STRING newSearchPath; - var.Buffer = buffer; - var.MaximumLength = sizeof(buffer); + RtlInitEmptyUnicodeString(&var, buffer, sizeof(buffer)); if (!NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &name, &var))) buffer[0] = 0; diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 2e91afed8563..22ec46ba10f2 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -666,7 +666,7 @@ VOID PvpLoadDbgHelpFromPath( { HMODULE dbghelpModule; - if (dbghelpModule = LoadLibrary(DbgHelpPath)) + if (DbgHelpPath && (dbghelpModule = LoadLibrary(DbgHelpPath))) { PPH_STRING fullDbghelpPath; ULONG indexOfFileName; @@ -702,25 +702,67 @@ VOID PvpLoadDbgHelpFromPath( PhSymbolProviderCompleteInitialization(dbghelpModule); } +PPH_STRING PhFindDbghelpPath( + 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; +} + + BOOLEAN PvpLoadDbgHelp( _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider ) { static UNICODE_STRING symbolPathVarName = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); PPH_STRING symbolSearchPath; + PPH_STRING dbgHelpPath; PPH_SYMBOL_PROVIDER symbolProvider; - WCHAR buffer[512] = L""; - UNICODE_STRING symbolPathUs = - { - .Buffer = buffer, - .Length = sizeof(buffer) - sizeof(UNICODE_NULL), - .MaximumLength = sizeof(buffer) - }; + UNICODE_STRING symbolPathUs; + WCHAR buffer[512]; + RtlInitEmptyUnicodeString(&symbolPathUs, buffer, sizeof(buffer)); + if (!PhSymbolProviderInitialization()) return FALSE; - PvpLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); + dbgHelpPath = PhFindDbghelpPath(); + PvpLoadDbgHelpFromPath(PhGetString(dbgHelpPath)); symbolProvider = PhCreateSymbolProvider(NULL); // Load symbol path from _NT_SYMBOL_PATH if configured by the user. @@ -730,12 +772,16 @@ BOOLEAN PvpLoadDbgHelp( } else { + // Set the default path (C:\\Symbols is the default hard-coded path for livekd). symbolSearchPath = PhCreateString(L"SRV*C:\\Symbols*http://msdl.microsoft.com/download/symbols"); } PhSetSearchPathSymbolProvider(symbolProvider, symbolSearchPath->Buffer); PhDereferenceObject(symbolSearchPath); + if (dbgHelpPath) + PhDereferenceObject(dbgHelpPath); + *SymbolProvider = symbolProvider; return TRUE; } \ No newline at end of file From f5d19dbaba56f66f303daa89d90edf07662352eb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 25 Nov 2017 02:53:48 +1100 Subject: [PATCH 594/839] Fix typo --- ProcessHacker/procprv.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 7adacbcb2237..e65e512f4b8b 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -111,7 +111,10 @@ typedef struct _PH_PROCESS_QUERY_S1_DATA ULONG IsSecureProcess : 1; ULONG IsSubsystemProcess : 1; - ULONG Spare : 23; + ULONG IsBeingDebugged : 1; + ULONG IsImmersive : 1; + + ULONG Spare : 21; }; }; } PH_PROCESS_QUERY_S1_DATA, *PPH_PROCESS_QUERY_S1_DATA; @@ -1006,6 +1009,17 @@ VOID PhpProcessQueryStage1( } } + // Debugged + if (processHandleLimited) + { + BOOLEAN isBeingDebugged; + + if (NT_SUCCESS(PhGetProcessIsBeingDebugged(processHandleLimited, &isBeingDebugged))) + { + Data->IsBeingDebugged = isBeingDebugged; + } + } + // Command line, .NET if (processHandleLimited) { @@ -1157,11 +1171,11 @@ VOID PhpProcessQueryStage1( // Immersive if (processHandleLimited && IsImmersiveProcess_I) { - processItem->IsImmersive = !!IsImmersiveProcess_I(processHandleLimited); + Data->IsImmersive = !!IsImmersiveProcess_I(processHandleLimited); } // Package full name - if (processHandleLimited && WINDOWS_HAS_IMMERSIVE && processItem->IsImmersive) + if (processHandleLimited && WINDOWS_HAS_IMMERSIVE && Data->IsImmersive) { Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } @@ -1305,6 +1319,8 @@ VOID PhpFillProcessItemStage1( processItem->IsProtectedProcess = Data->IsProtectedProcess; processItem->IsSecureProcess = Data->IsSecureProcess; processItem->IsSubsystemProcess = Data->IsSubsystemProcess; + processItem->IsBeingDebugged = Data->IsBeingDebugged; + processItem->IsImmersive = Data->IsImmersive; PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); } From 47a7a891d3cfce5796ba8962e80e0dba407618af Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 27 Nov 2017 07:42:58 +1100 Subject: [PATCH 595/839] NetworkTools: Add packet loss, latency, bytes in/out columns to the network tab --- plugins/NetworkTools/NetworkTools.rc | 7 +- plugins/NetworkTools/main.c | 321 ++++++++++++++++++++++++++- plugins/NetworkTools/nettools.h | 28 ++- plugins/NetworkTools/options.c | 2 + plugins/NetworkTools/resource.h | 3 +- 5 files changed, 347 insertions(+), 14 deletions(-) diff --git a/plugins/NetworkTools/NetworkTools.rc b/plugins/NetworkTools/NetworkTools.rc index ba5d9ab2068c..434e4122d907 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -96,7 +96,7 @@ BEGIN CONTROL "",IDC_NETOUTPUTEDIT,"RICHEDIT50W",WS_VSCROLL | WS_TABSTOP | 0x4,2,2,387,212 END -IDD_OPTIONS DIALOGEX 0, 0, 201, 43 +IDD_OPTIONS DIALOGEX 0, 0, 201, 69 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -105,6 +105,9 @@ BEGIN EDITTEXT IDC_PINGPACKETLENGTH,7,17,89,14,ES_AUTOHSCROLL | ES_NUMBER EDITTEXT IDC_MAXHOPS,105,17,89,14,ES_AUTOHSCROLL | ES_NUMBER LTEXT "Max Tracert Hops:",IDC_STATIC,105,7,60,8 + CONTROL "Enable extended TCP statistics",IDC_ENABLE_EXTENDED_TCP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,115,10 + LTEXT "Note: Changes may require an application restart.",IDC_STATIC,7,54,162,8 END IDD_PING DIALOGEX 0, 0, 329, 151 @@ -656,7 +659,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 194 TOPMARGIN, 7 - BOTTOMMARGIN, 36 + BOTTOMMARGIN, 62 END IDD_PING, DIALOG diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index b034b73ca90f..1ff9eaa5d195 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -32,13 +32,25 @@ 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(); } @@ -329,6 +341,18 @@ LONG NTAPI NetworkServiceSortFunction( { case NETWORK_COLUMN_ID_REMOTE_COUNTRY: return PhCompareStringWithNull(extension1->RemoteCountryCode, extension2->RemoteCountryCode, TRUE); + case NETWORK_COLUMN_ID_LOCAL_SERVICE: + return PhCompareStringWithNull(extension1->LocalServiceName, extension2->LocalServiceName, TRUE); + case NETWORK_COLUMN_ID_REMOTE_SERVICE: + return PhCompareStringWithNull(extension1->RemoteServiceName, extension2->RemoteServiceName, 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; @@ -362,18 +386,51 @@ VOID NTAPI NetworkTreeNewInitializingCallback( 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; + PPH_NETWORK_ITEM networkItem = Object; PNETWORK_EXTENSION extension = Extension; memset(extension, 0, sizeof(NETWORK_EXTENSION)); + + extension->NetworkItem = networkItem; + + if (NetworkExtensionEnabled) + { + PhAcquireQueuedLockExclusive(&NetworkExtensionListLock); + InsertTailList(&NetworkExtensionListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&NetworkExtensionListLock); + } } VOID NTAPI NetworkItemDeleteCallback( @@ -385,13 +442,60 @@ VOID NTAPI NetworkItemDeleteCallback( //PPH_NETWORK_ITEM networkItem = Object; PNETWORK_EXTENSION extension = Extension; - PhClearReference(&extension->RemoteCountryCode); - PhClearReference(&extension->RemoteCountryName); + 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); if (extension->CountryIcon) DestroyIcon(extension->CountryIcon); } +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, @@ -418,6 +522,43 @@ VOID NTAPI NetworkNodeCreateCallback( 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( @@ -464,6 +605,30 @@ VOID UpdateNetworkNode( } } break; + case NETWORK_COLUMN_ID_BYTES_IN: + { + if (Extension->NumberOfBytesIn) + PhMoveReference(&Extension->BytesIn, PhFormatSize(Extension->NumberOfBytesIn, -1)); + } + break; + case NETWORK_COLUMN_ID_BYTES_OUT: + { + if (Extension->NumberOfBytesOut) + PhMoveReference(&Extension->BytesOut, PhFormatSize(Extension->NumberOfBytesOut, -1)); + } + break; + case NETWORK_COLUMN_ID_PACKETLOSS: + { + if (Extension->NumberOfLostPackets) + PhMoveReference(&Extension->PacketLossText, PhFormatUInt64(Extension->NumberOfLostPackets, TRUE)); + } + break; + case NETWORK_COLUMN_ID_LATENCY: + { + if (Extension->SampleRtt) + PhMoveReference(&Extension->LatencyText, PhFormatUInt64(Extension->SampleRtt, TRUE)); + } + break; } } @@ -497,6 +662,18 @@ VOID NTAPI TreeNewMessageCallback( 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; } } } @@ -563,24 +740,146 @@ VOID NTAPI TreeNewMessageCallback( extension->RemoteCountryName->Buffer, (INT)extension->RemoteCountryName->Length / 2, &rect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE + DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE ); } if (GeoDbExpired && !extension->CountryIcon) { - 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 not found.", -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; } } +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 = 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 = pathRod.FastRetran + pathRod.PktsRetrans; + extension->SampleRtt = pathRod.SampleRtt; + } + } + } +} + LOGICAL DllMain( _In_ HINSTANCE Instance, _In_ ULONG Reason, @@ -607,6 +906,7 @@ LOGICAL DllMain( { 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); @@ -663,6 +963,13 @@ LOGICAL DllMain( &TreeNewMessageCallbackRegistration ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + PhPluginSetObjectExtension( PluginInstance, EmNetworkItemType, diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index ff6aaa2f3427..eda0819c7553 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -59,6 +59,7 @@ #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; @@ -212,6 +213,9 @@ INT_PTR CALLBACK OptionsDlgProc( // country.c typedef struct _NETWORK_EXTENSION { + LIST_ENTRY ListEntry; + PPH_NETWORK_ITEM NetworkItem; + union { BOOLEAN Flags; @@ -220,7 +224,8 @@ typedef struct _NETWORK_EXTENSION BOOLEAN CountryValid : 1; BOOLEAN LocalValid : 1; BOOLEAN RemoteValid : 1; - BOOLEAN Spare : 5; + BOOLEAN StatsEnabled : 1; + BOOLEAN Spare : 4; }; }; @@ -229,13 +234,28 @@ typedef struct _NETWORK_EXTENSION 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_REMOTE_COUNTRY = 1, - NETWORK_COLUMN_ID_LOCAL_SERVICE = 2, - NETWORK_COLUMN_ID_REMOTE_SERVICE = 3, + 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 diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index accb0a4ece4d..abe24349106e 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -36,12 +36,14 @@ INT_PTR CALLBACK OptionsDlgProc( { SetDlgItemInt(hwndDlg, IDC_PINGPACKETLENGTH, PhGetIntegerSetting(SETTING_NAME_PING_SIZE), FALSE); SetDlgItemInt(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); } break; case WM_DESTROY: { PhSetIntegerSetting(SETTING_NAME_PING_SIZE, GetDlgItemInt(hwndDlg, IDC_PINGPACKETLENGTH, NULL, FALSE)); PhSetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS, GetDlgItemInt(hwndDlg, IDC_MAXHOPS, NULL, FALSE)); + PhSetIntegerSetting(SETTING_NAME_EXTENDED_TCP_STATS, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_EXTENDED_TCP)) == BST_CHECKED); } break; } diff --git a/plugins/NetworkTools/resource.h b/plugins/NetworkTools/resource.h index 204de7750856..4c00392ba362 100644 --- a/plugins/NetworkTools/resource.h +++ b/plugins/NetworkTools/resource.h @@ -270,6 +270,7 @@ #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 // @@ -277,7 +278,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 107 #define _APS_NEXT_COMMAND_VALUE 40006 -#define _APS_NEXT_CONTROL_VALUE 1032 +#define _APS_NEXT_CONTROL_VALUE 1033 #define _APS_NEXT_SYMED_VALUE 104 #endif #endif From 5232ef00acd82c159e48217f8968d70cc4fa402f Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 27 Nov 2017 07:51:49 +1100 Subject: [PATCH 596/839] Export Ipv6 ScopeId for plugins --- ProcessHacker/include/netprv.h | 2 ++ ProcessHacker/netprv.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/ProcessHacker/include/netprv.h b/ProcessHacker/include/netprv.h index 7b26038ed53f..06398f6d988e 100644 --- a/ProcessHacker/include/netprv.h +++ b/ProcessHacker/include/netprv.h @@ -36,6 +36,8 @@ typedef struct _PH_NETWORK_ITEM LARGE_INTEGER CreateTime; ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE]; + ULONG LocalScopeId; + ULONG RemoteScopeId; } PH_NETWORK_ITEM, *PPH_NETWORK_ITEM; // end_phapppub diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index a096ec281b85..7df386b212c1 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -43,6 +43,8 @@ typedef struct _PH_NETWORK_CONNECTION 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 @@ -589,6 +591,8 @@ VOID PhNetworkProviderUpdate( 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. @@ -924,6 +928,9 @@ BOOLEAN PhGetNetworkConnections( 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++; } From d40f25e4730055d530ce26222322d5d4acb71df2 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 28 Nov 2017 17:06:10 +1100 Subject: [PATCH 597/839] Increase column order limiting (https://wj32.org/processhacker/forums/viewtopic.php?t=2798) --- ProcessHacker/chcol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/chcol.c b/ProcessHacker/chcol.c index 68c16754a6c7..75e453219617 100644 --- a/ProcessHacker/chcol.c +++ b/ProcessHacker/chcol.c @@ -214,7 +214,7 @@ INT_PTR CALLBACK PhpColumnsDlgProc( break; case IDOK: { -#define ORDER_LIMIT 100 +#define ORDER_LIMIT 200 PPH_LIST activeList; ULONG activeCount; ULONG i; From f6e1662b94a9e99ba1288a675f0bbe25c7027445 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 29 Nov 2017 19:19:12 +1100 Subject: [PATCH 598/839] Fix emenu crash from commit d356b10289791bb5472291ae2eff50baa2d3f4b4 --- ProcessHacker/mainwnd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 9b039b2c7dc8..01e864229ad7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -3309,10 +3309,11 @@ VOID PhMwpUpdateUsersMenu( escapedMenuText = PhEscapeStringForMenuPrefix(&menuText->sr); PhDereferenceObject(menuText); - PhInsertEMenuItem(UsersMenu, userMenu = PhCreateEMenuItem(0, IDR_USER, escapedMenuText->Buffer, NULL, UlongToPtr(sessions[i].SessionId)), -1); + 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, -1); - PhAutoDereferenceObject(escapedMenuText); + PhDereferenceObject(escapedMenuText); } WinStationFreeMemory(sessions); From 2dd3232cb70808fedeb5e7269ac52eb1f9d9273e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 30 Nov 2017 15:05:23 +1100 Subject: [PATCH 599/839] Add column set support, Add View menu > Column set options #180 --- ProcessHacker/ProcessHacker.rc | 21 + ProcessHacker/ProcessHacker.vcxproj | 2 + ProcessHacker/ProcessHacker.vcxproj.filters | 6 + ProcessHacker/colsetmgr.c | 651 ++++++++++++++++++++ ProcessHacker/include/colsetmgr.h | 40 ++ ProcessHacker/include/phplug.h | 1 + ProcessHacker/include/proctree.h | 10 + ProcessHacker/mainwnd.c | 82 ++- ProcessHacker/mwpgproc.c | 34 + ProcessHacker/proctree.c | 27 + ProcessHacker/resource.h | 11 +- ProcessHacker/settings.c | 1 + 12 files changed, 880 insertions(+), 6 deletions(-) create mode 100644 ProcessHacker/colsetmgr.c create mode 100644 ProcessHacker/include/colsetmgr.h diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 02976aa4cd90..793a20ab1c10 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1887,6 +1887,19 @@ 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -2449,6 +2462,14 @@ BEGIN TOPMARGIN, 2 BOTTOMMARGIN, 258 END + + IDD_COLUMNSETS, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 227 + TOPMARGIN, 4 + BOTTOMMARGIN, 184 + END END #endif // APSTUDIO_INVOKED diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 9b4c0047495c..45896e808026 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -234,6 +234,7 @@ + @@ -348,6 +349,7 @@ + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index b969ed3f7d8f..3a8cdc58efd6 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -384,6 +384,9 @@ Process Hacker + + Process Hacker + @@ -545,6 +548,9 @@ Headers + + Headers + diff --git a/ProcessHacker/colsetmgr.c b/ProcessHacker/colsetmgr.c new file mode 100644 index 000000000000..63c0617b6c55 --- /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 +#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(L"ProcessTreeColumnSetConfig", &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); +} + +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(L"ProcessTreeColumnSetConfig"); + 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); + + SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + } + else + { + context = (PCOLUMNSET_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + } + + 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); + + RemoveProp(hwndDlg, PhMakeContextAtom()); + 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/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/phplug.h b/ProcessHacker/include/phplug.h index 18ae65492051..2be5efb9d36c 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -543,6 +543,7 @@ typedef struct _PH_PLUGIN_MENU_ITEM } 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 diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index 5ddd6ef7b0b5..65dba46ed220 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -242,6 +242,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/mainwnd.c b/ProcessHacker/mainwnd.c index 01e864229ad7..ad2de4ecbda7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -2351,6 +2352,73 @@ VOID PhMwpDispatchMenuCommand( } } break; + case ID_VIEW_ORGANIZECOLUMNSETS: + { + PhShowColumnSetEditorDialog(PhMainWndHandle, L"ProcessTreeColumnSetConfig"); + } + return; + case ID_VIEW_SAVECOLUMNSET: + { + PPH_EMENU_ITEM menuItem; + PPH_STRING columnSetName = NULL; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + while (PhaChoiceDialog( + PhMainWndHandle, + 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(PhMainWndHandle, WM_COMMAND, ItemId, 0); @@ -2363,7 +2431,7 @@ VOID PhMwpInitializeSubMenu( { PPH_EMENU_ITEM menuItem; - if (Index == 0) // Hacker + if (Index == PH_MENU_ITEM_LOCATION_HACKER) // Hacker { // Fix some menu items. if (PhGetOwnTokenAttributes().Elevated) @@ -2387,7 +2455,7 @@ VOID PhMwpInitializeSubMenu( // Fix up the Computer menu. PhMwpSetupComputerMenu(Menu); } - else if (Index == 1) // View + else if (Index == PH_MENU_ITEM_LOCATION_VIEW) // View { PPH_EMENU_ITEM trayIconsMenuItem; ULONG i; @@ -2506,7 +2574,7 @@ VOID PhMwpInitializeSubMenu( if (PhMwpUpdateAutomatically && (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_VIEW_UPDATEAUTOMATICALLY))) menuItem->Flags |= PH_EMENU_CHECKED; } - else if (Index == 2) // Tools + else if (Index == PH_MENU_ITEM_LOCATION_TOOLS) // Tools { if (!PhGetIntegerSetting(L"HiddenProcessesMenuEnabled")) { @@ -3309,7 +3377,13 @@ VOID PhMwpUpdateUsersMenu( 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)); + 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, -1); diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index f0b222193d3d..bc88c3fd8fda 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -110,6 +111,7 @@ 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); @@ -135,6 +137,38 @@ BOOLEAN PhMwpProcessesPageCallback( menuItem->Flags |= PH_EMENU_DISABLED; } } + + PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), startIndex + 4); + PhInsertEMenuItem(menu, menuItem = PhCreateEMenuItem(0, ID_VIEW_ORGANIZECOLUMNSETS, L"Organize column sets...", NULL, NULL), startIndex + 5); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SAVECOLUMNSET, L"Save column set...", NULL, NULL), startIndex + 6); + PhInsertEMenuItem(menu, columnSetMenuItem = PhCreateEMenuItem(0, 0, L"&Load 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(WCHAR)), NULL, NULL); + PhInsertEMenuItem(columnSetMenuItem, menuItem, -1); + } + } + + PhDeleteColumnSetList(columnSetList); + } } return TRUE; case MainTabPageLoadSettings: diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index a57c7792c777..7dadc864a055 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -263,6 +263,33 @@ VOID PhSaveSettingsProcessTreeList( 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 ) diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index b53551cec92b..82c94b02a94a 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -128,6 +128,7 @@ #define IDD_PLUGINPROPERTIES 228 #define IDD_PLUGINSDISABLED 241 #define IDD_PROCWMIPROVIDERS 242 +#define IDD_COLUMNSETS 243 #define IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 @@ -221,6 +222,7 @@ #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 @@ -359,6 +361,7 @@ #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 @@ -528,6 +531,7 @@ #define IDC_PLUGINTREE 1400 #define IDC_DISABLED 1401 #define IDC_LIST_DISABLED 1402 +#define IDC_COLUMNSETLIST 1403 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -697,6 +701,9 @@ #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 @@ -732,9 +739,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 243 +#define _APS_NEXT_RESOURCE_VALUE 244 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1403 +#define _APS_NEXT_CONTROL_VALUE 1404 #define _APS_NEXT_SYMED_VALUE 170 #endif #endif diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index dad506a77231..6df4cc013175 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -127,6 +127,7 @@ VOID PhAddDefaultSettings( 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"); From d238cba0e166857a507ee475093096fde941cbf7 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 1 Dec 2017 22:07:14 +1100 Subject: [PATCH 600/839] Fix toolbar regression updating from 2.39 to 3.0 --- plugins/ToolStatus/toolstatus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index fee12ad7a937..b5e370dcf73f 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -41,7 +41,7 @@ #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_TOOLBAR_CONFIG (PLUGIN_NAME L".ToolbarButtonConfig") #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") From 9b431bd69ee8d1ef4a425074eaf91ab1cf74a395 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 3 Dec 2017 11:53:29 +1100 Subject: [PATCH 601/839] Add missing description for DisallowStrippedImages --- ProcessHacker/procmtgn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/procmtgn.c b/ProcessHacker/procmtgn.c index cf72c479d837..1f07687c9bea 100644 --- a/ProcessHacker/procmtgn.c +++ b/ProcessHacker/procmtgn.c @@ -177,6 +177,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, ')'); } From bfca80bd7f365241e6d919fba209650fbe186451 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 3 Dec 2017 11:57:47 +1100 Subject: [PATCH 602/839] Fix column set leak #180 --- ProcessHacker/mainwnd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index ad2de4ecbda7..c803890aa2ef 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2381,7 +2381,7 @@ VOID PhMwpDispatchMenuCommand( break; } - if (!PhIsNullOrEmptyString(columnSetName)) + if (columnSetName) { PPH_STRING treeSettings; PPH_STRING sortSettings; @@ -2393,6 +2393,7 @@ VOID PhMwpDispatchMenuCommand( PhDereferenceObject(treeSettings); PhDereferenceObject(sortSettings); + PhDereferenceObject(columnSetName); } } return; From 2b3180c117e75b1d8e49dcbe9fbb8aced41927ff Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 3 Dec 2017 12:01:08 +1100 Subject: [PATCH 603/839] Update PCRE to 10.30 (https://www.pcre.org/changelog.txt) --- ProcessHacker/ProcessHacker.vcxproj | 8 +- ProcessHacker/pcre/config.h | 105 +- ProcessHacker/pcre/pcre2.h | 283 +- ProcessHacker/pcre/pcre2_auto_possess.c | 12 +- ProcessHacker/pcre/pcre2_chartables.c | 1 - ProcessHacker/pcre/pcre2_compile.c | 764 +- ProcessHacker/pcre/pcre2_config.c | 25 +- ProcessHacker/pcre/pcre2_context.c | 125 +- ProcessHacker/pcre/pcre2_dfa_match.c | 434 +- ProcessHacker/pcre/pcre2_error.c | 15 +- ProcessHacker/pcre/pcre2_find_bracket.c | 2 - ProcessHacker/pcre/pcre2_internal.h | 124 +- ProcessHacker/pcre/pcre2_intmodedep.h | 152 +- ProcessHacker/pcre/pcre2_jit_compile.c | 3330 +++++--- ProcessHacker/pcre/pcre2_jit_match.c | 8 +- ProcessHacker/pcre/pcre2_jit_test.c | 30 +- ProcessHacker/pcre/pcre2_maketables.c | 2 - ProcessHacker/pcre/pcre2_match.c | 9950 +++++++++++------------ ProcessHacker/pcre/pcre2_match_data.c | 7 +- ProcessHacker/pcre/pcre2_newline.c | 1 - ProcessHacker/pcre/pcre2_ord2utf.c | 2 - ProcessHacker/pcre/pcre2_pattern_info.c | 34 +- ProcessHacker/pcre/pcre2_printint.c | 5 +- ProcessHacker/pcre/pcre2_serialize.c | 6 +- ProcessHacker/pcre/pcre2_string_utils.c | 1 - ProcessHacker/pcre/pcre2_study.c | 39 +- ProcessHacker/pcre/pcre2_substitute.c | 1 - ProcessHacker/pcre/pcre2_substring.c | 2 +- ProcessHacker/pcre/pcre2_tables.c | 449 +- ProcessHacker/pcre/pcre2_ucd.c | 5448 +++++++------ ProcessHacker/pcre/pcre2_ucp.h | 38 +- ProcessHacker/pcre/pcre2_valid_utf.c | 9 +- ProcessHacker/pcre/pcre2_xclass.c | 1 - ProcessHacker/pcre/pcre2posix.c | 21 +- ProcessHacker/pcre/pcre2posix.h | 6 +- 35 files changed, 11625 insertions(+), 9815 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 45896e808026..f5b99fe1b1a1 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -85,7 +85,7 @@ Disabled $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -117,7 +117,7 @@ Disabled $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -151,7 +151,7 @@ MaxSpeed true $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded false @@ -190,7 +190,7 @@ MaxSpeed true $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded false diff --git a/ProcessHacker/pcre/config.h b/ProcessHacker/pcre/config.h index b71d730d3561..2843be569595 100644 --- a/ProcessHacker/pcre/config.h +++ b/ProcessHacker/pcre/config.h @@ -1,6 +1,6 @@ +/* src/config.h. Generated from config.h.in by configure. */ /* src/config.h.in. Generated from configure.ac by autoheader. */ - /* PCRE2 is written in Standard C, but there are a few non-standard things it can cope with, allowing it to run on SunOS4 and other "close to standard" systems. @@ -154,13 +154,11 @@ 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 pcre2_match() may use while matching + a pattern. The value is in kilobytes. */ +#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 @@ -172,28 +170,31 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to the sub-directory where libtool stores uninstalled libraries. */ -#undef LT_OBJDIR +/* This is ignored unless you are using libtool. */ +#ifndef LT_OBJDIR +#define LT_OBJDIR ".libs/" +#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. 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. */ +#ifndef MATCH_LIMIT_DEPTH +#define MATCH_LIMIT_DEPTH MATCH_LIMIT #endif /* This limit is parameterized just in case anybody ever wants to change it. @@ -215,32 +216,32 @@ 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 4 #endif /* Name of package */ -#undef PACKAGE +#define PACKAGE "pcre2" /* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT +#define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ -#undef PACKAGE_NAME +#define PACKAGE_NAME "PCRE2" /* Define to the full name and version of this package. */ -#undef PACKAGE_STRING +#define PACKAGE_STRING "PCRE2 10.30" /* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME +#define PACKAGE_TARNAME "pcre2" /* Define to the home page for this package. */ -#undef PACKAGE_URL +#define PACKAGE_URL "" /* Define to the version of this package. */ -#undef PACKAGE_VERSION +#define PACKAGE_VERSION "10.30" /* 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 @@ -267,19 +268,9 @@ sure both macros are undefined; an emulation function will then be used. */ #define PCRE2GREP_MAX_BUFSIZE 1048576 #endif -/* to make a symbol visible */ -#undef PCRE2POSIX_EXP_DECL - -/* to make a symbol visible */ -#undef PCRE2POSIX_EXP_DEFN - /* Define to any value to include debugging code. */ #undef PCRE2_DEBUG -/* to make a symbol visible */ -#undef PCRE2_EXP_DECL - - /* If you are compiling for a system other than a Unix-like system or Win32, and it needs some magic to be inserted before the definition of a function that is exported by the library, define this macro to @@ -300,6 +291,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 @@ -335,10 +331,10 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* Define to any value to enable the 32 bit PCRE2 library. */ -//#undef SUPPORT_PCRE2_32 +#undef SUPPORT_PCRE2_32 /* Define to any value to enable the 8 bit PCRE2 library. */ -//#undef SUPPORT_PCRE2_8 +#undef SUPPORT_PCRE2_8 /* Define to any value to enable support for Unicode and UTF encoding. This will work even in an EBCDIC environment, but it is incompatible with the @@ -353,28 +349,27 @@ sure both macros are undefined; an emulation function will then be used. */ /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE -# undef _ALL_SOURCE +# define _ALL_SOURCE 1 #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE -# undef _GNU_SOURCE +# define _GNU_SOURCE 1 #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS -# undef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE -# undef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ -# undef __EXTENSIONS__ +# define __EXTENSIONS__ 1 #endif - /* Version number of package */ -#undef VERSION +#define VERSION "10.30" /* Define to 1 if on MINIX. */ #undef _MINIX @@ -387,11 +382,11 @@ sure both macros are undefined; an emulation function will then be used. */ #undef _POSIX_SOURCE /* Define to empty if `const' does not conform to ANSI C. */ -#undef const +/* #undef const */ /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ -#undef int64_t +/* #undef int64_t */ /* Define to `unsigned int' if does not define. */ -#undef size_t +/* #undef size_t */ diff --git a/ProcessHacker/pcre/pcre2.h b/ProcessHacker/pcre/pcre2.h index 550a2b93d81f..2cceb5cb79ba 100644 --- a/ProcessHacker/pcre/pcre2.h +++ b/ProcessHacker/pcre/pcre2.h @@ -5,22 +5,22 @@ /* 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-2017 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 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. + * 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. + * 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 @@ -48,9 +48,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE2_MAJOR 10 -#define PCRE2_MINOR 23 +#define PCRE2_MINOR 30 #define PCRE2_PRERELEASE -#define PCRE2_DATE 2017-02-14 +#define PCRE2_DATE 2017-08-14 /* 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 @@ -77,7 +77,7 @@ don't change existing definitions of PCRE2_EXP_DECL. */ a "calling convention" before exported function names. (This is secondhand information; I know nothing about MSVC myself). For example, something like -void __cdecl function(....) + 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 @@ -100,23 +100,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 */ @@ -142,17 +143,26 @@ extern "C" { #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(). */ +/* These are for pcre2_jit_compile(). */ #define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */ #define PCRE2_JIT_PARTIAL_SOFT 0x00000002u #define PCRE2_JIT_PARTIAL_HARD 0x00000004u - /* These are for pcre2_match(), pcre2_dfa_match(), and pcre2_jit_match(). Note - that PCRE2_ANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these - functions (though pcre2_jit_match() ignores the latter since it bypasses all - sanity checks). */ +/* These are for pcre2_match(), pcre2_dfa_match(), and pcre2_jit_match(). Note +that PCRE2_ANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these +functions (though pcre2_jit_match() ignores the latter since it bypasses all +sanity checks). */ #define PCRE2_NOTBOL 0x00000001u #define PCRE2_NOTEOL 0x00000002u @@ -161,13 +171,13 @@ extern "C" { #define PCRE2_PARTIAL_SOFT 0x00000010u #define PCRE2_PARTIAL_HARD 0x00000020u - /* These are additional options for pcre2_dfa_match(). */ +/* These are additional options for pcre2_dfa_match(). */ #define PCRE2_DFA_RESTART 0x00000040u #define PCRE2_DFA_SHORTEST 0x00000080u - /* These are additional options for pcre2_substitute(), which passes any others - through to pcre2_match(). */ +/* These are additional options for pcre2_substitute(), which passes any others +through to pcre2_match(). */ #define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u #define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u @@ -175,30 +185,41 @@ extern "C" { #define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u #define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u - /* A further option for pcre2_match(), not allowed for pcre2_dfa_match(), - ignored for pcre2_jit_match(). */ +/* A further option for pcre2_match(), not allowed for pcre2_dfa_match(), +ignored for pcre2_jit_match(). */ #define PCRE2_NO_JIT 0x00002000u - /* 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. */ +/* 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. */ #define PCRE2_NEWLINE_CR 1 #define PCRE2_NEWLINE_LF 2 #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: no match and partial match are "expected" errors. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) - /* Error codes for UTF-8 validity checks */ +/* Error codes for UTF-8 validity checks */ #define PCRE2_ERROR_UTF8_ERR1 (-3) #define PCRE2_ERROR_UTF8_ERR2 (-4) @@ -222,21 +243,21 @@ extern "C" { #define PCRE2_ERROR_UTF8_ERR20 (-22) #define PCRE2_ERROR_UTF8_ERR21 (-23) - /* Error codes for UTF-16 validity checks */ +/* Error codes for UTF-16 validity checks */ #define PCRE2_ERROR_UTF16_ERR1 (-24) #define PCRE2_ERROR_UTF16_ERR2 (-25) #define PCRE2_ERROR_UTF16_ERR3 (-26) - /* Error codes for UTF-32 validity checks */ +/* Error codes for UTF-32 validity checks */ #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. */ +/* 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 */ @@ -262,7 +283,8 @@ extern "C" { #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) @@ -272,8 +294,11 @@ extern "C" { #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) + - /* Request types for pcre2_pattern_info() */ +/* Request types for pcre2_pattern_info() */ #define PCRE2_INFO_ALLOPTIONS 0 #define PCRE2_INFO_ARGOPTIONS 1 @@ -296,11 +321,14 @@ extern "C" { #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 - /* Request types for pcre2_config(). */ +/* Request types for pcre2_config(). */ #define PCRE2_CONFIG_BSR 0 #define PCRE2_CONFIG_JIT 1 @@ -309,34 +337,36 @@ extern "C" { #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 - /* Types for code units in patterns and subject strings. */ +/* 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; \ @@ -348,6 +378,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; \ \ @@ -360,11 +393,11 @@ 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. */ #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ @@ -401,15 +434,15 @@ typedef struct pcre2_callout_enumerate_block { \ } pcre2_callout_enumerate_block; - /* List the generic forms of all other functions in macros, which will be - expanded for each width below. Start with functions that give general - information. */ +/* List the generic forms of all other functions in macros, which will be +expanded for each width below. Start with functions that give general +information. */ #define PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); - /* Functions for manipulating contexts. */ +/* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ @@ -431,6 +464,8 @@ 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 \ @@ -451,6 +486,10 @@ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ 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 \ @@ -461,8 +500,20 @@ 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. */ +/* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ @@ -476,7 +527,7 @@ PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ *pcre2_code_copy_with_tables(const pcre2_code *); - /* Functions that give information about a compiled pattern. */ +/* Functions that give information about a compiled pattern. */ #define PCRE2_PATTERN_INFO_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -486,7 +537,7 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ int (*)(pcre2_callout_enumerate_block *, void *), void *); - /* Functions for running a match and inspecting the result. */ +/* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ @@ -512,7 +563,7 @@ PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ pcre2_get_startchar(pcre2_match_data *); - /* Convenience functions for handling matched substrings. */ +/* Convenience functions for handling matched substrings. */ #define PCRE2_SUBSTRING_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -543,7 +594,7 @@ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **); - /* Functions for serializing / deserializing compiled patterns. */ +/* Functions for serializing / deserializing compiled patterns. */ #define PCRE2_SERIALIZE_FUNCTIONS \ PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ @@ -558,7 +609,7 @@ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_serialize_free(uint8_t *); - /* Convenience function for match + substitute. */ +/* Convenience function for match + substitute. */ #define PCRE2_SUBSTITUTE_FUNCTION \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -567,7 +618,17 @@ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *); - /* Functions for JIT processing */ +/* 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_CALL_CONVENTION \ @@ -585,7 +646,7 @@ PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ pcre2_jit_stack_free(pcre2_jit_stack *); - /* Other miscellaneous functions. */ +/* Other miscellaneous functions. */ #define PCRE2_OTHER_FUNCTIONS \ PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ @@ -594,19 +655,19 @@ 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 - three-level macro scheme is necessary to get the macros expanded when we want - them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for - generating three versions of everything below. After that, PCRE2_SUFFIX will be - re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as - pcre2_compile are called by application code. */ +/* Define macros that generate width-specific names from generic versions. The +three-level macro scheme is necessary to get the macros expanded when we want +them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for +generating three versions of everything below. After that, PCRE2_SUFFIX will be +re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as +pcre2_compile are called by application code. */ #define PCRE2_JOIN(a,b) a ## b #define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b) #define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH) - /* Data types */ +/* Data types */ #define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR) #define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR) @@ -618,22 +679,24 @@ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ #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_) - /* Data blocks */ +/* Data blocks */ #define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_) #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_) - /* Functions: the complete list in alphabetical order */ +/* Functions: the complete list in alphabetical order */ #define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) #define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) @@ -644,6 +707,10 @@ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ #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_) @@ -667,6 +734,7 @@ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ #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_) @@ -675,14 +743,17 @@ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ #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_) @@ -696,9 +767,14 @@ PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ #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. */ +/* Now generate all three sets of width-specific structures and function +prototypes. */ #define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \ PCRE2_TYPES_LIST \ @@ -706,6 +782,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 \ @@ -717,24 +795,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 @@ -746,9 +825,9 @@ PCRE2_OTHER_FUNCTIONS #undef PCRE2_OTHER_FUNCTIONS #undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS - /* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine - PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make - PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ +/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine +PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make +PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ #undef PCRE2_SUFFIX #ifndef PCRE2_CODE_UNIT_WIDTH @@ -774,4 +853,4 @@ PCRE2_OTHER_FUNCTIONS #endif /* PCRE2_H_IDEMPOTENT_GUARD */ - /* End of pcre2.h */ +/* End of pcre2.h */ diff --git a/ProcessHacker/pcre/pcre2_auto_possess.c b/ProcessHacker/pcre/pcre2_auto_possess.c index 2c987bbf5b76..ad3543f62737 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,8 +42,6 @@ POSSIBILITY OF SUCH DAMAGE. repeats into possessive repeats where possible. */ -#define HAVE_CONFIG_H - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -590,7 +588,6 @@ for(;;) 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 @@ -603,7 +600,6 @@ for(;;) continue; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: next_code = code + GET(code, 1); @@ -627,8 +623,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); @@ -1079,7 +1075,7 @@ 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) { diff --git a/ProcessHacker/pcre/pcre2_chartables.c b/ProcessHacker/pcre/pcre2_chartables.c index 23578d8932b0..203cb1a4ab04 100644 --- a/ProcessHacker/pcre/pcre2_chartables.c +++ b/ProcessHacker/pcre/pcre2_chartables.c @@ -20,7 +20,6 @@ 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 #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_compile.c b/ProcessHacker/pcre/pcre2_compile.c index 0e8199ff4123..e8de8dabbce4 100644 --- a/ProcessHacker/pcre/pcre2_compile.c +++ b/ProcessHacker/pcre/pcre2_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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -41,7 +41,6 @@ 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" @@ -164,7 +163,7 @@ the length of compiled items varies with this. In the real compile phase, this workspace is not currently used. */ -#define COMPILE_WORK_SIZE (2048*LINK_SIZE) /* Size in code units */ +#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */ #define C16_WORK_SIZE \ ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t)) @@ -694,17 +693,29 @@ static int posix_substitutes[] = { #define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t))) #endif /* SUPPORT_UNICODE */ -/* Masks for checking option settings. */ +/* 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 \ - (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) + (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 @@ -720,7 +731,8 @@ enum { ERR0 = COMPILE_ERROR_BASE, 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 }; + ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90, + ERR91, ERR92}; /* 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 @@ -731,8 +743,9 @@ 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_LIMR }; /* Read integer value for recursion limit */ + PSO_LIMD }; /* Read integer value for depth limit */ typedef struct pso { const uint8_t *name; @@ -753,12 +766,15 @@ static pso pso_list[] = { { (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_RECURSION_EQ, 16, PSO_LIMR, 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 } @@ -1474,7 +1490,10 @@ else if (utf) { if (c > 0x10ffffU) *errorcodeptr = ERR77; - else if (c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; + 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; } @@ -1608,7 +1627,7 @@ else if (c >= CHAR_8) break; - /* Fall through with a digit less than 8 */ + /* 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 @@ -1663,7 +1682,8 @@ else } else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) { - if (utf && c >= 0xd800 && c <= 0xdfff) + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) { ptr--; *errorcodeptr = ERR73; @@ -1732,7 +1752,8 @@ else } else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) { - if (utf && c >= 0xd800 && c <= 0xdfff) + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) { ptr--; *errorcodeptr = ERR73; @@ -1905,7 +1926,7 @@ if (c == CHAR_LEFT_CURLY_BRACKET) { if (ptr >= cb->end_pattern) goto ERROR_RETURN; c = *ptr++; - if (c == CHAR_NULL) goto ERROR_RETURN; + if (c == CHAR_NUL) goto ERROR_RETURN; if (c == CHAR_RIGHT_CURLY_BRACKET) break; name[i] = c; } @@ -2163,7 +2184,7 @@ the parsed pattern. Arguments: ptr current pattern pointer pcalloutptr points to a pointer to previous callout, or NULL - options the compiling options + auto_callout TRUE if auto_callouts are enabled parsed_pattern the parsed pattern pointer cb compile block @@ -2171,7 +2192,7 @@ Returns: possibly updated parsed_pattern pointer. */ static uint32_t * -manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, uint32_t options, +manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout, uint32_t *parsed_pattern, compile_block *cb) { uint32_t *previous_callout = *pcalloutptr; @@ -2179,7 +2200,7 @@ uint32_t *previous_callout = *pcalloutptr; if (previous_callout != NULL) previous_callout[2] = ptr - cb->start_pattern - (PCRE2_SIZE)previous_callout[1]; -if ((options & PCRE2_AUTO_CALLOUT) == 0) previous_callout = NULL; else +if (!auto_callout) previous_callout = NULL; else { if (previous_callout == NULL || previous_callout != parsed_pattern - 4 || @@ -2227,12 +2248,17 @@ typedef struct nest_save { uint16_t reset_group; uint16_t max_group; uint16_t flags; + uint32_t options; } nest_save; -#define NSF_RESET 0x0001u -#define NSF_EXTENDED 0x0002u -#define NSF_DUPNAMES 0x0004u -#define NSF_CONDASSERT 0x0008u +#define NSF_RESET 0x0001u +#define NSF_CONDASSERT 0x0002u + +/* Of the options that are changeable within the pattern, these are tracked +during parsing. The rest are used from META_OPTIONS items when compiling. */ + +#define PARSE_TRACKED_OPTIONS \ + (PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_NO_AUTO_CAPTURE) /* States used for analyzing ranges in character classes. The two OK values must be last. */ @@ -2276,15 +2302,57 @@ 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 = NULL; -nest_save *end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size); +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 @@ -2293,9 +2361,11 @@ 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 */ +/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */ -*has_lookbehind = FALSE; +if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED; + +/* Now scan the pattern */ while (ptr < ptrend) { @@ -2306,7 +2376,6 @@ while (ptr < ptrend) uint32_t prev_meta_quantifier; BOOL prev_okquantifier; PCRE2_SPTR tempptr; - PCRE2_SPTR thisptr; PCRE2_SIZE offset; if (parsed_pattern >= parsed_pattern_end) @@ -2318,7 +2387,7 @@ while (ptr < ptrend) if (nest_depth > cb->cx->parens_nest_limit) { errorcode = ERR19; - goto FAILED; + goto FAILED; /* Parentheses too deeply nested */ } /* Get next input character, save its position for callout handling. */ @@ -2345,8 +2414,8 @@ while (ptr < ptrend) goto FAILED; } if (!inverbname && after_manual_callout-- <= 0) - parsed_pattern = manage_callouts(thisptr, &previous_callout, options, - parsed_pattern, cb); + parsed_pattern = manage_callouts(thisptr, &previous_callout, + auto_callout, parsed_pattern, cb); PARSED_LITERAL(c, parsed_pattern); meta_quantifier = 0; } @@ -2491,7 +2560,7 @@ while (ptr < ptrend) !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode)))) { if (after_manual_callout-- <= 0) - parsed_pattern = manage_callouts(thisptr, &previous_callout, options, + parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, parsed_pattern, cb); } @@ -2575,11 +2644,23 @@ while (ptr < ptrend) /* ---- Escape sequence ---- */ case CHAR_BACKSLASH: + tempptr = ptr; escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, FALSE, cb); - if (errorcode != 0) goto FAILED; + 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 character. */ + /* The escape was a data escape or literal character. */ if (escape == 0) { @@ -2631,12 +2712,12 @@ while (ptr < ptrend) case ESC_C: #ifdef NEVER_BACKSLASH_C errorcode = ERR85; - goto FAILED; + goto ESCAPE_FAILED; #else if ((options & PCRE2_NEVER_BACKSLASH_C) != 0) { errorcode = ERR83; - goto FAILED; + goto ESCAPE_FAILED; } #endif okquantifier = TRUE; @@ -2646,7 +2727,7 @@ while (ptr < ptrend) case ESC_X: #ifndef SUPPORT_UNICODE errorcode = ERR45; /* Supported only with Unicode support */ - goto FAILED; + goto ESCAPE_FAILED; #endif case ESC_H: case ESC_h: @@ -2711,7 +2792,7 @@ while (ptr < ptrend) BOOL negated; uint16_t ptype = 0, pdata = 0; if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) - goto FAILED; + goto ESCAPE_FAILED; if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; *parsed_pattern++ = META_ESCAPE + escape; *parsed_pattern++ = (ptype << 16) | pdata; @@ -2719,7 +2800,7 @@ while (ptr < ptrend) } #else errorcode = ERR45; - goto FAILED; + goto ESCAPE_FAILED; #endif break; /* End \P and \p */ @@ -2735,7 +2816,7 @@ while (ptr < ptrend) *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE)) { errorcode = (escape == ESC_g)? ERR57 : ERR69; - goto FAILED; + goto ESCAPE_FAILED; } terminator = (*ptr == CHAR_LESS_THAN_SIGN)? CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? @@ -2753,18 +2834,18 @@ while (ptr < ptrend) if (p >= ptrend || *p != terminator) { errorcode = ERR57; - goto FAILED; + goto ESCAPE_FAILED; } ptr = p; goto SET_RECURSION; } - if (errorcode != 0) goto FAILED; + if (errorcode != 0) goto ESCAPE_FAILED; } /* Not a numerical recursion */ if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, - &errorcode, cb)) goto FAILED; + &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 */ @@ -2776,7 +2857,7 @@ while (ptr < ptrend) PUTOFFSET(offset, parsed_pattern); okquantifier = TRUE; - break; + break; /* End special escape processing */ } break; /* End escape sequence processing */ @@ -2908,7 +2989,8 @@ while (ptr < ptrend) /* 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 we skip them too. This makes for compatibility with Perl. */ + 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) @@ -2923,6 +3005,9 @@ while (ptr < ptrend) 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; @@ -2970,6 +3055,12 @@ while (ptr < ptrend) 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 @@ -3017,21 +3108,23 @@ while (ptr < ptrend) 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. PCRE - does not have a warning mode, so we give an error, because this is - likely an error on the user's part. */ + 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 && *ptr == CHAR_MINUS) + 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" just 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. */ + /* 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; @@ -3113,10 +3206,23 @@ while (ptr < ptrend) else { + tempptr = ptr; escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, TRUE, cb); - if (errorcode != 0) goto FAILED; + 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; @@ -3150,7 +3256,7 @@ while (ptr < ptrend) if (class_range_state == RANGE_STARTED) { errorcode = ERR50; - goto FAILED; + goto CLASS_ESCAPE_FAILED; } /* Of the remaining escapes, only those that define characters are @@ -3161,7 +3267,7 @@ while (ptr < ptrend) { case ESC_N: errorcode = ERR71; /* Not supported in a class */ - goto FAILED; + goto CLASS_ESCAPE_FAILED; case ESC_H: case ESC_h: @@ -3224,13 +3330,24 @@ while (ptr < ptrend) } #else errorcode = ERR45; - goto FAILED; + goto CLASS_ESCAPE_FAILED; #endif break; /* End \P and \p */ default: /* All others are not allowed in a class */ errorcode = ERR7; - goto FAILED_BACK; + 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; } } @@ -3388,8 +3505,7 @@ while (ptr < ptrend) } 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; + top_nest->options = options & PARSE_TRACKED_OPTIONS; /* Start of non-capturing group that resets the capture count for each branch. */ @@ -3404,9 +3520,7 @@ while (ptr < ptrend) ptr++; } - /* Scan for options imsxJU. We need to keep track of (?x) and (?J) for - use while scanning. The other options are used during the compiling - phases. */ + /* Scan for options imnsxJU to be set or unset. */ else { @@ -3429,16 +3543,36 @@ while (ptr < ptrend) 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_x: *optset |= PCRE2_EXTENDED; 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 @@ -3917,8 +4051,7 @@ while (ptr < ptrend) } top_nest->nest_depth = nest_depth; top_nest->flags = NSF_CONDASSERT; - if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED; - if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES; + top_nest->options = options & PARSE_TRACKED_OPTIONS; } break; @@ -4039,20 +4172,17 @@ while (ptr < ptrend) break; /* End of group; reset the capture count to the maximum if we are in a (?| - group and/or reset the extended and dupnames options. Disallow quantifier - for a condition that is an assertion. */ + 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_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->flags & NSF_CONDASSERT) != 0) okquantifier = FALSE; if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; @@ -4079,9 +4209,24 @@ if (inverbname && ptr >= ptrend) /* Manage callout for the final item */ -parsed_pattern = manage_callouts(ptr, &previous_callout, options, +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. */ @@ -4090,6 +4235,7 @@ 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; @@ -4168,6 +4314,18 @@ for (;;) 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; } @@ -4750,7 +4908,6 @@ for (;; pptr++) int class_has_8bitchar; int i; uint32_t mclength; - uint32_t templastcapture; uint32_t skipunits; uint32_t subreqcu, subfirstcu; uint32_t groupnumber; @@ -5202,6 +5359,10 @@ for (;; pptr++) 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: { @@ -5210,12 +5371,11 @@ for (;; pptr++) *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP; *class_uchardata++ = ptype; *class_uchardata++ = pdata; -#ifdef SUPPORT_WIDE_CHARS xclass_has_prop = TRUE; -#endif class_has_8bitchar--; /* Undo! */ } break; +#endif } goto CONTINUE_CLASS; @@ -5757,7 +5917,6 @@ for (;; pptr++) pptr++; tempcode = code; tempreqvary = cb->req_varyopt; /* Save value before group */ - templastcapture = cb->lastcapture; /* Save value before group */ length_prevgroup = 0; /* Initialize for pre-compile phase */ if ((group_return = @@ -5787,12 +5946,6 @@ for (;; pptr++) if (note_group_empty && bravalue != OP_COND && group_return > 0) matched_char = TRUE; - /* If that 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->lastcapture <= templastcapture) - *code = OP_ONCE_NC; - /* If we've just compiled an assertion, pop the assert depth. */ if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) @@ -6117,7 +6270,7 @@ for (;; pptr++) } else *callout_string++ = *pp++; } - *callout_string++ = CHAR_NULL; + *callout_string++ = CHAR_NUL; /* Set the length of the entire item, the advance to its end. */ @@ -6213,24 +6366,6 @@ for (;; pptr++) tempcode = previous; op_previous = *previous; - /* 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 (op_previous == OP_RECURSE) - { - memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); - op_previous = *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; - group_return = -1; /* Set "may match empty string" */ - } - /* Now handle repetition for the different types of item. */ switch (op_previous) @@ -6315,6 +6450,77 @@ for (;; pptr++) 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. */ + + 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 @@ -6327,7 +6533,6 @@ for (;; pptr++) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -6344,10 +6549,10 @@ for (;; pptr++) 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. */ + /* 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 */ { @@ -6571,14 +6776,12 @@ for (;; pptr++) /* Convert possessive ONCE brackets to non-capturing */ - if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && - possessive_quantifier) *bracode = OP_BRA; + 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 || *bracode == OP_ONCE_NC) - *ketcode = OP_KETRMAX + repeat_type; + if (*bracode == OP_ONCE) *ketcode = OP_KETRMAX + repeat_type; /* Handle non-ONCE brackets and possessive ONCEs (which have been converted to non-capturing above). */ @@ -7147,7 +7350,6 @@ for (;; pptr++) if (mclength == 1 || req_caseopt == 0) { - firstcu = mcbuffer[0] | req_caseopt; firstcu = mcbuffer[0]; firstcuflags = req_caseopt; if (mclength != 1) @@ -7572,7 +7774,7 @@ do { /* Atomic groups */ - else if (op == OP_ONCE || op == OP_ONCE_NC) + else if (op == OP_ONCE) { if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert)) return FALSE; @@ -7702,7 +7904,7 @@ do { /* Atomic brackets */ - else if (op == OP_ONCE || op == OP_ONCE_NC) + else if (op == OP_ONCE) { if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert)) return FALSE; @@ -7724,9 +7926,8 @@ do { } /* 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. */ + 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; @@ -7937,7 +8138,6 @@ do { 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; @@ -8053,6 +8253,10 @@ 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 @@ -8069,7 +8273,7 @@ parsed_skip(uint32_t *pptr, uint32_t skiptype) { uint32_t nestlevel = 0; -for (pptr += 1;; pptr++) +for (;; pptr++) { uint32_t meta = META_CODE(*pptr); @@ -8164,11 +8368,12 @@ return pptr; /* 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. Caching is used to improve processing speed when -the same capturing group occurs many times. +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 @@ -8179,27 +8384,29 @@ Returns: the group length or a negative number */ static int -get_grouplength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, +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. */ +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) +if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0) { uint32_t groupinfo = cb->groupinfo[group]; - if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0) + if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; + if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) { - if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; - if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) - return groupinfo & GI_FIXED_LENGTH_MASK; + if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET); + return groupinfo & GI_FIXED_LENGTH_MASK; } } -/* Scan the group */ +/* Scan the group. In this case we find the end pointer of necessity. */ for(;;) { @@ -8357,11 +8564,12 @@ for (;; pptr++) } break; - /* Lookaheads can be ignored. */ + /* 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, PSKIP_KET); + pptr = parsed_skip(pptr + 1, PSKIP_KET); if (pptr == NULL) goto PARSED_SKIP_FAILED; break; @@ -8382,6 +8590,7 @@ for (;; pptr++) case META_BACKREF_BYNAME: if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0) goto ISNOTFIXED; + /* Fall through */ case META_RECURSE_BYNAME: { @@ -8436,7 +8645,8 @@ for (;; pptr++) goto RECURSE_OR_BACKREF_LENGTH; } - /* Fall through for groups >= 10 - picking up group twice does no harm. */ + /* 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. */ @@ -8459,15 +8669,24 @@ for (;; pptr++) else if (*gptr == (META_CAPTURE | group)) break; } - gptrend = parsed_skip(gptr, PSKIP_KET); + /* 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, errcodeptr, lcptr, group, + grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group, &this_recurse, cb); if (grouplength < 0) { @@ -8504,7 +8723,8 @@ for (;; pptr++) case META_NOCAPTURE: pptr++; CHECK_GROUP: - grouplength = get_grouplength(&pptr, errcodeptr, lcptr, group, recurses, cb); + grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group, + recurses, cb); if (grouplength < 0) return -1; itemlength = grouplength; break; @@ -8764,7 +8984,7 @@ 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; /* Set TRUE if a lookbehind is found */ +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 */ @@ -8786,8 +9006,9 @@ 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_recursion = UINT32_MAX; +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 */ @@ -8825,18 +9046,27 @@ if (pattern == NULL) 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) +if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 || + (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_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)); +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. */ @@ -8911,10 +9141,11 @@ for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET; /* --------------- 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. 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. */ +/* 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)); @@ -8923,70 +9154,75 @@ if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1)); ptr = pattern; skipatstart = 0; -while (patlen - skipatstart >= 2 && - ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && - ptr[skipatstart+1] == CHAR_ASTERISK) +if ((options & PCRE2_LITERAL) == 0) { - for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) + while (patlen - skipatstart >= 2 && + ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && + ptr[skipatstart+1] == CHAR_ASTERISK) { - pso *p = pso_list + i; - - if (patlen - skipatstart - 2 >= p->length && - PRIV(strncmp_c8)(ptr+skipatstart+2, (char *)(p->name), p->length) == 0) + for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) { uint32_t c, pp; + pso *p = pso_list + i; - skipatstart += p->length + 2; - switch(p->type) + if (patlen - skipatstart - 2 >= p->length && + PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name), + p->length) == 0) { - case PSO_OPT: - cb.external_options |= p->value; - break; + 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_FLG: + setflags |= p->value; + break; - case PSO_NL: - newline = p->value; - setflags |= PCRE2_NL_SET; - 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_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_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; + 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; } - if (p->type == PSO_LIMM) limit_match = c; - else limit_recursion = c; - skipatstart += pp - skipatstart; - break; + break; /* Out of the table scan loop */ } - break; /* Out of the table scan loop */ } + if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ } - if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ } /* End of pattern-start options; advance to start of real regex. */ @@ -9004,7 +9240,9 @@ if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) #endif /* Check UTF. We have the original options in 'options', with that value as -modified by (*UTF) etc in cb->external_options. */ +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) @@ -9017,6 +9255,14 @@ if (utf) 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. */ @@ -9048,6 +9294,11 @@ switch(newline) 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; @@ -9075,10 +9326,10 @@ 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). 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. */ +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) @@ -9095,6 +9346,11 @@ 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; @@ -9203,7 +9459,8 @@ 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); + 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) @@ -9212,6 +9469,13 @@ if (re == NULL) 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; @@ -9221,8 +9485,9 @@ 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_heap = limit_heap; re->limit_match = limit_match; -re->limit_recursion = limit_recursion; +re->limit_depth = limit_depth; re->first_codeunit = 0; re->last_codeunit = 0; re->bsr_convention = bsr; @@ -9394,14 +9659,19 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0 && is_anchored(codestart, 0, &cb, 0, FALSE)) 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. */ +/* 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_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0) +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, FALSE); @@ -9434,52 +9704,50 @@ if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0) } } - /* 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.) */ + /* 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 (is_startline(codestart, 0, &cb, 0, FALSE)) + 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. -Again, skip this if PCRE2_NO_START_OPTIMIZE is set. */ + /* 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|PCRE2_NO_START_OPTIMIZE)) == 0 || - (reqcuflags & REQ_VARY) != 0)) - { - re->last_codeunit = reqcu; - re->flags |= PCRE2_LASTSET; + 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). */ + /* Handle caseless required code units as for first code units (above). */ - if ((reqcuflags & REQ_CASELESS) != 0) - { - if (reqcu < 128 || (!utf && reqcu < 255)) + if ((reqcuflags & REQ_CASELESS) != 0) { - if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; - } + 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; + else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) + re->flags |= PCRE2_LASTCASELESS; #endif + } } - } -/* 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. */ + /* Finally, 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_CB_ERROR; - } + 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 diff --git a/ProcessHacker/pcre/pcre2_config.c b/ProcessHacker/pcre/pcre2_config.c index 89223ae957bc..d009c0a676a5 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,14 @@ if (where == NULL) /* Requests a length */ return PCRE2_ERROR_BADOPTION; case PCRE2_CONFIG_BSR: + case PCRE2_CONFIG_HEAPLIMIT: case PCRE2_CONFIG_JIT: case PCRE2_CONFIG_LINKSIZE: case PCRE2_CONFIG_MATCHLIMIT: + case PCRE2_CONFIG_DEPTHLIMIT: 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 +117,10 @@ switch (what) #endif break; + case PCRE2_CONFIG_HEAPLIMIT: + *((uint32_t *)where) = HEAP_LIMIT; + break; + case PCRE2_CONFIG_JIT: #ifdef SUPPORT_JIT *((uint32_t *)where) = 1; @@ -144,6 +148,10 @@ switch (what) *((uint32_t *)where) = MATCH_LIMIT; break; + case PCRE2_CONFIG_DEPTHLIMIT: + *((uint32_t *)where) = MATCH_LIMIT_DEPTH; + break; + case PCRE2_CONFIG_NEWLINE: *((uint32_t *)where) = NEWLINE_DEFAULT; break; @@ -152,16 +160,11 @@ switch (what) *((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..2c14df00805b 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 @@ -38,9 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#include -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -58,14 +56,14 @@ POSSIBILITY OF SUCH DAMAGE. static void *default_malloc(size_t size, void *data) { (void)data; -return PhAllocateSafe(size); +return malloc(size); } static void default_free(void *block, void *data) { (void)data; -PhFree(block); +free(block); } @@ -140,7 +138,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 +162,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 +169,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 +189,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 +260,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 +300,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 +317,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 +359,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 +375,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 +392,7 @@ return 0; } -/* ------------ Match contexts ------------ */ +/* ------------ Match context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_callout(pcre2_match_context *mcontext, @@ -351,6 +403,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 +417,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 +431,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 +445,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_dfa_match.c b/ProcessHacker/pcre/pcre2_dfa_match.c index d0f756910bfc..5ae13944c72b 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -71,7 +71,6 @@ 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" @@ -84,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) @@ -173,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 */ @@ -246,7 +245,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 */ @@ -376,14 +375,10 @@ internal_dfa_match( { 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 @@ -401,7 +396,8 @@ BOOL utf = FALSE; BOOL reset_could_continue = FALSE; -if (rlevel++ > mb->match_limit_recursion) return PCRE2_ERROR_RECURSIONLIMIT; +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; @@ -418,21 +414,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; @@ -477,15 +467,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); @@ -698,7 +690,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 @@ -1387,8 +1379,46 @@ for (;;) if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } rgb = UCD_GRAPHBREAK(d); if ((PRIV(ucp_gbtable)[lgb] & (1u << 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 = nptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(d, bptr); + } + else +#endif + d = *bptr; + if (UCD_GRAPHBREAK(d) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + ncount++; - lgb = rgb; nptr += dlen; } count++; @@ -1649,8 +1679,46 @@ for (;;) if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } rgb = UCD_GRAPHBREAK(d); if ((PRIV(ucp_gbtable)[lgb] & (1u << 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 = nptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(d, bptr); + } + else +#endif + d = *bptr; + if (UCD_GRAPHBREAK(d) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + ncount++; - lgb = rgb; nptr += dlen; } ADD_NEW_DATA(-(state_offset + count), 0, ncount); @@ -1920,8 +1988,46 @@ for (;;) if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } rgb = UCD_GRAPHBREAK(d); if ((PRIV(ucp_gbtable)[lgb] & (1u << 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 = nptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(d, bptr); + } + else +#endif + d = *bptr; + if (UCD_GRAPHBREAK(d) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + ncount++; - lgb = rgb; nptr += dlen; } if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) @@ -2110,8 +2216,46 @@ for (;;) if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } rgb = UCD_GRAPHBREAK(d); if ((PRIV(ucp_gbtable)[lgb] & (1u << 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 = nptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(d, bptr); + } + else +#endif + d = *bptr; + if (UCD_GRAPHBREAK(d) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + ncount++; - lgb = rgb; nptr += dlen; } if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) @@ -2137,6 +2281,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); @@ -2540,11 +2685,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 @@ -2890,7 +3037,6 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_ONCE: - case OP_ONCE_NC: { PCRE2_SIZE local_offsets[2]; int local_workspace[1000]; @@ -3070,7 +3216,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. */ @@ -3078,11 +3224,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; } @@ -3155,6 +3303,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. */ @@ -3217,7 +3372,8 @@ if (mcontext == NULL) { mb->callout = NULL; mb->memctl = re->memctl; - mb->match_limit_recursion = PRIV(default_match_context).recursion_limit; + mb->match_limit = PRIV(default_match_context).match_limit; + mb->match_limit_depth = PRIV(default_match_context).depth_limit; } else { @@ -3230,10 +3386,15 @@ else mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; - mb->match_limit_recursion = mcontext->recursion_limit; + mb->match_limit = mcontext->match_limit; + mb->match_limit_depth = mcontext->depth_limit; } -if (mb->match_limit_recursion > re->limit_recursion) - mb->match_limit_recursion = re->limit_recursion; + +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; mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; @@ -3243,6 +3404,7 @@ mb->end_subject = end_subject; mb->start_offset = start_offset; mb->moptions = options; mb->poptions = re->overall_options; +mb->match_call_count = 0; /* Process the \R and newline settings. */ @@ -3260,6 +3422,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; @@ -3326,34 +3493,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) { @@ -3399,8 +3559,8 @@ for (;;) /* 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. */ + we stop the optimization scans for a first code unit at a newline. If the + match fails at the newline, later code breaks this loop. */ if (firstline) { @@ -3420,69 +3580,137 @@ for (;;) 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, 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 (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 >= 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) { - 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++; + } } - } + } /* End of first code unit handling */ /* Restore fudged end_subject */ diff --git a/ProcessHacker/pcre/pcre2_error.c b/ProcessHacker/pcre/pcre2_error.c index 9ef2b9cd0041..d98cae99636a 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-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" @@ -177,6 +176,8 @@ static const unsigned char compile_error_texts[] = "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" ; /* Match-time and UTF error texts are in the same format. */ @@ -245,7 +246,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" @@ -257,6 +258,8 @@ static const unsigned char match_error_texts[] = "match with end before start is not supported\0" "too many replacements (more than INT_MAX)\0" "bad serialized data\0" + "heap limit exceeded\0" + "invalid syntax\0" ; @@ -272,7 +275,7 @@ 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 @@ -305,8 +308,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_find_bracket.c b/ProcessHacker/pcre/pcre2_find_bracket.c index 1564cf96f5ff..357385a11ccc 100644 --- a/ProcessHacker/pcre/pcre2_find_bracket.c +++ b/ProcessHacker/pcre/pcre2_find_bracket.c @@ -46,8 +46,6 @@ 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 diff --git a/ProcessHacker/pcre/pcre2_internal.h b/ProcessHacker/pcre/pcre2_internal.h index 6a8774ce8c1f..9ccce25d4712 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -240,6 +240,16 @@ 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 20K 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 + /* Define the default BSR convention. */ #ifdef BSR_ANYCRLF @@ -547,9 +557,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 @@ -668,7 +683,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' @@ -909,6 +924,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)" @@ -922,7 +938,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" @@ -944,7 +962,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' @@ -1182,6 +1200,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 @@ -1195,7 +1214,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 @@ -1510,68 +1531,67 @@ 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 */ /* These are forced failure and success verbs */ - OP_FAIL, /* 159 */ - OP_ACCEPT, /* 160 */ - OP_ASSERT_ACCEPT, /* 161 Used inside assertions */ - OP_CLOSE, /* 162 Used before OP_ACCEPT to close open captures */ + OP_FAIL, /* 158 */ + OP_ACCEPT, /* 159 */ + OP_ASSERT_ACCEPT, /* 160 Used inside assertions */ + OP_CLOSE, /* 161 Used before OP_ACCEPT to close open captures */ /* This is used to skip a subpattern with a {0} quantifier */ - OP_SKIPZERO, /* 163 */ + OP_SKIPZERO, /* 162 */ /* This is used to identify a DEFINE group during compilation so that it can be checked for having only one branch. It is changed to OP_FALSE before compilation finishes. */ - OP_DEFINE, /* 164 */ + OP_DEFINE, /* 163 */ /* This is not an opcode, but is used to check that tables indexed by opcode are the correct length, in order to catch updating errors - there have been @@ -1618,7 +1638,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", \ @@ -1702,7 +1722,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 */ \ @@ -1774,10 +1793,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)] @@ -1832,8 +1858,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_) @@ -1852,12 +1882,16 @@ 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)[]; +#if PCRE2_CODE_UNIT_WIDTH == 32 +extern const ucd_record PRIV(dummy_ucd_record)[]; +#endif extern const uint8_t PRIV(ucd_stage1)[]; extern const uint16_t PRIV(ucd_stage2)[]; extern const uint32_t PRIV(ucp_gbtable)[]; diff --git a/ProcessHacker/pcre/pcre2_intmodedep.h b/ProcessHacker/pcre/pcre2_intmodedep.h index ebff7e30661c..387f65eb08a9 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-2017 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 @@ -201,20 +202,25 @@ arithmetic results in a signed value. Hence the cast. */ /* Other macros that are different for 8-bit mode. The MAX_255 macro checks whether its argument, which is assumed to be one code unit, 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. */ +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 @@ -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 @@ -611,8 +624,9 @@ typedef struct pcre2_real_code { uint32_t compile_options; /* Options passed to pcre2_compile() */ uint32_t overall_options; /* Options after processing the pattern */ 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 +639,11 @@ 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 large 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 +pairs of elements for each capturing group. (See also the heapframe structure +below.) */ typedef struct pcre2_real_match_data { pcre2_memctl memctl; @@ -638,7 +656,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[10000];/* The first field */ } pcre2_real_match_data; @@ -740,27 +758,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; @@ -768,35 +767,75 @@ 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 */ -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 */ +#else + 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. */ + + 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[10000]; /* Must be last in the structure */ +} heapframe; + +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 */ @@ -807,30 +846,23 @@ 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 */ 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 @@ -845,7 +877,9 @@ 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 */ - uint32_t match_limit_recursion; /* 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 */ diff --git a/ProcessHacker/pcre/pcre2_jit_compile.c b/ProcessHacker/pcre/pcre2_jit_compile.c index 5a02249324b3..c7bf0b2c3e02 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,8 +38,6 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -315,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; @@ -343,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. */ @@ -404,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; @@ -428,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; @@ -437,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; @@ -502,7 +525,7 @@ 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)) #define TMP1 SLJIT_R0 #define TMP2 SLJIT_R2 @@ -572,13 +595,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)); @@ -608,8 +635,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 */ @@ -677,7 +704,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: @@ -808,7 +834,7 @@ switch(*cc) default: /* All opcodes are supported now! */ - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } } @@ -1306,7 +1332,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)) { @@ -1335,7 +1361,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: @@ -1656,11 +1681,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; @@ -1673,7 +1698,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); } @@ -1687,9 +1712,9 @@ 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; @@ -1703,9 +1728,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]; @@ -1716,27 +1741,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; @@ -1750,20 +1775,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; @@ -1778,21 +1803,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) +{ +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 private_data_length = needs_control_head ? 3 : 2; +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); } @@ -1804,26 +1935,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; @@ -1831,13 +1966,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]); @@ -1845,8 +1980,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]); @@ -1854,8 +1989,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]); @@ -1863,20 +1998,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; @@ -1888,11 +2023,51 @@ 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_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); @@ -1900,329 +2075,446 @@ 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); } - if (!tmp2empty) + 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_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; + } + 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) @@ -2339,7 +2631,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)); } @@ -2349,7 +2641,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); @@ -2357,7 +2649,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) @@ -2365,7 +2657,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) @@ -2409,7 +2701,7 @@ else 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); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } } @@ -2447,7 +2739,7 @@ else 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); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); } @@ -2465,22 +2757,22 @@ static sljit_sw SLJIT_CALL 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; } @@ -2520,7 +2812,7 @@ 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); +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. */ @@ -3106,8 +3398,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; @@ -3126,6 +3418,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) @@ -3167,7 +3460,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)); @@ -3181,7 +3474,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)); @@ -3215,15 +3508,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)); @@ -3246,7 +3539,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)); @@ -3283,10 +3576,30 @@ 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_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); @@ -3301,7 +3614,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; @@ -3313,6 +3626,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; @@ -3320,7 +3635,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) { @@ -3377,7 +3692,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); } @@ -3390,8 +3705,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 @@ -3428,8 +3743,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); @@ -3447,40 +3762,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; @@ -3489,7 +3806,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 @@ -3512,6 +3829,7 @@ while (TRUE) { case OP_CHARI: caseless = TRUE; + /* Fall through */ case OP_CHAR: last = FALSE; cc++; @@ -3543,6 +3861,7 @@ while (TRUE) case OP_MINPLUSI: case OP_POSPLUSI: caseless = TRUE; + /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: @@ -3551,6 +3870,7 @@ while (TRUE) case OP_EXACTI: caseless = TRUE; + /* Fall through */ case OP_EXACT: repeat = GET2(cc, 1); last = FALSE; @@ -3561,6 +3881,7 @@ while (TRUE) case OP_MINQUERYI: case OP_POSQUERYI: caseless = TRUE; + /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: @@ -3584,7 +3905,6 @@ while (TRUE) continue; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_BRAPOS: case OP_CBRA: @@ -3705,12 +4025,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); @@ -3754,8 +4074,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; @@ -3770,7 +4090,7 @@ while (TRUE) do { if ((byte & 0x1) != 0) - add_prefix_char(chr, chars); + add_prefix_char(chr, chars, TRUE); byte >>= 1; chr++; } @@ -3778,14 +4098,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); @@ -3849,17 +4169,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++; } @@ -3878,7 +4199,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) { @@ -3897,264 +4248,665 @@ 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); - -/* First part (unaligned start) */ - -OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit)); - -SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1); - -/* MOVD xmm, r/m32 */ -instruction[0] = 0x66; -instruction[1] = 0x0f; -instruction[2] = 0x6e; -instruction[3] = 0xc0 | (2 << 3) | tmp1_ind; -sljit_emit_op_custom(compiler, instruction, 4); - -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; - sljit_emit_op_custom(compiler, instruction, 4); - } - -/* PSHUFD xmm1, xmm2/m128, imm8 */ -instruction[2] = 0x70; -instruction[3] = 0xc0 | (2 << 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; - sljit_emit_op_custom(compiler, instruction, 5); - } +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +sljit_u8 instruction[5]; +#else +sljit_u8 instruction[4]; +#endif -OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 0xf); -OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); +SLJIT_ASSERT(dst_xmm_reg < 8); /* MOVDQA xmm1, xmm2/m128 */ #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - -if (str_ptr_ind < 8) +if (src_general_reg < 8) { + instruction[0] = 0x66; + instruction[1] = 0x0f; instruction[2] = 0x6f; - instruction[3] = (0 << 3) | str_ptr_ind; + instruction[3] = (dst_xmm_reg << 3) | src_general_reg; sljit_emit_op_custom(compiler, instruction, 4); - - if (load_twice) - { - instruction[3] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); - } } else { + instruction[0] = 0x66; instruction[1] = 0x41; instruction[2] = 0x0f; instruction[3] = 0x6f; - instruction[4] = (0 << 3) | (str_ptr_ind & 0x7); - sljit_emit_op_custom(compiler, instruction, 5); - - if (load_twice) - { - instruction[4] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 5); - } - instruction[1] = 0x0f; + 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] = (0 << 3) | str_ptr_ind; +instruction[3] = (dst_xmm_reg << 3) | src_general_reg; sljit_emit_op_custom(compiler, instruction, 4); +#endif +} -if (load_twice) +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) { - instruction[3] = (1 << 3) | str_ptr_ind; + 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); -#endif + /* 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); -if (bit != 0) - { /* POR xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ instruction[2] = 0xeb; - instruction[3] = 0xc0 | (0 << 3) | 3; + instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind; sljit_emit_op_custom(compiler, instruction, 4); } +} -/* 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); +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 tmp2_ind = sljit_get_register_index(TMP2); +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 (load_twice) +if (char1 != char2) { - instruction[3] = 0xc0 | (1 << 3) | 3; - sljit_emit_op_custom(compiler, instruction, 4); + bit = char1 ^ char2; + if (!is_powerof2(bit)) + bit = 0; } -/* PMOVMSKB reg, xmm */ -instruction[2] = 0xd7; -instruction[3] = 0xc0 | (tmp1_ind << 3) | 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 | (cmp1_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); -if (load_twice) +if (char1 != char2) { - OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0); - instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2)); + + /* MOVD xmm, r/m32 */ + instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); + } - OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); - OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0); +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 | (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 | (cmp2_ind << 3) | 3; + sljit_emit_op_custom(compiler, instruction, 5); } -OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0); +#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); + +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); + +/* 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); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_LSHR, 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); - -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); -quit[1] = JUMP(SLJIT_JUMP); +quit = JUMP(SLJIT_NOT_ZERO); -JUMPHERE(nomatch); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 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); + +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]); /* Second part (aligned) */ +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); -/* MOVDQA xmm1, xmm2/m128 */ -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +/* 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(quit); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); -if (str_ptr_ind < 8) +if (common->mode != PCRE2_JIT_COMPLETE) { - instruction[2] = 0x6f; - instruction[3] = (0 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); + 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)); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset > 0) + { + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); + + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); + + quit = jump_if_utf_char_start(compiler, TMP1); + + 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]; + +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) + { + 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)); + + OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, STR_END, 0); + CMOV(SLJIT_LESS, STR_END, TMP1, 0); + } - if (load_twice) +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 @@ -4163,15 +4915,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)); @@ -4179,76 +4932,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); @@ -4257,85 +4955,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; @@ -4346,7 +5015,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); @@ -4354,21 +5026,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; @@ -4388,16 +5089,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); } } @@ -4405,54 +5107,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) + if (chars[i].last_count >= 2) offset = i; } - else if (chars[offset * MAX_DIFF_CHARS] == 2 && chars[i * MAX_DIFF_CHARS] <= 2) - { - if (chars[i * MAX_DIFF_CHARS] == 1) - 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)); @@ -4464,7 +5150,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)); @@ -4485,20 +5171,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); } } @@ -4514,15 +5200,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)); } @@ -4531,33 +5211,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); - } - } + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); 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 @@ -4595,8 +5262,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 @@ -4640,8 +5307,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 @@ -4656,79 +5323,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); @@ -4804,31 +5467,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); } @@ -4861,11 +5543,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); } @@ -4905,11 +5587,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 @@ -4937,15 +5619,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; @@ -4963,7 +5645,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++; @@ -4976,7 +5658,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++; @@ -5088,9 +5770,116 @@ 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++; + } } + +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) @@ -5101,22 +5890,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); } @@ -5127,34 +5916,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); } @@ -5167,22 +5956,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 | 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); } @@ -5207,7 +5996,7 @@ 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)); +OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); @@ -5251,7 +6040,7 @@ OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0); 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)); +OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); @@ -5418,7 +6207,7 @@ do #endif default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } context->ucharptr = 0; @@ -5593,7 +6382,7 @@ while (*cc != XCL_END) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } cc += 2; @@ -5611,13 +6400,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)); } @@ -5638,7 +6427,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; @@ -5650,7 +6439,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 @@ -5669,6 +6458,15 @@ 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_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); @@ -5760,14 +6558,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; } @@ -5786,14 +6584,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; } @@ -5818,12 +6616,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; @@ -5845,33 +6643,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; @@ -5893,8 +6691,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])) @@ -5906,63 +6704,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); @@ -5971,21 +6769,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); @@ -5993,21 +6791,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; @@ -6053,6 +6851,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; @@ -6068,10 +6867,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)); @@ -6093,9 +6892,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); @@ -6114,6 +6913,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); } @@ -6131,8 +6931,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); @@ -6146,8 +6946,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]); @@ -6184,16 +6984,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]); @@ -6231,7 +7031,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 @@ -6244,7 +7044,7 @@ switch(type) check_start_used_ptr(common); return cc + LINK_SIZE; } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); return cc; } @@ -6275,7 +7075,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; @@ -6289,7 +7089,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; @@ -6303,7 +7103,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; @@ -6345,8 +7145,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 @@ -6406,6 +7206,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; @@ -6415,6 +7216,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; @@ -6441,7 +7243,7 @@ switch(type) 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); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); JUMPTO(SLJIT_NOT_ZERO, label); OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); @@ -6584,7 +7386,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 @@ -6611,7 +7413,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 @@ -6628,7 +7430,7 @@ switch(type) return cc + GET(cc, 0) - 1; #endif } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); return cc; } @@ -6814,9 +7616,9 @@ 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); @@ -6907,7 +7709,7 @@ switch(type) cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -6921,7 +7723,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) @@ -6934,7 +7736,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 { @@ -7098,8 +7900,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) @@ -7108,71 +7912,73 @@ 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) { -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; +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 = 1; /* 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) { @@ -7184,10 +7990,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); @@ -7195,11 +8004,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)); @@ -7227,20 +8035,21 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_pt /* 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); +/* 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)); +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); +OP2(SLJIT_SUB | 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_GREATER)); -if (common->forced_quit_label == NULL) - add_jump(compiler, &common->forced_quit, JUMP(SLJIT_SIG_LESS)); +if (common->abort_label == NULL) + add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL) /* SIG_LESS */); else - JUMPTO(SLJIT_SIG_LESS, common->forced_quit_label); + JUMPTO(SLJIT_NOT_EQUAL /* SIG_LESS */, common->abort_label); return cc + callout_length; } @@ -7282,6 +8091,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; @@ -7292,13 +8102,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; @@ -7365,7 +8175,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); @@ -7380,21 +8190,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) { @@ -7410,16 +8220,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; } @@ -7436,23 +8246,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)); } } @@ -7462,25 +8273,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)); @@ -7488,16 +8299,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; } @@ -7510,26 +8321,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); } @@ -7578,18 +8389,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); } } @@ -7598,13 +8409,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)); @@ -7632,7 +8443,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()); } @@ -7685,16 +8498,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; } @@ -7719,23 +8532,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) { @@ -7822,7 +8635,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 */ @@ -7890,8 +8702,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) { @@ -7968,7 +8778,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); } @@ -8041,7 +8851,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) @@ -8059,7 +8869,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) @@ -8074,7 +8884,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) @@ -8131,13 +8941,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); @@ -8290,7 +9100,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) @@ -8320,7 +9130,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) @@ -8348,6 +9158,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); @@ -8420,7 +9231,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -8498,7 +9309,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) @@ -8517,7 +9328,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); } @@ -8570,7 +9381,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); @@ -8581,10 +9392,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. */ @@ -8630,7 +9441,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)); } } @@ -8647,7 +9458,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. */ @@ -8870,7 +9681,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 @@ -8878,7 +9689,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); } } @@ -8908,7 +9719,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); } @@ -8970,7 +9781,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); @@ -8990,7 +9801,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)); } @@ -9017,7 +9828,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 @@ -9046,7 +9857,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 @@ -9072,7 +9883,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)); } @@ -9159,7 +9970,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); @@ -9170,7 +9981,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()); @@ -9188,7 +9999,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9209,6 +10020,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. */ @@ -9225,9 +10039,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 @@ -9311,7 +10125,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); @@ -9320,7 +10134,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) @@ -9542,7 +10356,6 @@ while (cc < ccend) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -9617,7 +10430,7 @@ while (cc < ccend) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return; } if (cc == NULL) @@ -9725,7 +10538,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); @@ -9771,7 +10584,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9806,27 +10619,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) +if (!CURRENT_AS(recurse_backtrack)->inlined_pattern) { - 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) - { - 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) @@ -9879,7 +10686,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()); } @@ -9889,7 +10698,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); @@ -9949,8 +10758,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; @@ -10002,7 +10809,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) @@ -10056,6 +10863,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); } @@ -10108,7 +10916,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()); @@ -10249,7 +11059,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); } @@ -10304,7 +11116,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)); @@ -10385,6 +11197,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) { @@ -10394,7 +11207,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) @@ -10440,22 +11253,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 @@ -10506,7 +11320,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); @@ -10623,7 +11440,6 @@ while (current) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -10672,7 +11488,7 @@ while (current) break; case OP_COMMIT: - if (!common->local_exit) + 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)); @@ -10694,7 +11510,7 @@ while (current) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } current = current->prev; @@ -10709,38 +11525,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)); @@ -10762,7 +11592,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))) @@ -10776,55 +11674,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 @@ -10856,11 +11764,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; @@ -11078,7 +11988,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) @@ -11086,11 +11996,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 @@ -11137,6 +12047,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)); @@ -11149,15 +12062,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(); @@ -11238,9 +12162,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); @@ -11251,7 +12175,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) { @@ -11268,7 +12192,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. */ @@ -11280,7 +12204,7 @@ 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); +OP2(SLJIT_SUB, 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); diff --git a/ProcessHacker/pcre/pcre2_jit_match.c b/ProcessHacker/pcre/pcre2_jit_match.c index a323971ff3ea..4cad754c759e 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.max_limit = local_space; +local_stack.limit = local_space; +local_stack.base = local_space + MACHINE_STACK_SIZE; +local_stack.top = local_space + MACHINE_STACK_SIZE; arguments->stack = &local_stack; return executable_func(arguments); } diff --git a/ProcessHacker/pcre/pcre2_jit_test.c b/ProcessHacker/pcre/pcre2_jit_test.c index 705ba181eb3c..96280b71680f 100644 --- a/ProcessHacker/pcre/pcre2_jit_test.c +++ b/ProcessHacker/pcre/pcre2_jit_test.c @@ -258,6 +258,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 +709,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 +726,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 +846,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,9 +1323,9 @@ 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) { @@ -1348,9 +1362,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) { @@ -1392,9 +1406,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) { diff --git a/ProcessHacker/pcre/pcre2_maketables.c b/ProcessHacker/pcre/pcre2_maketables.c index ddfadfb00d42..2c7ae84d864d 100644 --- a/ProcessHacker/pcre/pcre2_maketables.c +++ b/ProcessHacker/pcre/pcre2_maketables.c @@ -44,8 +44,6 @@ 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" diff --git a/ProcessHacker/pcre/pcre2_match.c b/ProcessHacker/pcre/pcre2_match.c index e05dc35179eb..689169e837d1 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,23 +38,39 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H +// dmex: Disable warnings. +#pragma warning(push) +#pragma warning(disable : 4244) #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 enables 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) @@ -62,60 +78,252 @@ 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 }; + +#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. + +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; + +cb.version = 1; +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.subject = mb->start_subject; +cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject); +cb.start_match = (PCRE2_SIZE)(Fstart_match - mb->start_subject); +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; +return rc; +} @@ -131,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 @@ -143,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, 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 - 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) { @@ -169,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). */ @@ -227,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; } } @@ -244,278 +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 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 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(PCRE2_SPTR eptr, 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(PCRE2_SPTR eptr, PCRE2_SPTR callpat, - PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, - uint32_t rdepth) -{ -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;\ + } @@ -523,2022 +535,1713 @@ 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(PCRE2_SPTR eptr, 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. */ - -int rrc; /* Returns from recursive calls */ -int i; /* Used for loops not involving calls to RMATCH() */ -uint32_t c; /* Character values not kept over RMATCH() calls */ -BOOL utf; /* Local copy of UTF flag for speed */ - -BOOL minimize, possessive; /* Quantifier options */ -int condcode; +/* Frame-handling variables */ -/* 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". */ +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 */ -#ifdef HEAP_MATCH_RECURSE -heapframe *frame = (heapframe *)mb->match_frames_base; +/* Local variables that do not need to be preserved over calls to RRMATCH(). */ -/* Copy in the original argument variables */ +PCRE2_SPTR bracode; /* Temp pointer to start of group */ +PCRE2_SIZE offset; /* Used for group offsets */ +PCRE2_SIZE length; /* Used for various length calculations */ -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 caseless 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); + +N->rdepth = Frdepth + 1; +F = N; -if (mb->match_call_count++ >= mb->match_limit) RRETURN(PCRE2_ERROR_MATCHLIMIT); -if (rdepth >= mb->match_limit_recursion) RRETURN(PCRE2_ERROR_RECURSIONLIMIT); +/* Carry on processing with a new frame. */ -/* 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. +NEW_FRAME: +Fgroup_frame_type = group_frame_type; +Fecode = start_ecode; /* Starting code pointer */ +Fback_frame = frame_size; /* Default is go back one 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(). */ +/* 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 ((mb->match_function_type & MATCH_CBEGROUP) != 0) +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 = *Fecode; + 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; + /* ===================================================================== */ + /* 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. */ - /* 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. */ - - 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 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; - } - /* 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. */ + /*===================================================================== */ + /* Match any single character type except newline; have to take care with + CRLF newlines and partial matching. */ - 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 = (uint32_t)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) - { - PCRE2_SIZE *iptr = mb->ovector + offset_top; - 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; + + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATNOTCHAR; - /* Otherwise, we have a match. */ + 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 (i > eptr - mb->start_subject) RRETURN(MATCH_NOMATCH); - eptr -= i; - } - - /* Save the earliest consulted character, then skip to next op code */ - if (eptr < mb->start_used_ptr) mb->start_used_ptr = eptr; - ecode += 1 + LINK_SIZE; - break; - - /* 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: - { - unsigned int callout_length = (*ecode == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE); + if (Lmin == Lmax) continue; - if (mb->callout != NULL) + if (reptype == REPTYPE_MIN) { - pcre2_callout_block cb; - cb.version = 1; - cb.callout_number = ecode[LINK_SIZE + 1]; - cb.capture_top = (uint32_t)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) +#ifdef SUPPORT_UNICODE + if (utf) { - cb.callout_number = ecode[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; + 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 */ { - 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 (;;) + { + 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); + } } - - if ((rrc = mb->callout(&cb, mb->callout_data)) > 0) - RRETURN(MATCH_NOMATCH); - if (rrc < 0) RRETURN(rrc); + /* Control never gets here */ } - 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; - - 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); - - /* Add to "recursing stack" */ - - 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; - - /* Where to continue from afterwards */ - - ecode += 1 + LINK_SIZE; - - /* 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. */ - -#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 */ - } -#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. */ + /* Maximize case */ - if (mb->ovecsave_chain != NULL) - { - new_recursive.ovec_save = mb->ovecsave_chain->saved_ovec; - mb->ovecsave_chain = mb->ovecsave_chain->next; - } else { - 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; - } - - memcpy(new_recursive.ovec_save, mb->ovector, - mb->offset_end * sizeof(PCRE2_SIZE)); - - /* 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. */ + Lstart_eptr = Feptr; - cbegroup = (*callpat >= OP_SBRA); - do - { - 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 */ - } + 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; + } - /* 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. */ + /* 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 (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) + 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 */ { - rrc = MATCH_NOMATCH; - goto RECURSION_RETURN; + 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--; + } } - - /* Any return code other than NOMATCH is an error. */ - - 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); } - - RECURSION_MATCHED: - break; - - /* An alternation is the end of a branch; scan along to find the end of the - bracketed group and go to there. */ - - case OP_ALT: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - break; - - /* 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. */ - - 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; - - 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; +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lc +#undef Loc - /* 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); + /* ===================================================================== */ + /* Match a bit-mapped character class, possibly repeatedly. These op codes + 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. */ - /* 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. */ +#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) - if (*prev >= OP_SBRA || *prev == OP_ONCE) + case OP_NCLASS: + case OP_CLASS: { - saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ - eptrb = eptrb->epb_prev; /* Backup to previous group */ - } - else saved_eptr = NULL; + Lbyte_map_address = Fecode + 1; /* Save for matching */ + Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ - /* 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. */ + /* Look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats. */ - 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 */ - } + 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; - /* 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. */ + 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; - if (*prev == OP_CBRA || *prev == OP_SCBRA || - *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS) - { - number = GET2(prev, 1+LINK_SIZE); - offset = number << 1; + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; + } - /* Handle a recursively called group. */ + /* First, ensure the minimum number of matches are present. */ - if (mb->recursive != NULL && mb->recursive->group_num == number) +#ifdef SUPPORT_UNICODE + if (utf) { - mb->end_match_ptr = eptr; - mb->start_match_ptr = mstart; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_MATCH); + 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); + } } - - /* Deal with capturing */ - - mb->capture_last = (mb->capture_last & OVFLMASK) | number; - if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else + else +#endif + /* Not UTF mode */ { - /* 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) + for (i = 1; i <= Lmin; i++) { - PCRE2_SIZE *iptr = mb->ovector + offset_top; - PCRE2_SIZE *iend = mb->ovector + offset; - while (iptr < iend) *iptr++ = PCRE2_UNSET; + 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); } - - /* 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 Lmax == Lmin we are done. Continue with main loop. */ - 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; - } + if (Lmin == Lmax) continue; - /* 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 minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ - 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) + if (reptype == REPTYPE_MIN) { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM9); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; - RRETURN(MATCH_ONCE); - } - 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 */ - - 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. */ +#ifdef SUPPORT_UNICODE + if (utf) + { + 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); + } + } + else +#endif + /* Not UTF mode */ + { + 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 */ + } - 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; + /* If maximizing, find the longest possible run, then work backwards. */ - /* Start of match assertion */ + else + { + Lstart_eptr = Feptr; - case OP_SOM: - if (eptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); - ecode++; - 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; + } - /* Reset the start of match point */ + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - case OP_SET_SOM: - mstart = eptr; - ecode++; - break; + 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++; + } - /* Multiline mode: assert before any newline, or before end of subject - unless noteol is set. */ + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - case OP_DOLLM: - if (eptr < mb->end_subject) - { - if (!IS_NEWLINE(eptr)) - { - 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); + while (Feptr >= Lstart_eptr) + { + RMATCH(Fecode, RM24); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } } + 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; + /* Control never gets here */ - /* ... else fall through for endonly */ +#undef Lbyte_map_address +#undef Lbyte_map +#undef Lstart_eptr +#undef Lmin +#undef Lmax - /* End of subject assertion (\z) */ - case OP_EOD: - if (eptr < mb->end_subject) RRETURN(MATCH_NOMATCH); - SCHECK_PARTIAL(); - ecode++; - break; + /* ===================================================================== */ + /* 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. */ - /* End of subject or ending \n assertion (\Z) */ +#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] - case OP_EODN: - ASSERT_NL_OR_EOS: - if (eptr < mb->end_subject && - (!IS_NEWLINE(eptr) || eptr != mb->end_subject - mb->nllen)) +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: { - 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); - } - RRETURN(MATCH_NOMATCH); - } - - /* Either at end of string or \n before end. */ + Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */ + Fecode += GET(Fecode, 1); /* Advance past the item */ - SCHECK_PARTIAL(); - 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; - /* Word boundary assertions */ + 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_NOT_WORD_BOUNDARY: - case OP_WORD_BOUNDARY: - { + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; + } - /* 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. */ + /* First, ensure the minimum number of matches are present. */ -#ifdef SUPPORT_UNICODE - if (utf) + for (i = 1; i <= Lmin; i++) { - /* Get status of previous character */ - - if (eptr == mb->start_subject) prev_is_word = FALSE; else + if (Feptr >= mb->end_subject) { - 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; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + GETCHARINCTEST(fc, Feptr); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); + } - /* Get status of next character */ + /* If Lmax == Lmin we can just continue with the main loop. */ - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - cur_is_word = FALSE; - } - else + if (Lmin == Lmax) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (reptype == REPTYPE_MIN) + { + 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 + 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 == mb->end_subject - 1 && - 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: @@ -2554,110 +2257,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; @@ -2665,7 +2371,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; @@ -2675,16 +2381,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; @@ -2692,43 +2398,45 @@ 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); @@ -2736,1606 +2444,1032 @@ for (;;) else { int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) + GETCHARINCTEST(fc, Feptr); + lgb = UCD_GRAPHBREAK(fc); + while (Feptr < mb->end_subject) { int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); + if (!utf) fc = *Feptr; else { GETCHARLEN(fc, Feptr, len); } + rgb = UCD_GRAPHBREAK(fc); if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - ecode++; - break; -#endif /* SUPPORT_UNICODE */ + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ - /* Match a back reference, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + int ricount = 0; + PCRE2_SPTR bptr = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ - 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. */ + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(fc, bptr); + } + else +#endif + fc = *bptr; + if (UCD_GRAPHBREAK(fc) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } - 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; + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ - /* Initializing 'offset' avoids a compiler warning in the REF_REPEAT - code. */ + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; - 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; + Feptr += len; } } - goto REF_REPEAT; + CHECK_PARTIAL(); + Fecode++; + break; - case OP_REF: - case OP_REFI: - caseless = op == OP_REFI; - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - ecode += 1 + IMM2_SIZE; +#endif /* SUPPORT_UNICODE */ - /* Set up for repetition, or handle the non-repeated case */ - 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; + /* ===================================================================== */ + /* Match a single character type repeatedly. Note that the property type + does not need to be in a stack frame as it not used within an RMATCH() + loop. */ - 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; +#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] - 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 */ - } + case OP_TYPEEXACT: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; - /* 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. */ + 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; - if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) - { - if (mb->ovector[offset] == mb->ovector[offset + 1]) continue; - } - else /* Group is not set */ - { - if (min == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) - continue; - } + case OP_TYPEPOSSTAR: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; - /* First, ensure the minimum number of matches are present. */ + case OP_TYPEPOSPLUS: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; - for (i = 1; i <= min; i++) - { - PCRE2_SIZE slength; - int 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; - } + case OP_TYPEPOSQUERY: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATTYPE; + + case OP_TYPEPOSUPTO: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ + 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]; - if (min == max) continue; + /* Common code for all repeated character type matches. */ - /* If minimizing, keep trying and advancing the pointer */ + REPEATTYPE: + Lctype = *Fecode++; /* Code for the character type */ - if (minimize) +#ifdef SUPPORT_UNICODE + if (Lctype == OP_PROP || Lctype == OP_NOTPROP) { - 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 */ + proptype = *Fecode++; + Lpropvalue = *Fecode++; } + else proptype = -1; +#endif - /* If maximizing, find the longest string and work backwards, as long as - the matched lengths for each iteration are the same. */ + /* 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. */ - else + if (Lmin > 0) { - BOOL samelengths = TRUE; - pp = eptr; - length = mb->ovector[offset+1] - mb->ovector[offset]; - - for (i = min; i < max; i++) +#ifdef SUPPORT_UNICODE + if (proptype >= 0) /* Property tests in all modes */ { - PCRE2_SIZE slength; - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - - if (rc != 0) + switch(proptype) { - /* Can't use CHECK_PARTIAL because we don't want to update eptr in - the soft partial matching case. */ - - if (rc > 0 && mb->partial != 0 && - mb->end_subject > mb->start_used_ptr) + case PT_ANY: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= Lmin; i++) { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); } break; - } - - if (slength != length) samelengths = FALSE; - eptr += slength; - } - - /* 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. */ - - if (samelengths) - { - while (eptr >= pp) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM15); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr -= length; - } - } - - /* The rare case of non-matching lengths. Re-scan the repetition for each - iteration. We know that match_ref() will succeed every time. */ - else - { - max = i; - for (;;) - { - 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++) + case PT_LAMP: + for (i = 1; i <= Lmin; i++) { - PCRE2_SIZE slength; - (void)match_ref(offset, offset_top, eptr, mb, caseless, &slength); - eptr += slength; + 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); } - } - } - - 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 */ - - 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; - break; - - default: /* No repeat follows */ - min = max = 1; - break; - } - - /* First, ensure the minimum number of matches are present. */ + break; -#ifdef SUPPORT_UNICODE - if (utf) - { - for (i = 1; i <= min; i++) - { - 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); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) + case PT_GC: + for (i = 1; i <= Lmin; i++) { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - 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) - { - for (fi = min;; fi++) + case PT_PC: + for (i = 1; i <= Lmin; i++) { - 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) + if (Feptr >= 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); + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) + break; + + case PT_SC: + for (i = 1; i <= Lmin; i++) { - 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) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_ALNUM: + for (i = 1; i <= Lmin; i++) + { + int category; + if (Feptr >= mb->end_subject) { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) 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 */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ + break; - else - { - pp = eptr; + /* 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. */ -#ifdef SUPPORT_UNICODE - if (utf) - { - for (i = min; i < max; i++) + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + for (i = 1; i <= Lmin; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - GETCHARLEN(c, eptr, len); - if (c > 255) + GETCHARINCTEST(fc, Feptr); + switch(fc) { - if (op == OP_CLASS) break; + HSPACE_CASES: + VSPACE_CASES: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + default: + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr += len; } + break; - if (possessive) continue; /* No backtracking */ - - for (;;) + case PT_WORD: + for (i = 1; i <= Lmin; i++) { - 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); + 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); } - } - else -#endif - /* Not UTF mode */ - { - for (i = min; i < max; i++) + break; + + case PT_CLIST: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + const uint32_t *cp; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - c = *eptr; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) + GETCHARINCTEST(fc, Feptr); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; + for (;;) { - if (op == OP_CLASS) break; + if (fc < *cp) + { + if (Lctype == OP_NOTPROP) break; + RRETURN(MATCH_NOMATCH); + } + if (fc == *cp++) + { + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + } } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr++; } + break; - if (possessive) continue; /* No backtracking */ - - while (eptr >= pp) + case PT_UCNC: + for (i = 1; i <= Lmin; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM19); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; + 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; - RRETURN(MATCH_NOMATCH); + /* This should not occur */ + + default: + return PCRE2_ERROR_INTERNAL; + } } -#undef BYTE_MAP - } - /* Control never gets here */ + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ - /* 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. */ + else if (Lctype == OP_EXTUNI) + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { + int lgb, rgb; + GETCHARINCTEST(fc, Feptr); + lgb = UCD_GRAPHBREAK(fc); + while (Feptr < mb->end_subject) + { + int len = 1; + if (!utf) fc = *Feptr; else { GETCHARLEN(fc, Feptr, len); } + rgb = UCD_GRAPHBREAK(fc); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - { - data = ecode + 1 + LINK_SIZE; /* Save for matching */ - ecode += GET(ecode, 1); /* Advance past the item */ + /* Not breaking between Regional Indicators is allowed only if + there are an even number of preceding RIs. */ - 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; + if (lgb == ucp_gbRegionalIndicator && + rgb == ucp_gbRegionalIndicator) + { + int ricount = 0; + PCRE2_SPTR bptr = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ - 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; - break; + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(fc, bptr); + } + else +#endif + fc = *bptr; + if (UCD_GRAPHBREAK(fc) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } - default: /* No repeat follows */ - min = max = 1; - break; - } + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ - /* First, ensure the minimum number of matches are present. */ + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + Feptr += len; + } + } + CHECK_PARTIAL(); } - 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; + else +#endif /* SUPPORT_UNICODE */ - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ +/* Handle all other cases in UTF mode */ - if (minimize) +#ifdef SUPPORT_UNICODE + if (utf) switch(Lctype) { - for (fi = min;; fi++) + case OP_ANY: + 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) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if (!PRIV(xclass)(c, data, utf)) 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++); } - /* 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_ALLANY: + for (i = 1; i <= Lmin; i++) { - int len = 1; - if (eptr >= mb->end_subject) + 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; + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, *Feptr, Feptr++); } + break; - if (possessive) continue; /* No backtracking */ + case OP_ANYBYTE: + if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH); + Feptr += Lmin; + break; - for(;;) + case OP_ANYNL: + 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); - } + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); - /* Control never gets here */ - } -#endif /* End of XCLASS */ + case CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; - /* Match a single character, casefully */ + case CHAR_LF: + break; - 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; + 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; + } + } + break; - /* Match a single character, caselessly. If we are at the end of the - subject, give up immediately. */ + case OP_NOT_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + } + break; - case OP_CHARI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + } + break; -#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) + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) { -#ifdef SUPPORT_UNICODE - if (dc != UCD_OTHERCASE(fc)) -#endif + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } } - } - } - 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. - - The various UTF/non-UTF and caseful/caseless cases are handled separately, - for speed. */ - - REPEATCHAR: -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - charptr = ecode; - GETCHARLEN(fc, ecode, length); - ecode += length; - - /* Handle multibyte character matching specially here. There is - support for caseless matching if UCP support is present. */ - - if (length > 1) - { - uint32_t othercase; - if (op >= OP_STARI && /* Caseless */ - (othercase = UCD_OTHERCASE(fc)) != fc) - oclength = PRIV(ord2utf)(othercase, occhars); - else oclength = 0; + break; - for (i = 1; i <= min; i++) + case OP_VSPACE: + 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); } + GETCHARINC(fc, Feptr); + switch(fc) + { + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } } + break; - if (min == max) continue; - - if (minimize) + case OP_NOT_DIGIT: + for (i = 1; i <= Lmin; i++) { - for (fi = min;; fi++) + if (Feptr >= mb->end_subject) { - 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); - } + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - /* Control never gets here */ + GETCHARINC(fc, Feptr); + if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); } + break; - else /* Maximize */ + case OP_DIGIT: + 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 (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(;;) + uint32_t cc; + if (Feptr >= mb->end_subject) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM23); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); + 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. */ } - /* 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) + 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++); } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; - } - if (min == max) continue; - if (minimize) - { - for (fi = min;; fi++) + break; + + case OP_WHITESPACE: + 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) + uint32_t cc; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; + 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 */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - if (eptr >= mb->end_subject) + uint32_t cc; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) break; - eptr++; + cc = UCHAR21(Feptr); + if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, *Feptr, Feptr++); } - if (possessive) continue; /* No backtracking */ - for (;;) + break; + + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM25); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); + 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. */ } - /* Control never gets here */ - } - } + break; - /* Caseful comparisons (includes all multi-byte characters) */ + default: + return PCRE2_ERROR_INTERNAL; + } /* End switch(Lctype) */ - else - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH); - } + else +#endif /* SUPPORT_UNICODE */ - if (min == max) continue; + /* Code for the non-UTF case for minimum matching of operators other + than OP_PROP and OP_NOTPROP. */ - if (minimize) + switch(Lctype) { - for (fi = min;; fi++) + case OP_ANY: + 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); - } - /* Control never gets here */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) + 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]) { - SCHECK_PARTIAL(); - break; + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } - if (fc != UCHAR21TEST(eptr)) break; - eptr++; + Feptr++; } - if (possessive) continue; /* No backtracking */ - for (;;) + break; + + case OP_ALLANY: + if (Feptr > mb->end_subject - Lmin) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM27); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - /* Control never gets here */ - } - } - /* Control never gets here */ - - /* Match a negated single one-byte character. The character we are - checking can be multibyte. */ + Feptr += Lmin; + break; - case OP_NOT: - case OP_NOTI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } -#ifdef SUPPORT_UNICODE - if (utf) - { - 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 */ - { - 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. */ + /* 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++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); - REPEATNOTCHAR: - GETCHARINCTEST(fc, ecode); + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + 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 CHAR_LF: + 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 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; -#ifdef SUPPORT_UNICODE - if (utf) - { - uint32_t d; - for (i = 1; i <= min; i++) + 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); } - GETCHARINC(d, eptr); - if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) + switch(*Feptr++) { - SCHECK_PARTIAL(); + default: break; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif RRETURN(MATCH_NOMATCH); } - if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; } - } - - if (min == max) continue; + break; - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) { - uint32_t d; - for (fi = min;; fi++) + if (Feptr >= mb->end_subject) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM28); - 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 || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - } - else -#endif /*SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (fi = min;; fi++) + switch(*Feptr++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM29); - 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 || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + break; } } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; + break; -#ifdef SUPPORT_UNICODE - if (utf) + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) { - uint32_t d; - for (i = min; i < max; i++) + if (Feptr >= mb->end_subject) { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(d, eptr, len); - if (fc == d || (uint32_t)foc == d) break; - eptr += len; + SCHECK_PARTIAL(); + 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. */ - - for(;;) + switch(*Feptr++) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM30); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + default: break; } } - else -#endif /* SUPPORT_UNICODE */ - /* 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; - } - if (fc == *eptr || foc == *eptr) break; - eptr++; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - if (possessive) continue; /* No backtracking */ - for (;;) + switch(*Feptr++) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM31); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + break; } } - /* Control never gets here */ - } - } - - /* Caseful comparisons */ + break; - else - { -#ifdef SUPPORT_UNICODE - if (utf) - { - uint32_t d; - for (i = 1; i <= min; i++) + case OP_NOT_DIGIT: + 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) RRETURN(MATCH_NOMATCH); + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) + break; + + case OP_DIGIT: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (fc == *eptr++) RRETURN(MATCH_NOMATCH); + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - } - - if (min == max) continue; + break; - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) + case OP_NOT_WHITESPACE: + for (i = 1; i <= Lmin; i++) { - uint32_t d; - for (fi = min;; fi++) + if (Feptr >= mb->end_subject) { - 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); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - else -#endif - /* Not UTF mode */ + break; + + case OP_WHITESPACE: + for (i = 1; i <= Lmin; i++) { - for (fi = min;; fi++) + if (Feptr >= mb->end_subject) { - 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); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; + break; -#ifdef SUPPORT_UNICODE - if (utf) + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - 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 (Feptr >= mb->end_subject) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM34); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - else -#endif - /* Not UTF mode */ + break; + + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - 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 (Feptr >= mb->end_subject) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM35); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - /* Control never gets here */ + break; + + default: + return PCRE2_ERROR_INTERNAL; } } - /* 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 */ + /* If Lmin = Lmax we are done. Continue with the main loop. */ -#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 + if (Lmin == Lmax) continue; - /* 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 minimizing, we have to test the rest of the pattern before each + subsequent match. */ - if (min > 0) + if (reptype == REPTYPE_MIN) { #ifdef SUPPORT_UNICODE - if (prop_type >= 0) + if (proptype >= 0) { - switch(prop_type) + switch(proptype) { case PT_ANY: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM208); + 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); + GETCHARINCTEST(fc, Feptr); + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_LAMP: - for (i = 1; i <= min; i++) + for (;;) { int chartype; - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM209); + 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); - chartype = UCD_CHARTYPE(c); + GETCHARINCTEST(fc, Feptr); + chartype = UCD_CHARTYPE(fc); if ((chartype == ucp_Lu || chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_GC: - for (i = 1; i <= min; i++) + for (;;) { - 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(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_PC: - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM211); + 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 ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_SC: - for (i = 1; i <= min; i++) + 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(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_ALNUM: - for (i = 1; i <= min; i++) + for (;;) { int category; - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM213); + 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) == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == + (Lctype == OP_NOTPROP)) 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 @@ -4343,98 +3477,119 @@ 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); @@ -4442,645 +3597,449 @@ for (;;) else { int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) + GETCHARINCTEST(fc, Feptr); + lgb = UCD_GRAPHBREAK(fc); + while (Feptr < mb->end_subject) { int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); + if (!utf) fc = *Feptr; else { GETCHARLEN(fc, Feptr, len); } + rgb = UCD_GRAPHBREAK(fc); if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; + + /* 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 = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(fc, bptr); + } + else +#endif + fc = *bptr; + if (UCD_GRAPHBREAK(fc) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + + Feptr += len; } } 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_ALLANY: + case OP_ANYBYTE: + break; - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) RRETURN(MATCH_NOMATCH); - eptr += min; - break; + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); - 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 CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; - case CHAR_CR: - if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++; + 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; + } break; - case CHAR_LF: + case OP_NOT_HSPACE: + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } 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); + case OP_HSPACE: + switch(fc) + { + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } 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_VSPACE: + switch(fc) + { + VSPACE_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_VSPACE: + switch(fc) + { + VSPACE_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_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_NOT_DIGIT: + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) + 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_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_NOT_WHITESPACE: + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 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_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_NOT_WORDCHAR: + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 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_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); + 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]) + if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) + RRETURN(MATCH_NOMATCH); + fc = *Feptr++; + switch(Lctype) { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - 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: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; + case OP_ALLANY: + case OP_ANYBYTE: + break; - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); - case OP_ANYNL: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + break; - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; - break; + case CHAR_LF: + break; - case CHAR_LF: + 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 CHAR_VT: - case CHAR_FF: - case CHAR_NEL: + case OP_NOT_HSPACE: + switch(fc) + { + default: break; + HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - case 0x2028: - case 0x2029: + HSPACE_MULTIBYTE_CASES: #endif - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + RRETURN(MATCH_NOMATCH); + } break; - } - } - break; - 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 OP_HSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: + HSPACE_MULTIBYTE_CASES: #endif - RRETURN(MATCH_NOMATCH); - } - } - break; + 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_VSPACE: + switch(fc) + { + default: break; + VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: + VSPACE_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_VSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - 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); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: + VSPACE_MULTIBYTE_CASES: #endif + break; + } 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_NOT_DIGIT: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + 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_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_NOT_WHITESPACE: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 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_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_NOT_WORDCHAR: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 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_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 @@ -5088,1326 +4047,2098 @@ 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 <= pp 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); + lgb = UCD_GRAPHBREAK(fc); + while (Feptr < mb->end_subject) { int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); + if (!utf) fc = *Feptr; else { GETCHARLEN(fc, Feptr, len); } + rgb = UCD_GRAPHBREAK(fc); if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; + + /* 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 = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(bptr); +#endif + /* bptr is pointing to the left-hand character */ + + while (bptr > mb->start_subject) + { + bptr--; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(bptr); + GETCHAR(fc, bptr); + } + else +#endif + fc = *bptr; + if (UCD_GRAPHBREAK(fc) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend follows E_Base[_GAZ] do not update lgb; this allows + any number of Extend before a following E_Modifier. */ + + if (rgb != ucp_gbExtend || + (lgb != ucp_gbE_Base && lgb != ucp_gbE_Base_GAZ)) + lgb = rgb; + + Feptr += len; } } 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 + { + BACKCHAR(fptr); + GETCHAR(fc, fptr); + } + lgb = UCD_GRAPHBREAK(fc); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + Feptr = fptr; + rgb = lgb; + } + } } + 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; - - 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; - } - break; - - case OP_NOT_HSPACE: - switch(c) - { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - break; - - case OP_HSPACE: - switch(c) - { - HSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - break; - - case OP_NOT_VSPACE: - switch(c) - { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - break; - - case OP_VSPACE: - switch(c) - { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, *Feptr, Feptr++); } - break; - - case OP_NOT_DIGIT: - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_DIGIT: - if (c >= 256 || (mb->ctypes[c] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WHITESPACE: - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_WHITESPACE: - if (c >= 256 || (mb->ctypes[c] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WORDCHAR: - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_WORDCHAR: - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); } - } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - 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) + else { + Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */ SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); } - if (ctype == OP_ANY && IS_NEWLINE(eptr)) - RRETURN(MATCH_NOMATCH); - c = *eptr++; - switch(ctype) - { - case OP_ANY: /* This is the non-NL case */ - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - c == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - break; - - case OP_ALLANY: - case OP_ANYBYTE: - break; - - case OP_ANYNL: - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; + 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; + /* The "byte" (i.e. "code unit") case is the same as non-UTF */ - case OP_NOT_HSPACE: - switch(c) - { - default: break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); - } - break; + 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_HSPACE: - switch(c) + case OP_ANYNL: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) { - default: RRETURN(MATCH_NOMATCH); - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif + SCHECK_PARTIAL(); break; } - break; - - case OP_NOT_VSPACE: - switch(c) + GETCHARLEN(fc, Feptr, len); + if (fc == CHAR_CR) { - default: break; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); + if (++Feptr >= mb->end_subject) break; + if (UCHAR21(Feptr) == CHAR_LF) Feptr++; } - break; - - case OP_VSPACE: - switch(c) + else { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - break; + if (fc != CHAR_LF && + (mb->bsr_convention == PCRE2_BSR_ANYCRLF || + (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL +#ifndef EBCDIC + && fc != 0x2028 && fc != 0x2029 +#endif /* Not EBCDIC */ + ))) + break; + Feptr += len; } - 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 */ + break; -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - for (i = min; i < max; i++) + case OP_NOT_HSPACE: + case OP_HSPACE: + for (i = Lmin; i < Lmax; i++) { + BOOL gotspace; 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; + 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 PT_LAMP: - for (i = min; i < max; i++) + case OP_NOT_VSPACE: + case OP_VSPACE: + for (i = Lmin; i < Lmax; i++) { - int chartype; + BOOL gotspace; int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); 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; + 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 PT_GC: - 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_CATEGORY(c) == prop_value) == prop_fail_result) break; - eptr+= len; + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break; + Feptr+= len; } break; - case PT_PC: - for (i = min; i < max; i++) + case OP_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_CHARTYPE(c) == prop_value) == prop_fail_result) break; - eptr+= len; + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break; + Feptr+= len; } break; - case PT_SC: - 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); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break; - eptr+= len; + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break; + Feptr+= len; } break; - case PT_ALNUM: - 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) == prop_fail_result) - break; - eptr+= len; + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break; + Feptr+= len; } 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_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); - 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 */ - break; - } - eptr+= len; + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break; + Feptr+= len; } - ENDLOOP99: break; - case PT_WORD: - for (i = min; i < max; i++) + case OP_WORDCHAR: + 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) - break; - eptr+= len; - } - break; - - case PT_CLIST: - for (i = min; i < max; i++) - { - const uint32_t *cp; - int len = 1; - if (eptr >= 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; - } - GOT_MAX: - break; - - case PT_UCNC: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= 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) - break; - eptr += len; + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break; + Feptr+= len; } break; default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - /* eptr is now past the end of the maximum run */ - - 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, RM44); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - if (utf) BACKCHAR(eptr); - } - } - - /* 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) - { - 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(); + 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 */ - - /* 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. */ + /* 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(;;) { - int lgb, rgb; - PCRE2_SPTR fptr; - - if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */ - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM45); + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM221); 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. */ - - eptr--; - if (!utf) c = *eptr; else - { - BACKCHAR(eptr); - GETCHAR(c, eptr); - } - rgb = UCD_GRAPHBREAK(c); - - 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; - } + Feptr--; + BACKCHAR(Feptr); + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && + UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR) + Feptr--; } } - else -#endif /* SUPPORT_UNICODE */ +#endif /* SUPPORT_UNICODE */ -#ifdef SUPPORT_UNICODE - if (utf) + /* Not UTF mode */ { - switch(ctype) + switch(Lctype) { case OP_ANY: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - if (IS_NEWLINE(eptr)) break; + if (IS_NEWLINE(Feptr)) break; if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr + 1 >= mb->end_subject && + Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && - UCHAR21(eptr) == 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; } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); + Feptr++; } break; 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; - - /* The byte case is the same as non-UTF8 */ - case OP_ANYBYTE: - c = max - min; - if (c > (uint32_t)(mb->end_subject - eptr)) + fc = Lmax - Lmin; + if (fc > (uint32_t)(mb->end_subject - Feptr)) { - eptr = mb->end_subject; + Feptr = mb->end_subject; SCHECK_PARTIAL(); } - else eptr += c; + else Feptr += fc; break; case OP_ANYNL: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c == CHAR_CR) + fc = *Feptr; + if (fc == CHAR_CR) { - if (++eptr >= mb->end_subject) break; - if (UCHAR21(eptr) == CHAR_LF) eptr++; + if (++Feptr >= mb->end_subject) break; + if (*Feptr == CHAR_LF) Feptr++; } 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; + 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 + && fc != 0x2028 && fc != 0x2029 +#endif + ))) break; + Feptr++; } } break; case OP_NOT_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: Feptr++; 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++) + for (i = Lmin; i < Lmax; i++) { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - switch(c) + switch(*Feptr) { - HSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; + default: goto ENDLOOP01; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; } - if (gotspace == (ctype == OP_NOT_HSPACE)) break; - eptr += len; } + ENDLOOP01: break; case OP_NOT_VSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: Feptr++; break; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP02; + } + } + ENDLOOP02: + break; + case OP_VSPACE: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - switch(c) + switch(*Feptr) { - VSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; + default: goto ENDLOOP03; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; } - if (gotspace == (ctype == OP_NOT_VSPACE)) break; - eptr += len; } + ENDLOOP03: break; case OP_NOT_DIGIT: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) break; - eptr+= len; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + break; + Feptr++; } break; case OP_DIGIT: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_digit) == 0) break; - eptr+= len; + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) + break; + Feptr++; } break; case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) break; - eptr+= len; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) + break; + Feptr++; } break; case OP_WHITESPACE: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_space) == 0) break; - eptr+= len; + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) + break; + Feptr++; } break; case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) break; - eptr+= len; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + break; + Feptr++; } break; case OP_WORDCHAR: - for (i = min; i < max; i++) + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLEN(c, eptr, len); - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) break; - eptr+= len; + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) + break; + Feptr++; } break; default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; } - if (possessive) continue; /* No backtracking */ + if (reptype == REPTYPE_POS) 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, RM46); + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM34); 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--; + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF && + Feptr[-1] == CHAR_CR) Feptr--; } } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - switch(ctype) - { - 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 && - *eptr == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - } - break; + } + break; /* End of repeat character type processing */ - 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; +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lctype +#undef Lpropvalue - 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; - 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; + /* ===================================================================== */ + /* 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 - 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_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; - 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; + 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; - 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; + case OP_REF: + case OP_REFI: + Lcaseless = (Fop == OP_REFI); + Loffset = (GET2(Fecode, 1) << 1) - 2; + Fecode += 1 + IMM2_SIZE; - case OP_NOT_DIGIT: - for (i = min; i < max; i++) + /* 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. */ + + 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; + + 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; + + default: /* No repeat follows */ + { + rrc = match_ref(Loffset, Lcaseless, F, mb, &length); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + Feptr += length; + continue; /* With the main loop */ + } + + /* 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. */ + + 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; + } + + /* First, ensure the minimum number of matches are present. */ + + 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; + } + + /* If min = max, we are done. They are not both allowed to be zero. */ + + if (Lmin == Lmax) continue; + + /* If minimizing, keep trying and advancing the pointer. */ + + 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 */ + } + + /* If maximizing, find the longest string and work backwards, as long as + the matched lengths for each iteration are the same. */ + + else + { + BOOL samelengths = TRUE; + Lstart = Feptr; /* Starting position */ + Flength = Fovector[Loffset+1] - Fovector[Loffset]; + + 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. */ + + if (rrc > 0 && mb->partial != 0 && + mb->end_subject > mb->start_used_ptr) { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) break; - eptr++; + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } break; + } - case OP_DIGIT: - for (i = min; i < max; i++) + if (slength != Flength) samelengths = FALSE; + Feptr += slength; + } + + /* 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. */ + + if (samelengths) + { + while (Feptr >= Lstart) + { + RMATCH(Fecode, RM21); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + 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 + { + Lmax = i; + for (;;) + { + 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 (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) break; - eptr++; + PCRE2_SIZE slength; + (void)match_ref(Loffset, Lcaseless, F, mb, &slength); + Feptr += slength; } + } + } + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + +#undef Lcaseless +#undef Lmin +#undef Lmax +#undef Lstart +#undef Loffset + + + +/* ========================================================================= */ +/* Opcodes for the start of various parenthesized items */ +/* ========================================================================= */ + + /* 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)). */ + + + /* ===================================================================== */ + /* 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; + } + + 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) RRETURN(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 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; + 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 op code */ + + 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) + frame_size; + 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_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); - 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; + /* 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_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_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; + /* 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_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); - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } + /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that + the branch in which it occurs can be determined. */ - 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: + 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); + + 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); /* Back track */ + +#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) + #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 */ - /************************************************* @@ -6440,8 +6171,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; @@ -6451,7 +6180,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; @@ -6466,18 +6194,21 @@ 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. */ 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. */ @@ -6506,8 +6237,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 @@ -6519,20 +6250,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 @@ -6591,7 +6324,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; @@ -6610,7 +6343,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; @@ -6618,14 +6359,10 @@ 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. */ +/* 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; @@ -6637,8 +6374,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 @@ -6649,20 +6384,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; @@ -6679,6 +6400,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; @@ -6696,71 +6422,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); + +/* 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 kilobytes.) */ -if (re->top_backref >= match_data->oveccount) +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) - { - PCRE2_SIZE *iptr = mb->ovector + ocount; - 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) { @@ -6784,7 +6530,6 @@ the loop runs just once. */ for(;;) { PCRE2_SPTR new_start_match; - mb->capture_last = 0; /* ----------------- Start of match optimizations ---------------- */ @@ -6800,8 +6545,8 @@ for(;;) /* 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. */ + we stop the optimization scans for a first code unit at a newline. If the + match fails at the newline, later code breaks this loop. */ if (firstline) { @@ -6821,90 +6566,156 @@ for(;;) 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, 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 (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->partial && start_match >= end_subject) + { + rc = MATCH_NOMATCH; + 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) { - 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++; + } } - } + } /* End first code unit handling */ /* Restore fudged end_subject */ end_subject = save_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) { @@ -6913,12 +6724,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 @@ -6931,27 +6746,52 @@ for(;;) 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 */ { - 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) @@ -6961,8 +6801,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; } @@ -6983,14 +6823,14 @@ 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; 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) { @@ -7016,9 +6856,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 */ @@ -7092,11 +6932,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; @@ -7110,18 +6950,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. */ @@ -7130,68 +6962,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 : (int)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) - { - 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)? @@ -7207,18 +6985,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; @@ -7229,11 +7003,9 @@ 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) diff --git a/ProcessHacker/pcre/pcre2_match_data.c b/ProcessHacker/pcre/pcre2_match_data.c index 6e2e0a40c8d1..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,6 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" @@ -52,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) @@ -60,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 1d22666380bb..6e9366db93c6 100644 --- a/ProcessHacker/pcre/pcre2_newline.c +++ b/ProcessHacker/pcre/pcre2_newline.c @@ -47,7 +47,6 @@ 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" diff --git a/ProcessHacker/pcre/pcre2_ord2utf.c b/ProcessHacker/pcre/pcre2_ord2utf.c index 07ab10fda25f..1403730996d6 100644 --- a/ProcessHacker/pcre/pcre2_ord2utf.c +++ b/ProcessHacker/pcre/pcre2_ord2utf.c @@ -39,8 +39,6 @@ POSSIBILITY OF SUCH DAMAGE. */ -#define HAVE_CONFIG_H - /* 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. */ diff --git a/ProcessHacker/pcre/pcre2_pattern_info.c b/ProcessHacker/pcre/pcre2_pattern_info.c index fadad4b324a2..540707b2258c 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,8 +39,6 @@ POSSIBILITY OF SUCH DAMAGE. */ -#define HAVE_CONFIG_H - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -77,10 +75,12 @@ 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_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: @@ -91,7 +91,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: @@ -99,6 +98,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: @@ -139,6 +139,11 @@ 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_FIRSTCODETYPE: *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 : ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0; @@ -154,6 +159,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; @@ -162,6 +172,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; @@ -217,11 +232,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; @@ -257,11 +267,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. */ diff --git a/ProcessHacker/pcre/pcre2_printint.c b/ProcessHacker/pcre/pcre2_printint.c index 62074976489b..e4dd53fe8ca0 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -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: diff --git a/ProcessHacker/pcre/pcre2_serialize.c b/ProcessHacker/pcre/pcre2_serialize.c index 41d36a910c4a..d2cc603cbb5b 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -41,7 +41,6 @@ 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" @@ -215,7 +214,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 f3666460bd78..2a1f282629ab 100644 --- a/ProcessHacker/pcre/pcre2_string_utils.c +++ b/ProcessHacker/pcre/pcre2_string_utils.c @@ -42,7 +42,6 @@ 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" diff --git a/ProcessHacker/pcre/pcre2_study.c b/ProcessHacker/pcre/pcre2_study.c index d6403dccb0bb..b92686759dac 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,15 +42,12 @@ POSSIBILITY OF SUCH DAMAGE. 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 @@ -159,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; @@ -173,8 +170,7 @@ for (;;) } /* Fall through */ - case OP_ONCE_NC: - case OP_BRA: + case OP_ONCE: case OP_SBRA: case OP_BRAPOS: case OP_SBRAPOS: @@ -790,6 +786,7 @@ if (utf) if (caseless) { +#ifdef SUPPORT_UNICODE if (utf) { #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -802,10 +799,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; @@ -954,7 +953,6 @@ do case OP_ALLANY: case OP_ANY: case OP_ANYBYTE: - case OP_CIRC: case OP_CIRCM: case OP_CLOSE: case OP_COMMIT: @@ -1022,6 +1020,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 @@ -1068,7 +1073,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; @@ -1450,6 +1454,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 @@ -1577,12 +1585,11 @@ 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; diff --git a/ProcessHacker/pcre/pcre2_substitute.c b/ProcessHacker/pcre/pcre2_substitute.c index c828e307f643..8da951fc6e2a 100644 --- a/ProcessHacker/pcre/pcre2_substitute.c +++ b/ProcessHacker/pcre/pcre2_substitute.c @@ -39,7 +39,6 @@ POSSIBILITY OF SUCH DAMAGE. */ -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_substring.c b/ProcessHacker/pcre/pcre2_substring.c index bee84b1d8e73..f6d7c3972270 100644 --- a/ProcessHacker/pcre/pcre2_substring.c +++ b/ProcessHacker/pcre/pcre2_substring.c @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_tables.c b/ProcessHacker/pcre/pcre2_tables.c index be0c101bc226..9f8dc293aa22 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-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,12 +39,11 @@ 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. */ -#define HAVE_CONFIG_H #ifndef PCRE2_PCRE2TEST /* We're compiling the library */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -149,7 +148,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 +157,62 @@ 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 (E_Base or E_Base_GAZ followed + by E_Modifier). Extend characters are allowed before the modifier; this + cannot be represented in this table, the code has to deal with it. + +8. Do not break within emoji zwj sequences (ZWJ followed by Glue_After_Zwj or + E_Base_GAZ). + +9. 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. + +10. Otherwise, break everywhere. */ +#define ESZ (1< 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 43d0150e7554..407d3f5b8796 100644 --- a/ProcessHacker/pcre/pcre2_xclass.c +++ b/ProcessHacker/pcre/pcre2_xclass.c @@ -43,7 +43,6 @@ 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/pcre2posix.c b/ProcessHacker/pcre/pcre2posix.c index 343b835aa216..908f91cded92 100644 --- a/ProcessHacker/pcre/pcre2posix.c +++ b/ProcessHacker/pcre/pcre2posix.c @@ -44,8 +44,7 @@ functions. */ // dmex: Disable warnings. #pragma warning(push) -#pragma warning(disable : 4267) -#define HAVE_CONFIG_H +#pragma warning(disable : 4267) #ifdef HAVE_CONFIG_H #include "config.h" @@ -102,7 +101,7 @@ 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 +#define PCRE2_CALL_CONVENTION #endif /* Table to translate PCRE2 compile time error codes into POSIX error codes. @@ -146,6 +145,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 */ @@ -235,20 +235,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) @@ -263,7 +268,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; } @@ -342,8 +347,8 @@ 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] + so; + pmatch[i].rm_eo = ovector[i*2+1] + so; } for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; return 0; diff --git a/ProcessHacker/pcre/pcre2posix.h b/ProcessHacker/pcre/pcre2posix.h index 6505976aa493..4ae1d3c2a051 100644 --- a/ProcessHacker/pcre/pcre2posix.h +++ b/ProcessHacker/pcre/pcre2posix.h @@ -62,6 +62,8 @@ extern "C" { #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; From 672659157bf8659da4a422b5faed16f6d6f0d67a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 3 Dec 2017 13:18:44 +1100 Subject: [PATCH 604/839] Add temporary workaround for extra mitigation options --- ProcessHacker/mtgndlg.c | 199 +++++++++++++++++++++++++++++++++++----- ProcessHacker/prpggen.c | 22 +---- 2 files changed, 177 insertions(+), 44 deletions(-) diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index 164152b0f52f..80317341f046 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -3,6 +3,7 @@ * process mitigation policy details * * Copyright (C) 2016 wj32 + * Copyright (C) 2016-2017 dmex * * This file is part of Process Hacker. * @@ -26,12 +27,15 @@ 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; @@ -42,44 +46,127 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( _In_ LPARAM lParam ); +NTSTATUS PhpGetProcessSystemDllInitBlock( + _In_ HANDLE ProcessHandle, + _Out_ PPS_SYSTEM_DLL_INIT_BLOCK *SystemDllInitBlock + ) +{ + NTSTATUS status; + PPS_SYSTEM_DLL_INIT_BLOCK ldrInitBlock = NULL; + PVOID ldrInitBlockBaseAddress = NULL; + PPH_STRING ntdllFileName; + + ldrInitBlock = PhAllocate(sizeof(PS_SYSTEM_DLL_INIT_BLOCK)); + memset(ldrInitBlock, 0, sizeof(PS_SYSTEM_DLL_INIT_BLOCK)); + + ntdllFileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\System32\\ntdll.dll"); + status = PhGetProcedureAddressRemote( + ProcessHandle, + ntdllFileName->Buffer, + "LdrSystemDllInitBlock", + 0, + &ldrInitBlockBaseAddress, + NULL + ); + + PhDereferenceObject(ntdllFileName); + + if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) + { + status = NtReadVirtualMemory( + ProcessHandle, + ldrInitBlockBaseAddress, + ldrInitBlock, + sizeof(PS_SYSTEM_DLL_INIT_BLOCK), + NULL + ); + } + + if (NT_SUCCESS(status)) + { + *SystemDllInitBlock = ldrInitBlock; + } + + return status; +} + 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++) + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) { - if (Information->Pointers[policy] && PhDescribeProcessMitigationPolicy( - policy, - Information->Pointers[policy], - &shortDescription, - &longDescription - )) + PPS_SYSTEM_DLL_INIT_BLOCK dllInitBlock; + + if (NT_SUCCESS(PhpGetProcessSystemDllInitBlock(processHandle, &dllInitBlock))) { - context.Entries[policy].ShortDescription = shortDescription; - context.Entries[policy].LongDescription = longDescription; + context.SystemDllInitBlock = dllInitBlock; } + + NtClose(processHandle); } - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MITIGATION), - ParentWindowHandle, - PhpProcessMitigationPolicyDlgProc, - (LPARAM)&context - ); + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION, + ProcessId + ))) + { + 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; + } + } + + 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); - for (policy = 0; policy < MaxProcessMitigationPolicy; policy++) + 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 +177,36 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( _In_ LPARAM lParam ) { + PMITIGATION_POLICY_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PMITIGATION_POLICY_CONTEXT)lParam; + SetProp(hwndDlg, L"Context", (HANDLE)context); + } + else + { + context = (PMITIGATION_POLICY_CONTEXT)GetProp(hwndDlg, L"Context"); + + if (uMsg == WM_DESTROY) + { + RemoveProp(hwndDlg, L"Context"); + } + } + + 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,6 +222,33 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); } + if (context->SystemDllInitBlock) + { + 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 depenedent 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); + } + } + ExtendedListView_SortItems(lvHandle); ExtendedListView_SetColumnWidth(lvHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); ListView_SetItemState(lvHandle, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); @@ -123,7 +257,26 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( 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: diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index a6d288b7c7d9..ad5c5c54bac6 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -527,27 +527,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: From c498bc0772554b0bedc37e9bf8c87e91e3345745 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 3 Dec 2017 13:23:22 +1100 Subject: [PATCH 605/839] Update header --- ProcessHacker/include/phapp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index ab2ee920e15f..5c1740621268 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -482,7 +482,7 @@ VOID PhShowMemoryStringDialog( VOID PhShowProcessMitigationPolicyDialog( _In_ HWND ParentWindowHandle, - _In_ struct _PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION *Information + _In_ HANDLE ProcessId ); // netstk @@ -614,7 +614,7 @@ NTAPI PhCreateSearchControl( _In_ HWND Parent, _In_ HWND WindowHandle, - _In_ PWSTR BannerText + _In_opt_ PWSTR BannerText ); PHAPPAPI From d7524e366a5c619fefb18df0104434460144ff2c Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 4 Dec 2017 03:24:57 +1100 Subject: [PATCH 606/839] Add missing error case when editing environment variables --- ProcessHacker/prpgenv.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index d81c73a68264..02d007c0610d 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -304,16 +304,25 @@ INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( for (i = 0; i < numberOfIndices; i++) { item = PhItemArray(&environmentContext->Items, PtrToUlong(indices[i]) - 1); - PhSetEnvironmentVariableRemote(processHandle, &item->Name->sr, NULL, &timeout); + status = PhSetEnvironmentVariableRemote(processHandle, &item->Name->sr, NULL, &timeout); } NtClose(processHandle); PhpRefreshEnvironment(hwndDlg, environmentContext, processItem); + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hwndDlg, L"Unable to delete the environment variable.", status, 0); + } + else if (status == STATUS_TIMEOUT) + { + PhShowStatus(hwndDlg, L"Unable to delete the environment variable.", 0, WAIT_TIMEOUT); + } } else { - PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); + PhShowStatus(hwndDlg, L"Unable to open the process.", status, 0); } } @@ -543,7 +552,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; } From 2512bd079e33ce2164909a439663acee8d4c7922 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 4 Dec 2017 03:40:00 +1100 Subject: [PATCH 607/839] Fix stale token privileges after changing token integrity level --- ProcessHacker/tokprp.c | 96 +++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 3e6596d6b7f4..906bd4dc7222 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -389,7 +389,6 @@ BOOLEAN PhpUpdateTokenGroups( return FALSE; ExtendedListView_SetRedraw(GroupsLv, FALSE); - ListView_DeleteAllItems(GroupsLv); PhpUpdateSidsFromTokenGroups(GroupsLv, groups, FALSE); @@ -400,7 +399,6 @@ BOOLEAN PhpUpdateTokenGroups( } ExtendedListView_SortItems(GroupsLv); - ExtendedListView_SetRedraw(GroupsLv, TRUE); if (TokenPageContext->RestrictedSids) @@ -416,6 +414,59 @@ BOOLEAN PhpUpdateTokenGroups( return TRUE; } +BOOLEAN PhpUpdateTokenPrivileges( + _In_ HWND hwndDlg, + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND PrivilegesLv, + _In_ HANDLE TokenHandle + ) +{ + PTOKEN_PRIVILEGES privileges; + ULONG i; + + if (!NT_SUCCESS(PhGetTokenPrivileges(TokenHandle, &privileges))) + return FALSE; + + ExtendedListView_SetRedraw(PrivilegesLv, FALSE); + ListView_DeleteAllItems(PrivilegesLv); + + for (i = 0; i < privileges->PrivilegeCount; i++) + { + INT lvItemIndex; + PPH_STRING privilegeName; + PPH_STRING privilegeDisplayName; + + if (PhLookupPrivilegeName( + &privileges->Privileges[i].Luid, + &privilegeName + )) + { + privilegeDisplayName = NULL; + PhLookupPrivilegeDisplayName(&privilegeName->sr, &privilegeDisplayName); + + // Name + lvItemIndex = PhAddListViewItem(PrivilegesLv, MAXINT, privilegeName->Buffer, &privileges->Privileges[i]); + // Status + PhSetListViewSubItem(PrivilegesLv, lvItemIndex, 1, PhGetPrivilegeAttributesString(privileges->Privileges[i].Attributes)); + // Description + PhSetListViewSubItem(PrivilegesLv, lvItemIndex, 2, PhGetString(privilegeDisplayName)); + + if (privilegeDisplayName) PhDereferenceObject(privilegeDisplayName); + PhDereferenceObject(privilegeName); + } + } + + ExtendedListView_SortItems(PrivilegesLv); + ExtendedListView_SetRedraw(PrivilegesLv, TRUE); + + if (TokenPageContext->Privileges) + PhFree(TokenPageContext->Privileges); + + TokenPageContext->Privileges = privileges; + + return TRUE; +} + FORCEINLINE PTOKEN_PAGE_CONTEXT PhpTokenPageHeader( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -495,7 +546,6 @@ INT_PTR CALLBACK PhpTokenPageProc( BOOLEAN isVirtualizationEnabled; PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; PPH_STRING appContainerSid; - ULONG i; if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) { @@ -570,40 +620,7 @@ INT_PTR CALLBACK PhpTokenPageProc( 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); - } + PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, privilegesLv, tokenHandle); NtClose(tokenHandle); } @@ -903,7 +920,10 @@ INT_PTR CALLBACK PhpTokenPageProc( ); if (NT_SUCCESS(status)) - PhpUpdateTokenGroups(hwndDlg, tokenPageContext, GetDlgItem(hwndDlg, IDC_GROUPS), tokenHandle); + { + PhpUpdateTokenGroups(hwndDlg, tokenPageContext, tokenPageContext->GroupsListViewHandle, tokenHandle); + PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, tokenPageContext->PrivilegesListViewHandle, tokenHandle); + } NtClose(tokenHandle); } From ab7b45412a981cbea423955220c9927f1ee77c69 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 4 Dec 2017 04:19:22 +1100 Subject: [PATCH 608/839] UserNotes: Fix crash changing affinity settings, Fix changing affinity for system processes, Add extra error checking/messages --- plugins/UserNotes/main.c | 58 ++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 409f92957f66..06df65c71879 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -171,21 +171,23 @@ PPH_STRING SaveCustomColors( return PhFinalStringBuilderString(&stringBuilder); } -ULONG_PTR GetProcessAffinity( - _In_ HANDLE ProcessId +NTSTATUS GetProcessAffinity( + _In_ HANDLE ProcessId, + _Out_ ULONG_PTR *Affinity ) { + NTSTATUS status; HANDLE processHandle; ULONG_PTR affinityMask = 0; PROCESS_BASIC_INFORMATION basicInfo; - if (NT_SUCCESS(PhOpenProcess( + if (NT_SUCCESS(status = PhOpenProcess( &processHandle, ProcessQueryAccess, ProcessId ))) { - if (NT_SUCCESS(PhGetProcessBasicInformation( + if (NT_SUCCESS(status = PhGetProcessBasicInformation( processHandle, &basicInfo ))) @@ -196,7 +198,10 @@ ULONG_PTR GetProcessAffinity( NtClose(processHandle); } - return affinityMask; + if (NT_SUCCESS(status)) + *Affinity = affinityMask; + + return status; } IO_PRIORITY_HINT GetProcessIoPriority( @@ -438,7 +443,7 @@ VOID NTAPI MenuItemCallback( if (!highlightPresent) { CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; - chooseColor.hwndOwner = PhMainWndHandle; + chooseColor.hwndOwner = menuItem->OwnerWindow; chooseColor.lpCustColors = ProcessCustomColors; chooseColor.lpfnHook = ColorDlgHookProc; chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK; @@ -503,8 +508,18 @@ VOID NTAPI MenuItemCallback( } else { - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->AffinityMask = GetProcessAffinity(processItem->ProcessId); + 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(); @@ -524,8 +539,18 @@ VOID NTAPI MenuItemCallback( } else { - object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); - object->AffinityMask = GetProcessAffinity(processItem->ProcessId); + 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(); @@ -622,6 +647,7 @@ VOID NTAPI MenuHookCallback( break; case PHAPP_ID_PROCESS_AFFINITY: { + NTSTATUS status; BOOLEAN changed = FALSE; ULONG_PTR affinityMask; ULONG_PTR newAffinityMask; @@ -630,14 +656,18 @@ VOID NTAPI MenuHookCallback( 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; - // Query the current process affinity. - affinityMask = GetProcessAffinity(processItem->ProcessId); - // Show the affinity dialog (with our values). - if (PhShowProcessAffinityDialog2(PhMainWndHandle, affinityMask, &newAffinityMask)) + if (PhShowProcessAffinityDialog2(menuHookInfo->MenuInfo->OwnerWindow, affinityMask, &newAffinityMask)) { PDB_OBJECT object; From 94789a8bcf82ad3f238a05ea0fc018f926659c86 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 4 Dec 2017 04:29:42 +1100 Subject: [PATCH 609/839] Fix error saving column sets #180 --- ProcessHacker/colsetmgr.c | 5 +++-- ProcessHacker/mainwnd.c | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/colsetmgr.c b/ProcessHacker/colsetmgr.c index 63c0617b6c55..901dd3628c4c 100644 --- a/ProcessHacker/colsetmgr.c +++ b/ProcessHacker/colsetmgr.c @@ -113,7 +113,7 @@ VOID PhSaveSettingsColumnList( PhRemoveEndStringBuilder(&stringBuilder, 1); settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); - PhSetStringSetting2(L"ProcessTreeColumnSetConfig", &settingsString->sr); + PhSetStringSetting2(SettingName, &settingsString->sr); } VOID PhDeleteColumnSetList( @@ -136,6 +136,7 @@ VOID PhDeleteColumnSetList( PhDereferenceObject(ColumnSetList); } +_Success_(return) BOOLEAN PhLoadSettingsColumnSet( _In_ PWSTR SettingName, _In_ PPH_STRING ColumnSetName, @@ -151,7 +152,7 @@ BOOLEAN PhLoadSettingsColumnSet( PH_STRINGREF remaining; PH_STRINGREF part; - settingsString = PhaGetStringSetting(L"ProcessTreeColumnSetConfig"); + settingsString = PhaGetStringSetting(SettingName); remaining = settingsString->sr; if (remaining.Length == 0) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index c803890aa2ef..ad2de4ecbda7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2381,7 +2381,7 @@ VOID PhMwpDispatchMenuCommand( break; } - if (columnSetName) + if (!PhIsNullOrEmptyString(columnSetName)) { PPH_STRING treeSettings; PPH_STRING sortSettings; @@ -2393,7 +2393,6 @@ VOID PhMwpDispatchMenuCommand( PhDereferenceObject(treeSettings); PhDereferenceObject(sortSettings); - PhDereferenceObject(columnSetName); } } return; From 7853b856525252952f9e897ac807c1b114754dd7 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 4 Dec 2017 15:02:15 +1100 Subject: [PATCH 610/839] Fix race introduced from commit c63ad12d --- ProcessHacker/procprv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index e65e512f4b8b..40b692dde21b 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1179,8 +1179,6 @@ VOID PhpProcessQueryStage1( { Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } - - PhpQueueProcessQueryStage2(processItem); } VOID PhpProcessQueryStage2( @@ -1323,6 +1321,9 @@ VOID PhpFillProcessItemStage1( processItem->IsImmersive = Data->IsImmersive; PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); + + // Note: Queue stage 2 processing after filling stage1 process data. + PhpQueueProcessQueryStage2(processItem); } VOID PhpFillProcessItemStage2( From f853e5930624ee0d0728084b716b4c166fcbf69d Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 10 Dec 2017 08:08:29 +1100 Subject: [PATCH 611/839] Add PhLoadAppKey, PhLoadString, PhLoadIndirectString helper functions --- ProcessHacker/ProcessHacker.def | 5 ++ phlib/include/phnative.h | 10 +++ phlib/include/phutil.h | 16 +++++ phlib/native.c | 83 ++++++++++++++++++++++ phlib/util.c | 121 ++++++++++++++++++++++++++++++++ phnt/include/ntregapi.h | 6 ++ 6 files changed, 241 insertions(+) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index f36b044488fa..b77f3d95a2d3 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -181,6 +181,8 @@ EXPORTS PhInsertStringBuilderEx PhIntegerToString64 PhInvokeCallback + PhLoadIndirectString + PhLoadResource PhLocalTimeToSystemTime PhLowerBoundElementAvlTree PhLowerDualBoundElementAvlTree @@ -284,6 +286,7 @@ EXPORTS PhInjectDllProcess PhListenNamedPipe PhOpenKey + PhLoadAppKey PhOpenProcess = PhOpenProcessPublic PhOpenThread = PhOpenThreadPublic PhOpenThreadProcess @@ -373,6 +376,8 @@ EXPORTS PhParseCommandLineFuzzy PhParseCommandLinePart PhQueryRegistryString + PhQueryRegistryUlong + PhQueryRegistryUlong64 PhReferenceObjects PhSetFileDialogFileName PhSetFileDialogFilter diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 1ab3c6574068..14467aa734e5 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -935,6 +935,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 diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index cdfbbdbdba2b..84a3c84b047d 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -819,6 +819,14 @@ PhQueryRegistryString( _In_opt_ PWSTR ValueName ); +PHLIBAPI +ULONG +NTAPI +PhQueryRegistryUlong( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ); + PHLIBAPI ULONG64 NTAPI @@ -891,6 +899,7 @@ PhShowFileDialog( #define PH_FILEDIALOG_DEFAULTEXPANDED 0x40 #define PH_FILEDIALOG_STRICTFILETYPES 0x80 #define PH_FILEDIALOG_PICKFOLDERS 0x100 +#define PH_FILEDIALOG_NOPATHVALIDATE 0x200 PHLIBAPI ULONG @@ -1110,6 +1119,13 @@ PhLoadResource( _Out_ PVOID *ResourceBuffer ); +PHLIBAPI +PPH_STRING +NTAPI +PhLoadIndirectString( + _In_ PWSTR SourceString + ); + #ifdef __cplusplus } #endif diff --git a/phlib/native.c b/phlib/native.c index 1ffe2d2a675b..4f66d48c3d4c 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5942,6 +5942,89 @@ NTSTATUS PhOpenKey( 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. * diff --git a/phlib/util.c b/phlib/util.c index 979a6a5ca74f..6e9b9c2bb9db 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -3512,6 +3512,34 @@ PPH_STRING PhQueryRegistryString( return string; } +ULONG PhQueryRegistryUlong( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ) +{ + ULONG ulong = 0; + 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 @@ -5216,3 +5244,96 @@ BOOLEAN PhLoadResource( return TRUE; } + +PPH_STRING PhLoadString( + _In_ PVOID DllBase, + _In_ ULONG ResourceId + ) +{ + PPH_STRING string = NULL; + ULONG resourceLength; + PVOID resourceBuffer; + ULONG stringCount; + PWSTR stringBuffer; + ULONG i; + + if (!PhLoadResource( + DllBase, + MAKEINTRESOURCE((LOWORD(ResourceId) >> 4) + 1), + RT_STRING, + &resourceLength, + &resourceBuffer + )) + { + return NULL; + } + + stringBuffer = resourceBuffer; + stringCount = ResourceId & 0x000F; + + for (i = 0; i < stringCount; 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; +} + +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)) + 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)) + { + indirectString = PhLoadString(libraryModule, -index); + FreeLibrary(libraryModule); + } + + PhDereferenceObject(libraryString); + } + else + { + //indirectString = PhCreateString(SourceString); + } + + return indirectString; +} diff --git a/phnt/include/ntregapi.h b/phnt/include/ntregapi.h index da9bc244de3a..a6e1240898ef 100644 --- a/phnt/include/ntregapi.h +++ b/phnt/include/ntregapi.h @@ -534,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 From b1e80bc0a4b34330b948e0ef78bb86478e3232c5 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 12 Dec 2017 20:52:07 +1100 Subject: [PATCH 612/839] Remove about window parent (Feature request #201) --- ProcessHacker/about.c | 4 ++-- ProcessHacker/include/phapp.h | 2 +- ProcessHacker/mainwnd.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index 91c61818a748..83a005208958 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -128,13 +128,13 @@ static INT_PTR CALLBACK PhpAboutDlgProc( } VOID PhShowAboutDialog( - _In_ HWND ParentWindowHandle + VOID ) { DialogBox( PhInstanceHandle, MAKEINTRESOURCE(IDD_ABOUT), - ParentWindowHandle, + NULL, PhpAboutDlgProc ); } diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 5c1740621268..ea1fda8a0e3c 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -294,7 +294,7 @@ BOOLEAN PhUiCreateDumpFileProcess( // about VOID PhShowAboutDialog( - _In_ HWND ParentWindowHandle + VOID ); PPH_STRING PhGetDiagnosticsString( diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index ad2de4ecbda7..b2031ecb6b7c 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -943,7 +943,7 @@ VOID PhMwpOnCommand( break; case ID_HELP_ABOUT: { - PhShowAboutDialog(PhMainWndHandle); + PhShowAboutDialog(); } break; case ID_PROCESS_TERMINATE: From f0b3516ab417689bf4acdfba3e9fc89f3caa8669 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 13 Dec 2017 00:00:17 +1100 Subject: [PATCH 613/839] Fix runas service regression from 2cfb6835 --- ProcessHacker/runas.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 954f4fb811eb..e204caab1d10 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -370,6 +370,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { NTSTATUS status; PPH_STRING program; + PPH_STRING programEscaped; PPH_STRING userName; PPH_STRING password; PPH_STRING logonTypeString; @@ -382,6 +383,15 @@ INT_PTR CALLBACK PhpRunAsDlgProc( userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME); logonTypeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE); + if (PhIsNullOrEmptyString(program)) + break; + + // Escape the path. (dmex: poor man's PathQuoteSpaces) + if (!PhStartsWithString2(program, L"\"", FALSE) && PhFindCharInString(program, 0, L' ') != -1) + { + programEscaped = PhaConcatStrings(3, L"\"", PhGetString(program), L"\""); + } + // Fix up the user name if it doesn't have a domain. if (PhFindCharInString(userName, 0, '\\') == -1) { @@ -431,7 +441,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( PhpSplitUserName(userName->Buffer, &domainPart, &userPart); memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); - createInfo.CommandLine = program->Buffer; + createInfo.CommandLine = programEscaped->Buffer; createInfo.UserName = PhGetString(userPart); createInfo.DomainName = PhGetString(domainPart); createInfo.Password = PhGetStringOrEmpty(password); @@ -457,7 +467,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { status = PhExecuteRunAsCommand2( hwndDlg, - program->Buffer, + programEscaped->Buffer, userName->Buffer, PhGetStringOrEmpty(password), logonType, From e30b1fe9fd5a342cbd4a958454a7d930b6dc1080 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 13 Dec 2017 00:03:26 +1100 Subject: [PATCH 614/839] Tidy up --- ProcessHacker/include/memlist.h | 2 +- ProcessHacker/memrslt.c | 2 +- phlib/basesup.c | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/include/memlist.h b/ProcessHacker/include/memlist.h index 482f485a62cd..a7f714d32fa8 100644 --- a/ProcessHacker/include/memlist.h +++ b/ProcessHacker/include/memlist.h @@ -114,7 +114,7 @@ VOID PhSetOptionsMemoryList( VOID PhReplaceMemoryList( _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ PPH_MEMORY_ITEM_LIST List + _In_opt_ PPH_MEMORY_ITEM_LIST List ); VOID PhUpdateMemoryNode( diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index ba3b0750c547..efe9d74ff961 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -108,7 +108,7 @@ static PPH_STRING PhpGetStringForSelectedResults( result = Results->Items[i]; - PhAppendFormatStringBuilder(&stringBuilder, L"0x%Ix (%u): %s\r\n", result->Address, result->Length, + PhAppendFormatStringBuilder(&stringBuilder, L"0x%Ix (%lu): %s\r\n", result->Address, result->Length, result->Display.Buffer ? result->Display.Buffer : L""); } diff --git a/phlib/basesup.c b/phlib/basesup.c index 93a08cfe4c43..35364e49c0af 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -88,11 +88,6 @@ VOID NTAPI PhpPointerListDeleteProcedure( _In_ ULONG Flags ); -VOID NTAPI PhpQueueDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - VOID NTAPI PhpHashtableDeleteProcedure( _In_ PVOID Object, _In_ ULONG Flags From 9ccc789b83cae3fc6a5c7221704c25f4a1ce6e05 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 13 Dec 2017 00:04:27 +1100 Subject: [PATCH 615/839] Disable contextmenu for system and idle processes --- ProcessHacker/mwpgproc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index bc88c3fd8fda..acaa84fa7bc8 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -543,7 +543,11 @@ VOID PhMwpInitializeProcessMenu( // If the user selected a fake process, disable all but // a few menu items. - if (PH_IS_FAKE_PROCESS_ID(Processes[0]->ProcessId)) + if ( + PH_IS_FAKE_PROCESS_ID(Processes[0]->ProcessId) || + Processes[0]->ProcessId == SYSTEM_IDLE_PROCESS_ID || + Processes[0]->ProcessId == SYSTEM_PROCESS_ID // TODO: Some menu entires could be enabled for the system process? + ) { PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); PhEnableEMenuItem(Menu, ID_PROCESS_PROPERTIES, TRUE); From ff755fb6fcbf9439b3ed6eb6fbfbc235ddb7c9af Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 14 Dec 2017 23:47:32 +1100 Subject: [PATCH 616/839] Fix macro usage, Improve struct checks --- phlib/mapimg.c | 40 ++++++++++++---------------------------- tools/peview/ldprp.c | 14 +++++--------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/phlib/mapimg.c b/phlib/mapimg.c index fbee31dec399..1ddb46aa6510 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1219,7 +1219,7 @@ NTSTATUS PhGetMappedImageCfg64( return status; // Not every load configuration defines CFG characteristics - if (config64->Size < UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardFlags)) + if (!RTL_CONTAINS_FIELD(config64, config64->Size, GuardFlags)) return STATUS_INVALID_VIEW_SIZE; CfgConfig->MappedImage = MappedImage; @@ -1238,7 +1238,7 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardFunctionEntries = config64->GuardCFFunctionCount; CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardCFFunctionTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(config64->GuardCFFunctionTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1261,16 +1261,12 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardAdressIatEntries = 0; CfgConfig->GuardAdressIatTable = 0; - if ( - config64->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardAddressTakenIatEntryTable) + - sizeof(config64->GuardAddressTakenIatEntryTable) + - sizeof(config64->GuardAddressTakenIatEntryCount) - ) + if (RTL_CONTAINS_FIELD(config64, config64->Size, GuardAddressTakenIatEntryTable)) { CfgConfig->NumberOfGuardAdressIatEntries = config64->GuardAddressTakenIatEntryCount; CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardAddressTakenIatEntryTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(config64->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1294,16 +1290,12 @@ NTSTATUS PhGetMappedImageCfg64( CfgConfig->NumberOfGuardLongJumpEntries = 0; CfgConfig->GuardLongJumpTable = 0; - if ( - config64->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardLongJumpTargetTable) + - sizeof(config64->GuardLongJumpTargetTable) + - sizeof(config64->GuardLongJumpTargetCount) - ) + if (RTL_CONTAINS_FIELD(config64, config64->Size, GuardLongJumpTargetTable)) { CfgConfig->NumberOfGuardLongJumpEntries = config64->GuardLongJumpTargetCount; CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config64->GuardLongJumpTargetTable, MappedImage->NtHeaders->OptionalHeader.ImageBase), + (ULONG)(config64->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), NULL ); @@ -1339,7 +1331,7 @@ NTSTATUS PhGetMappedImageCfg32( return status; // Not every load configuration defines CFG characteristics - if (config32->Size < UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardFlags)) + if (!RTL_CONTAINS_FIELD(config32, config32->Size, GuardFlags)) return STATUS_INVALID_VIEW_SIZE; CfgConfig->MappedImage = MappedImage; @@ -1358,7 +1350,7 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->NumberOfGuardFunctionEntries = config32->GuardCFFunctionCount; CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardCFFunctionTable , MappedImage->NtHeaders32->OptionalHeader.ImageBase), + config32->GuardCFFunctionTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, NULL ); @@ -1381,16 +1373,12 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->NumberOfGuardAdressIatEntries = 0; CfgConfig->GuardAdressIatTable = 0; - if ( - config32->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardAddressTakenIatEntryTable) + - sizeof(config32->GuardAddressTakenIatEntryTable) + - sizeof(config32->GuardAddressTakenIatEntryCount) - ) + if (RTL_CONTAINS_FIELD(config32, config32->Size, GuardAddressTakenIatEntryTable)) { CfgConfig->NumberOfGuardAdressIatEntries = config32->GuardAddressTakenIatEntryCount; CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardAddressTakenIatEntryTable, MappedImage->NtHeaders32->OptionalHeader.ImageBase), + config32->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, NULL ); @@ -1414,16 +1402,12 @@ NTSTATUS PhGetMappedImageCfg32( CfgConfig->NumberOfGuardLongJumpEntries = 0; CfgConfig->GuardLongJumpTable = 0; - if ( - config32->Size >= UFIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, GuardLongJumpTargetTable) + - sizeof(config32->GuardLongJumpTargetTable) + - sizeof(config32->GuardLongJumpTargetCount) - ) + if (RTL_CONTAINS_FIELD(config32, config32->Size, GuardLongJumpTargetTable)) { CfgConfig->NumberOfGuardLongJumpEntries = config32->GuardLongJumpTargetCount; CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( MappedImage, - (ULONG)(ULONG_PTR)PTR_SUB_OFFSET(config32->GuardLongJumpTargetTable, MappedImage->NtHeaders32->OptionalHeader.ImageBase), + config32->GuardLongJumpTargetTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, NULL ); diff --git a/tools/peview/ldprp.c b/tools/peview/ldprp.c index d19cb649d83b..39516c7b0dae 100644 --- a/tools/peview/ldprp.c +++ b/tools/peview/ldprp.c @@ -85,23 +85,19 @@ INT_PTR CALLBACK PvpPeLoadConfigDlgProc( 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 >= UFIELD_OFFSET(Type, GuardCFCheckFunctionPointer)) \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, GuardCFCheckFunctionPointer)) \ { \ 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 >= UFIELD_OFFSET(Type, GuardAddressTakenIatEntryTable) \ - + sizeof((Config)->GuardAddressTakenIatEntryTable) \ - + sizeof((Config)->GuardAddressTakenIatEntryCount)) \ + 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); \ + 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 >= UFIELD_OFFSET(Type, GuardLongJumpTargetTable) \ - + sizeof((Config)->GuardLongJumpTargetTable) \ - + sizeof((Config)->GuardLongJumpTargetCount)) \ + 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); \ From 5e34ca62e18296b33ced1d1b91a1336af5409b32 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 15 Dec 2017 09:27:02 +1100 Subject: [PATCH 617/839] Add RtlQueryImageMitigationPolicy/RtlSetImageMitigationPolicy types --- phnt/include/ntrtl.h | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 4d505b1f7602..92ad843fd878 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -6589,4 +6589,182 @@ RtlCrc64( #endif +// 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; +} 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 + #endif From fc07a5d1b69df0076bb85ff48e556c74b3234fc4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 00:06:55 +1100 Subject: [PATCH 618/839] Fix HideDriverServices regression from commit 1f39694e5a79ea18e6c8f5a8ed56e9fd45042f17 --- ProcessHacker/mwpgsrv.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/mwpgsrv.c b/ProcessHacker/mwpgsrv.c index c5ac2d0119f9..9d07c0cedab9 100644 --- a/ProcessHacker/mwpgsrv.c +++ b/ProcessHacker/mwpgsrv.c @@ -117,7 +117,8 @@ BOOLEAN PhMwpServicesPageCallback( return TRUE; case MainTabPageLoadSettings: { - // Nothing + if (PhGetIntegerSetting(L"HideDriverServices")) + DriverFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), PhMwpDriverServiceTreeFilter, NULL); } return TRUE; case MainTabPageSaveSettings: @@ -159,11 +160,7 @@ VOID PhMwpNeedServiceTreeList( if (!ServiceTreeListLoaded) { ServiceTreeListLoaded = TRUE; - PhLoadSettingsServiceTreeList(); - - if (PhGetIntegerSetting(L"HideDriverServices")) - DriverFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), PhMwpDriverServiceTreeFilter, NULL); } } From 3e9ad6f7ec57590a9b1e9ece88f63f6db3dcac11 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 01:02:57 +1100 Subject: [PATCH 619/839] Fix typo --- ProcessHacker/proctree.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 7dadc864a055..ceee54151cad 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -320,7 +320,7 @@ FORCEINLINE ULONG PhHashProcessNode( return HandleToUlong(Value->ProcessId) / 4; } -FORCEINLINE BOOLEAN PhpValidateParentCreateTime( +FORCEINLINE BOOLEAN PhpValidateParentProcessNode( _In_ PPH_PROCESS_NODE Child, _In_ PPH_PROCESS_NODE Parent ) @@ -375,7 +375,7 @@ PPH_PROCESS_NODE PhAddProcessNode( // Find this process' parent and add the process to it if we found it. if ( (parentNode = PhFindProcessNode(ProcessItem->ParentProcessId)) && - PhpValidateParentCreateTime(processNode, parentNode) + PhpValidateParentProcessNode(processNode, parentNode) ) { PhAddItemList(parentNode->Children, processNode); @@ -397,7 +397,7 @@ PPH_PROCESS_NODE PhAddProcessNode( if ( node != processNode && // for cases where the parent PID = PID (e.g. System Idle Process) node->ProcessItem->ParentProcessId == ProcessItem->ProcessId && - PhpValidateParentCreateTime(node, processNode) + PhpValidateParentProcessNode(node, processNode) ) { node->Parent = processNode; From ac4e206311a12188507e4adbd94832faaace04ab Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 01:05:03 +1100 Subject: [PATCH 620/839] Change default physical memory color (old graphs only) --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 6df4cc013175..77dadc5362c3 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -211,7 +211,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ColorIoReadOther", L"00ffff"); PhpAddIntegerSetting(L"ColorIoWrite", L"ff0077"); PhpAddIntegerSetting(L"ColorPrivate", L"0077ff"); - PhpAddIntegerSetting(L"ColorPhysical", L"ffff00"); + PhpAddIntegerSetting(L"ColorPhysical", L"ff8000"); // Blue PhpAddIntegerSetting(L"UseColorServiceStop", L"1"); PhpAddIntegerSetting(L"ColorServiceStop", L"6d6d6d"); // Dark grey From 5e4ed22ec7ab6b91946379fbde5eb8c84e6d1399 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 04:55:39 +1100 Subject: [PATCH 621/839] Add PhExtractIcon --- ProcessHacker/hidnproc.c | 6 ++---- ProcessHacker/procprv.c | 9 +++------ phlib/guisup.c | 16 ++++++---------- phlib/include/phutil.h | 9 +++++++++ phlib/util.c | 24 ++++++++++++++++++++++++ tools/peview/peprp.c | 8 +++----- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index f3b342c8c2fc..7744af80173f 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -665,12 +665,10 @@ static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( if (processItem->FileName) { // Small icon, large icon. - ExtractIconEx( + PhExtractIcon( processItem->FileName->Buffer, - 0, &processItem->LargeIcon, - &processItem->SmallIcon, - 1 + &processItem->SmallIcon ); // Version info. diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 40b692dde21b..1671ce92029a 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -956,14 +956,11 @@ VOID PhpProcessQueryStage1( if (processItem->FileName) { - // Small icon, large icon. - if (ExtractIconEx( + if (!PhExtractIcon( processItem->FileName->Buffer, - 0, &Data->LargeIcon, - &Data->SmallIcon, - 1 - ) == 0) + &Data->SmallIcon + )) { Data->LargeIcon = NULL; Data->SmallIcon = NULL; diff --git a/phlib/guisup.c b/phlib/guisup.c index e223e14d9d43..1753b9fd85da 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -689,16 +689,14 @@ VOID PhGetStockApplicationIcon( if (systemDirectory = PhGetSystemDirectory()) { PH_STRINGREF dllBaseName; - ULONG index; PhInitializeStringRef(&dllBaseName, L"\\user32.dll"); - index = 0; - dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName); - PhDereferenceObject(systemDirectory); - ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1); + PhExtractIcon(dllFileName->Buffer, &largeIcon, &smallIcon); + PhDereferenceObject(dllFileName); + PhDereferenceObject(systemDirectory); } } @@ -735,12 +733,10 @@ HICON PhGetFileShellIcon( if (FileName) { - ExtractIconEx( + PhExtractIcon( FileName, - 0, LargeIcon ? &icon : NULL, - !LargeIcon ? &icon : NULL, - 1 + !LargeIcon ? &icon : NULL ); } @@ -752,7 +748,7 @@ HICON PhGetFileShellIcon( ); if (icon) - icon = DuplicateIcon(NULL, icon); + icon = CopyIcon(icon); } return icon; diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 84a3c84b047d..8c04bc616f06 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1126,6 +1126,15 @@ PhLoadIndirectString( _In_ PWSTR SourceString ); +PHLIBAPI +BOOLEAN +NTAPI +PhExtractIcon( + _In_ PWSTR FileName, + _In_ HICON *IconLarge, + _In_ HICON *IconSmall + ); + #ifdef __cplusplus } #endif diff --git a/phlib/util.c b/phlib/util.c index 6e9b9c2bb9db..58c897b28ac5 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5287,6 +5287,12 @@ PPH_STRING PhLoadString( return string; } +// 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 ) @@ -5337,3 +5343,21 @@ PPH_STRING PhLoadIndirectString( return indirectString; } + +// rev from ExtractIconExW +BOOLEAN PhExtractIcon( + _In_ PWSTR FileName, + _In_ HICON *IconLarge, + _In_ HICON *IconSmall + ) +{ + static UINT (WINAPI *PrivateExtractIconExW)(PCWSTR, INT, HICON*, HICON*, UINT) = NULL; + + if (!PrivateExtractIconExW) + PrivateExtractIconExW = PhGetModuleProcAddress(L"user32.dll", "PrivateExtractIconExW"); + + if (!PrivateExtractIconExW) + return FALSE; + + return PrivateExtractIconExW(FileName, 0, IconLarge, IconSmall, 1) > 0; +} \ No newline at end of file diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 22ec46ba10f2..c13e90a75d9d 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -338,13 +338,11 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( { PhInitializeImageVersionInfo(&PvImageVersionInfo, PvFileName->Buffer); - if (ExtractIconEx( + if (!PhExtractIcon( PvFileName->Buffer, - 0, &PvImageLargeIcon, - NULL, - 1 - ) == 0) + NULL + )) { PvImageLargeIcon = PhGetFileShellIcon(PvFileName->Buffer, NULL, TRUE); } From 48bc87a5a02246162dc0be31d4bce38f3fb2ff3e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 04:56:27 +1100 Subject: [PATCH 622/839] Add PH_FILEDIALOG_NOPATHVALIDATE flags, Fix PhpConvertProcessInformation warnings --- phlib/util.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 58c897b28ac5..9471249823ca 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2527,12 +2527,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); } @@ -3894,7 +3894,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[] = @@ -3904,7 +3905,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 } }; /** From bfd5a9fd5649cf59d2386e0844960a18f4e9b946 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 07:04:05 +1100 Subject: [PATCH 623/839] Move Stage2 cached settings --- ProcessHacker/mainwnd.c | 3 +++ ProcessHacker/settings.c | 3 --- phlib/settings.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index b2031ecb6b7c..fc0b3fef3eef 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2100,6 +2100,9 @@ VOID PhMwpLoadSettings( PhEnableServiceNonPoll = !!PhGetIntegerSetting(L"EnableServiceNonPoll"); PhEnableNetworkProviderResolve = !!PhGetIntegerSetting(L"EnableNetworkResolve"); + PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); + PhEnableServiceQueryStage2 = !!PhGetIntegerSetting(L"EnableServiceStage2"); + PhNfLoadStage1(); PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 77dadc5362c3..d7235af1a9e2 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -223,9 +223,6 @@ VOID PhUpdateCachedSettings( VOID ) { - PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); - PhEnableServiceQueryStage2 = !!PhGetIntegerSetting(L"EnableServiceStage2"); - PH_UPDATE_SETTING(CollapseServicesOnStart); PH_UPDATE_SETTING(ForceNoParent); PH_UPDATE_SETTING(HighlightingDuration); diff --git a/phlib/settings.c b/phlib/settings.c index fd5ce5fd3e2f..0ed937f79648 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -83,7 +83,7 @@ VOID PhSettingsInitialization( PhIgnoredSettings = PhCreateList(4); PhAddDefaultSettings(); - PhUpdateCachedSettings(); + //PhUpdateCachedSettings(); } BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( From e2536adcdb0b8335a319adb2ab8e272d6eda8f41 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 07:19:31 +1100 Subject: [PATCH 624/839] Update ntldr.h types --- phnt/include/ntldr.h | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 37ed1d78536b..190afe199733 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -625,4 +625,58 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX PVOID DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; +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 + ); + +NTSTATUS +NTAPI +LdrOpenImageFileOptionsKey( + _In_ PUNICODE_STRING SubKey, + _In_ BOOLEAN Wow64, + _Out_ PHANDLE NewKeyHandle + ); + +NTSTATUS +NTAPI +LdrQueryImageFileKeyOption( + _In_ HANDLE KeyHandle, + _In_ PCWSTR ValueName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnedLength + ); + +NTSTATUS +NTAPI +LdrQueryImageFileExecutionOptions( + _In_ PUNICODE_STRING SubKey, + _In_ PCWSTR ValueName, + _In_ ULONG ValueSize, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG RetunedLength + ); + #endif From 037deb59c3aab7645532440fb72cad0548e7db65 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 07:21:10 +1100 Subject: [PATCH 625/839] NetworkTools: Add preliminary warning text for extended TCP statistics --- plugins/NetworkTools/main.c | 12 ++++++++++++ plugins/NetworkTools/tracert.c | 18 ++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 1ff9eaa5d195..cff7a526fb14 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -609,24 +609,36 @@ VOID UpdateNetworkNode( { if (Extension->NumberOfBytesIn) PhMoveReference(&Extension->BytesIn, PhFormatSize(Extension->NumberOfBytesIn, -1)); + + 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, -1)); + + 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; } diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index c69ccd659198..87a986b61e71 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -178,13 +178,8 @@ VOID TracertQueueHostLookup( &remoteCountryName )) { - if (Node->RemoteCountryCode) - PhDereferenceObject(Node->RemoteCountryCode); - if (Node->RemoteCountryName) - PhDereferenceObject(Node->RemoteCountryName); - - Node->RemoteCountryCode = remoteCountryCode; - Node->RemoteCountryName = remoteCountryName; + PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); + PhMoveReference(&Node->RemoteCountryName, remoteCountryName); } } else if (Context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) @@ -234,13 +229,8 @@ VOID TracertQueueHostLookup( &remoteCountryName )) { - if (Node->RemoteCountryCode) - PhDereferenceObject(Node->RemoteCountryCode); - if (Node->RemoteCountryName) - PhDereferenceObject(Node->RemoteCountryName); - - Node->RemoteCountryCode = remoteCountryCode; - Node->RemoteCountryName = remoteCountryName; + PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); + PhMoveReference(&Node->RemoteCountryName, remoteCountryName); } } } From e8e11bfdddb9881399e6fbd77cf97f2fd62c3095 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 07:24:54 +1100 Subject: [PATCH 626/839] Fix service error dialog #203 --- ProcessHacker/srvprp.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index c3edcfdb001f..2cdb40209fe3 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -185,7 +185,8 @@ static VOID PhpRefreshControls( EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), FALSE); } - if (PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_TYPE), L"Driver", 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); @@ -552,17 +553,9 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( goto Cleanup; ErrorCase: - if (PhShowMessage2( - hwndDlg, - TDCBF_RETRY_BUTTON | TDCBF_CANCEL_BUTTON, - TD_ERROR_ICON, - L"Unable to change service configuration.", - L"%s", - PH_AUTO_T(PH_STRING, PhGetWin32Message(GetLastError()))->Buffer - ) == IDRETRY) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } + + PhShowStatus(hwndDlg, L"Unable to change service configuration.", 0, GetLastError()); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); Cleanup: if (newServicePassword) From 63f295a13af3fe8e0b2f80f0a0aed577280d150e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 10:00:11 +1100 Subject: [PATCH 627/839] Add icons to services tab, Add filename to services tab, Add ServiceChangeNotification support (#203) --- ProcessHacker/include/srvlist.h | 3 +- ProcessHacker/include/srvprv.h | 6 +- ProcessHacker/srvlist.c | 60 +++-- ProcessHacker/srvprv.c | 457 +++++++++++++++++++++++--------- 4 files changed, 374 insertions(+), 152 deletions(-) diff --git a/ProcessHacker/include/srvlist.h b/ProcessHacker/include/srvlist.h index ea73a2824be3..9636369dd48d 100644 --- a/ProcessHacker/include/srvlist.h +++ b/ProcessHacker/include/srvlist.h @@ -19,8 +19,9 @@ #define PHSVTLC_KEYMODIFIEDTIME 10 #define PHSVTLC_VERIFICATIONSTATUS 11 #define PHSVTLC_VERIFIEDSIGNER 12 +#define PHSVTLC_FILENAME 13 -#define PHSVTLC_MAXIMUM 13 +#define PHSVTLC_MAXIMUM 14 #define PHSN_CONFIG 0x1 #define PHSN_DESCRIPTION 0x2 diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index ea4ca015cc4e..7a1034f557cd 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -18,6 +18,10 @@ 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; @@ -40,7 +44,7 @@ typedef struct _PH_SERVICE_ITEM BOOLEAN HasTriggers : 1; BOOLEAN PendingProcess : 1; BOOLEAN NeedsConfigUpdate : 1; - BOOLEAN NeedsVerifyUpdate : 1; + BOOLEAN JustProcessed : 1; BOOLEAN Spare : 3; }; }; diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index c1ee3fd12dfe..ee2cdc4ef3e6 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -77,9 +77,7 @@ 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 @@ -142,6 +140,7 @@ VOID PhInitializeServiceTreeList( PhAddTreeNewColumnEx(hwnd, PHSVTLC_KEYMODIFIEDTIME, FALSE, L"Key modified time", 140, PH_ALIGN_LEFT, -1, 0, TRUE); PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, -1, 0); PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_FILENAME, FALSE, L"File name", 100, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS); TreeNew_SetRedraw(hwnd, TRUE); @@ -300,9 +299,7 @@ VOID PhpRemoveServiceNode( PhClearReference(&ServiceNode->BinaryPath); PhClearReference(&ServiceNode->LoadOrderGroup); PhClearReference(&ServiceNode->Description); - PhClearReference(&ServiceNode->TooltipText); - PhClearReference(&ServiceNode->KeyModifiedTimeText); PhDereferenceObject(ServiceNode->ServiceItem); @@ -495,8 +492,8 @@ static VOID PhpUpdateServiceNodeKey( int sortResult = 0; #define END_SORT_FUNCTION \ - /* if (sortResult == 0) */ \ - /* sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); */ \ + if (sortResult == 0) \ + sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); \ \ return PhModifySort(sortResult, ServiceTreeListSortOrder); \ } @@ -525,24 +522,24 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(Type) { - sortResult = intcmp(serviceItem1->Type, serviceItem2->Type); + sortResult = uintcmp(serviceItem1->Type, serviceItem2->Type); } END_SORT_FUNCTION BEGIN_SORT_FUNCTION(Status) { - sortResult = intcmp(serviceItem1->State, serviceItem2->State); + sortResult = uintcmp(serviceItem1->State, serviceItem2->State); } END_SORT_FUNCTION BEGIN_SORT_FUNCTION(StartType) { - sortResult = intcmp(serviceItem1->StartType, serviceItem2->StartType); + sortResult = uintcmp(serviceItem1->StartType, serviceItem2->StartType); if (sortResult == 0) - sortResult = intcmp(serviceItem1->DelayedStart, serviceItem2->DelayedStart); + sortResult = uintcmp(serviceItem1->DelayedStart, serviceItem2->DelayedStart); if (sortResult == 0) - sortResult = intcmp(serviceItem1->HasTriggers, serviceItem2->HasTriggers); + sortResult = uintcmp(serviceItem1->HasTriggers, serviceItem2->HasTriggers); } END_SORT_FUNCTION @@ -562,7 +559,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(ErrorControl) { - sortResult = intcmp(serviceItem1->ErrorControl, serviceItem2->ErrorControl); + sortResult = uintcmp(serviceItem1->ErrorControl, serviceItem2->ErrorControl); } END_SORT_FUNCTION @@ -592,7 +589,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(VerificationStatus) { - sortResult = intcmp(serviceItem1->VerifyResult, serviceItem2->VerifyResult); + sortResult = uintcmp(serviceItem1->VerifyResult, serviceItem2->VerifyResult); } END_SORT_FUNCTION @@ -606,6 +603,16 @@ BEGIN_SORT_FUNCTION(VerifiedSigner) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(FileName) +{ + sortResult = PhCompareStringWithNull( + serviceItem1->FileName, + serviceItem2->FileName, + TRUE + ); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpServiceTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -641,7 +648,8 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( SORT_FUNCTION(Description), SORT_FUNCTION(KeyModifiedTime), SORT_FUNCTION(VerificationStatus), - SORT_FUNCTION(VerifiedSigner) + SORT_FUNCTION(VerifiedSigner), + SORT_FUNCTION(FileName) }; int (__cdecl *sortFunction)(const void *, const void *); @@ -761,6 +769,9 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( case PHSVTLC_VERIFIEDSIGNER: getCellText->Text = PhGetStringRef(serviceItem->VerifySignerName); break; + case PHSVTLC_FILENAME: + getCellText->Text = PhGetStringRef(serviceItem->FileName); + break; default: return FALSE; } @@ -776,25 +787,22 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( if (!ServiceIconsLoaded) { - ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PHAPPLICATION)); - ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); + HICON icon; + + PhGetStockApplicationIcon(&icon, NULL); + + ServiceApplicationIcon = icon; ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); - ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, 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; - } + if (node->ServiceItem->SmallIcon) + getNodeIcon->Icon = node->ServiceItem->SmallIcon; else { - if (node->ServiceItem->State == SERVICE_RUNNING) - getNodeIcon->Icon = ServiceApplicationGoIcon; + if (node->ServiceItem->Type == SERVICE_KERNEL_DRIVER || node->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) + getNodeIcon->Icon = ServiceCogIcon; else getNodeIcon->Icon = ServiceApplicationIcon; } diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 137c1f58a4a8..3c43a2c7d99c 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -32,6 +32,18 @@ #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; @@ -51,12 +63,46 @@ typedef struct _PHP_SERVICE_NOTIFY_CONTEXT { LIST_ENTRY ListEntry; SC_HANDLE ServiceHandle; - PPH_STRING ServiceName; // Valid only when adding - BOOLEAN IsServiceManager; + 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 @@ -85,6 +131,10 @@ VOID PhpInitializeServiceNonPoll( VOID ); +VOID PhpWorkaroundWindows10ServiceTypeBug( + _Inout_ LPENUM_SERVICE_STATUS_PROCESS ServieEntry + ); + PPH_OBJECT_TYPE PhServiceItemType; PPH_HASHTABLE PhServiceHashtable; PH_QUEUED_LOCK PhServiceHashtableLock = PH_QUEUED_LOCK_INIT; @@ -102,16 +152,10 @@ static HANDLE PhpNonPollEventHandle; static PH_QUEUED_LOCK PhpNonPollServiceListLock = PH_QUEUED_LOCK_INIT; static LIST_ENTRY PhpNonPollServiceListHead; static LIST_ENTRY PhpNonPollServicePendingListHead; -static SLIST_HEADER PhpServiceQueryListHead; +static SLIST_HEADER PhpServiceQueryDataListHead; -typedef struct _PH_SERVICE_QUERY_DATA -{ - SLIST_ENTRY ListEntry; - PPH_SERVICE_ITEM ServiceItem; - - VERIFY_RESULT VerifyResult; - PPH_STRING VerifySignerName; -} PH_SERVICE_QUERY_DATA, *PPH_SERVICE_QUERY_DATA; +static _SubscribeServiceChangeNotifications SubscribeServiceChangeNotifications_I; +static _UnsubscribeServiceChangeNotifications UnsubscribeServiceChangeNotifications_I; BOOLEAN PhServiceProviderInitialization( VOID @@ -125,7 +169,13 @@ BOOLEAN PhServiceProviderInitialization( 40 ); - RtlInitializeSListHead(&PhpServiceQueryListHead); + RtlInitializeSListHead(&PhpServiceQueryDataListHead); + + if (WindowsVersion > WINDOWS_7) + { + SubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"secHost.dll", "SubscribeServiceChangeNotifications"); + UnsubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"secHost.dll", "UnsubscribeServiceChangeNotifications"); + } return TRUE; } @@ -144,15 +194,6 @@ PPH_SERVICE_ITEM PhCreateServiceItem( if (Information) { - if (WindowsVersion >= WINDOWS_10_RS2) - { - // https://github.com/processhacker2/processhacker/issues/120 - if (Information->ServiceStatusProcess.dwServiceType == SERVICE_WIN32) - Information->ServiceStatusProcess.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; - if (Information->ServiceStatusProcess.dwServiceType == (SERVICE_WIN32 | SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE)) - Information->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; - } - serviceItem->Name = PhCreateString(Information->lpServiceName); serviceItem->Key = serviceItem->Name->sr; serviceItem->DisplayName = PhCreateString(Information->lpDisplayName); @@ -182,6 +223,11 @@ VOID PhpServiceItemDeleteProcedure( 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( @@ -247,22 +293,21 @@ PPH_SERVICE_ITEM PhReferenceServiceItem( return serviceItem; } -VOID PhMarkNeedsConfigUpdateServiceItem( - _In_ PPH_SERVICE_ITEM ServiceItem +VOID PhpResetServiceNonPollGate( + VOID ) { - ServiceItem->NeedsConfigUpdate = TRUE; - if (PhEnableServiceNonPoll) - PhpNonPollGate = 1; + InterlockedExchange(&PhpNonPollGate, 1); } -VOID PhpResetServiceNonPollGate( - VOID +VOID PhMarkNeedsConfigUpdateServiceItem( + _In_ PPH_SERVICE_ITEM ServiceItem ) { - if (PhEnableServiceNonPoll) - PhpNonPollGate = 1; + ServiceItem->NeedsConfigUpdate = TRUE; + + PhpResetServiceNonPollGate(); } VOID PhpRemoveServiceItem( @@ -464,61 +509,149 @@ static ULONG PhpHashServiceNameEntry( return PhHashStringRef(&Value->Name, TRUE); } -NTSTATUS PhpServiceQueryWorker( - _In_ PVOID Parameter +VOID PhpServiceQueryStage1( + _Inout_ PPH_SERVICE_QUERY_S1_DATA Data ) { - PPH_SERVICE_QUERY_DATA data = (PPH_SERVICE_QUERY_DATA)Parameter; + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; SC_HANDLE serviceHandle; - PPH_STRING serviceFileName = NULL; - if (serviceHandle = PhOpenService(data->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) { - serviceFileName = PhGetServiceRelevantFileName(&data->ServiceItem->Name->sr, serviceHandle); + Data->FileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle); CloseServiceHandle(serviceHandle); } - if (serviceFileName) + 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 +} + +VOID PhpServiceQueryStage2( + _Inout_ PPH_SERVICE_QUERY_S2_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; + + if (serviceItem->FileName) { - data->VerifyResult = PhVerifyFileCached( - serviceFileName, + Data->VerifyResult = PhVerifyFileCached( + serviceItem->FileName, NULL, - &data->VerifySignerName, + &Data->VerifySignerName, FALSE ); - - PhDereferenceObject(serviceFileName); } PhpResetServiceNonPollGate(); // HACK +} - RtlInterlockedPushEntrySList(&PhpServiceQueryListHead, &data->ListEntry); +NTSTATUS PhpServiceQueryStage1Worker( + _In_ PVOID Parameter + ) +{ + PPH_SERVICE_QUERY_S1_DATA data; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; + + data = PhAllocate(sizeof(PH_SERVICE_QUERY_S1_DATA)); + memset(data, 0, sizeof(PH_SERVICE_QUERY_S1_DATA)); + data->Header.Stage = 1; + data->Header.ServiceItem = serviceItem; + + PhpServiceQueryStage1(data); + + RtlInterlockedPushEntrySList(&PhpServiceQueryDataListHead, &data->Header.ListEntry); return STATUS_SUCCESS; } -VOID PhpQueueServiceQuery( +NTSTATUS PhpServiceQueryStage2Worker( + _In_ PVOID Parameter + ) +{ + PPH_SERVICE_QUERY_S2_DATA data; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; + + data = PhAllocate(sizeof(PH_SERVICE_QUERY_S2_DATA)); + memset(data, 0, 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 PhpQueueServiceQueryStage2( _In_ PPH_SERVICE_ITEM ServiceItem ) { - PPH_SERVICE_QUERY_DATA data; PH_WORK_QUEUE_ENVIRONMENT environment; if (!PhEnableServiceQueryStage2) return; - data = PhAllocate(sizeof(PH_SERVICE_QUERY_DATA)); - memset(data, 0, sizeof(PH_SERVICE_QUERY_DATA)); - data->ServiceItem = ServiceItem; - PhReferenceObject(ServiceItem); PhInitializeWorkQueueEnvironment(&environment); environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; - environment.IoPriority = IoPriorityLow; - environment.PagePriority = MEMORY_PRIORITY_LOW; + 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. + PhpQueueServiceQueryStage2(serviceItem); +} + +VOID PhpFillServiceItemStage2( + _In_ PPH_SERVICE_QUERY_S2_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; - PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryWorker, data, NULL, &environment); + serviceItem->VerifyResult = Data->VerifyResult; + serviceItem->VerifySignerName = Data->VerifySignerName; } VOID PhFlushServiceQueryData( @@ -528,20 +661,26 @@ VOID PhFlushServiceQueryData( PSLIST_ENTRY entry; PPH_SERVICE_QUERY_DATA data; - if (!RtlFirstEntrySList(&PhpServiceQueryListHead)) + if (!RtlFirstEntrySList(&PhpServiceQueryDataListHead)) return; - entry = RtlInterlockedFlushSList(&PhpServiceQueryListHead); + entry = RtlInterlockedFlushSList(&PhpServiceQueryDataListHead); while (entry) { data = CONTAINING_RECORD(entry, PH_SERVICE_QUERY_DATA, ListEntry); entry = entry->Next; - data->ServiceItem->VerifyResult = data->VerifyResult; - data->ServiceItem->VerifySignerName = data->VerifySignerName; + 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->NeedsVerifyUpdate = TRUE; + data->ServiceItem->JustProcessed = TRUE; PhDereferenceObject(data->ServiceItem); PhFree(data); @@ -554,12 +693,10 @@ VOID PhServiceProviderUpdate( { 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; @@ -622,6 +759,10 @@ VOID PhServiceProviderUpdate( 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), @@ -754,8 +895,23 @@ VOID PhServiceProviderUpdate( } } - // Queue for verification. - PhpQueueServiceQuery(serviceItem); + // 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_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); @@ -767,22 +923,13 @@ VOID PhServiceProviderUpdate( } else { - if (WindowsVersion >= WINDOWS_10_RS2) - { - // https://github.com/processhacker2/processhacker/issues/120 - if (serviceEntry->ServiceStatusProcess.dwServiceType == SERVICE_WIN32) - serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; - if (serviceEntry->ServiceStatusProcess.dwServiceType == (SERVICE_WIN32 | SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE)) - serviceEntry->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; - } - 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->NeedsVerifyUpdate + serviceItem->JustProcessed ) { PH_SERVICE_MODIFIED_DATA serviceModifiedData; @@ -876,8 +1023,8 @@ VOID PhServiceProviderUpdate( serviceItem->NeedsConfigUpdate = FALSE; } - if (serviceItem->NeedsVerifyUpdate) // HACK - serviceItem->NeedsVerifyUpdate = FALSE; + if (serviceItem->JustProcessed) // HACK + serviceItem->JustProcessed = FALSE; // Raise the service modified event. PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); @@ -897,12 +1044,12 @@ VOID CALLBACK PhpServiceNonPollScNotifyCallback( _In_ PVOID pParameter ) { - PSERVICE_NOTIFYW notifyBuffer = 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)) && + if ((notifyBuffer->dwNotificationTriggered & (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED)) && notifyBuffer->pszServiceNames) { PWSTR name; @@ -963,6 +1110,9 @@ 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); @@ -971,6 +1121,31 @@ VOID PhpDestroyServiceNotifyContext( PhFree(NotifyContext); } +VOID CALLBACK PhpServicePropertyChangeNotifyCallback( + _In_ ULONG ServiceNotifyFlags, + _In_opt_ PVOID Context + ) +{ + PPHP_SERVICE_NOTIFY_CONTEXT notifyContext = Context; + PPH_SERVICE_ITEM serviceItem; + + 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 ) @@ -1007,12 +1182,16 @@ NTSTATUS PhpServiceNonPollThreadStart( { SC_HANDLE serviceHandle; - if (serviceHandle = OpenService(scManagerHandle, services[i].lpServiceName, SERVICE_QUERY_STATUS)) + 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 = PhAllocate(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); memset(notifyContext, 0, sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); notifyContext->ServiceHandle = serviceHandle; notifyContext->State = SnNotify; + notifyContext->ServiceName = PhCreateString(services[i].lpServiceName); InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); } } @@ -1039,61 +1218,80 @@ NTSTATUS PhpServiceNonPollThreadStart( 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 = 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 SnAdding: { - 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; + notifyContext->ServiceHandle = + OpenService(scManagerHandle, notifyContext->ServiceName->Buffer, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + + if (!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 (SubscribeServiceChangeNotifications_I && !notifyContext->IsServiceManager) + { + PSC_NOTIFICATION_REGISTRATION serviceNotifyRegistration; + + if (SubscribeServiceChangeNotifications_I( + notifyContext->ServiceHandle, + SC_EVENT_PROPERTY_CHANGE, // TODO: SC_EVENT_STATUS_CHANGE + 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. + lagging = TRUE; + break; + case ERROR_SERVICE_MARKED_FOR_DELETE: + default: + RemoveEntryList(¬ifyContext->ListEntry); + PhpDestroyServiceNotifyContext(notifyContext); + break; + } + } break; } } @@ -1149,3 +1347,14 @@ VOID PhpInitializeServiceNonPoll( PhCreateThread2(PhpServiceNonPollThreadStart, NULL); } + +VOID PhpWorkaroundWindows10ServiceTypeBug( + _Inout_ LPENUM_SERVICE_STATUS_PROCESS ServieEntry + ) +{ + // https://github.com/processhacker2/processhacker/issues/120 + 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; +} From 7f39241515d08bf7574c97c5b32270548a34fc60 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 16 Dec 2017 22:12:13 +1100 Subject: [PATCH 628/839] Fix typo --- ProcessHacker/srvprv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 3c43a2c7d99c..b6d1caf0fabf 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -173,8 +173,8 @@ BOOLEAN PhServiceProviderInitialization( if (WindowsVersion > WINDOWS_7) { - SubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"secHost.dll", "SubscribeServiceChangeNotifications"); - UnsubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"secHost.dll", "UnsubscribeServiceChangeNotifications"); + SubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"sechost.dll", "SubscribeServiceChangeNotifications"); + UnsubscribeServiceChangeNotifications_I = PhGetModuleProcAddress(L"sechost.dll", "UnsubscribeServiceChangeNotifications"); } return TRUE; From 007ce0f63dd5698c127f0ee6b389c24159d508d7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 17 Dec 2017 06:20:28 +1100 Subject: [PATCH 629/839] Fix crash handling service property notifications --- ProcessHacker/srvprv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index b6d1caf0fabf..b6738b57cdc7 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -1129,6 +1129,11 @@ VOID CALLBACK PhpServicePropertyChangeNotifyCallback( 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. + if (ServiceNotifyFlags == SERVICE_NOTIFY_DELETED) + return; + if (notifyContext->JustAddedNotifyRegistration) { notifyContext->JustAddedNotifyRegistration = FALSE; From f527abf107124297aa2c39b276a7a4a5c4224671 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 17 Dec 2017 06:21:33 +1100 Subject: [PATCH 630/839] Fix service error message text --- ProcessHacker/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 868753e1df70..485f4a2ea2ef 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1924,7 +1924,7 @@ static VOID PhpShowErrorService( PhShowStatus( hWnd, PhaFormatString( - L"Unable to %s %s", + L"Unable to %s %s.", Verb, Service->Name->Buffer )->Buffer, From 6ad67a6153fd813ef3a31b5337ba60efc0f102a4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 17 Dec 2017 06:50:21 +1100 Subject: [PATCH 631/839] Allow partial runas dialog executable paths, Fix runas crash --- ProcessHacker/runas.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index e204caab1d10..592fd75192c2 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -386,10 +386,23 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (PhIsNullOrEmptyString(program)) break; - // Escape the path. (dmex: poor man's PathQuoteSpaces) - if (!PhStartsWithString2(program, L"\"", FALSE) && PhFindCharInString(program, 0, L' ') != -1) + if (RtlDoesFileExists_U(program->Buffer)) { - programEscaped = PhaConcatStrings(3, L"\"", PhGetString(program), L"\""); + // Escape the path. (dmex: poor man's PathQuoteSpaces) + if (!PhStartsWithString2(program, L"\"", FALSE) && PhFindCharInString(program, 0, L' ') != -1) + programEscaped = PhaConcatStrings(3, L"\"", PhGetString(program), L"\""); + else + programEscaped = program; + } + else + { + WCHAR buffer[MAX_PATH]; + + // The user typed a name without a path so attempt to locate the executable. + if (PhSearchFilePath(program->Buffer, L".exe", buffer)) + programEscaped = PhaConcatStrings(3, L"\"", buffer, L"\""); + else + programEscaped = NULL; } // Fix up the user name if it doesn't have a domain. @@ -441,7 +454,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( PhpSplitUserName(userName->Buffer, &domainPart, &userPart); memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); - createInfo.CommandLine = programEscaped->Buffer; + createInfo.CommandLine = PhGetString(programEscaped); createInfo.UserName = PhGetString(userPart); createInfo.DomainName = PhGetString(domainPart); createInfo.Password = PhGetStringOrEmpty(password); @@ -467,7 +480,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { status = PhExecuteRunAsCommand2( hwndDlg, - programEscaped->Buffer, + PhGetString(programEscaped), userName->Buffer, PhGetStringOrEmpty(password), logonType, From 685a8228cc8c2c97fda142290398527d2f50743a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 17 Dec 2017 07:18:08 +1100 Subject: [PATCH 632/839] Export PhSearchFilePath --- phlib/include/phutil.h | 9 +++ phlib/util.c | 121 ++++++++++++++++++++++------------------- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 8c04bc616f06..38027644c8e1 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1073,6 +1073,15 @@ PhParseCommandLineFuzzy( _Out_opt_ PPH_STRING *FullFileName ); +PHLIBAPI +BOOLEAN +NTAPI +PhSearchFilePath( + _In_ PWSTR FileName, + _In_opt_ PWSTR Extension, + _Out_writes_(MAX_PATH) PWSTR Buffer + ); + PHLIBAPI PPH_STRING NTAPI diff --git a/phlib/util.c b/phlib/util.c index 9471249823ca..d21a2fe255d5 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -4860,59 +4860,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. @@ -4984,7 +4931,7 @@ BOOLEAN PhParseCommandLineFuzzy( tempCommandLine = PhCreateString2(&commandLine); - if (PhpSearchFilePath(tempCommandLine->Buffer, L".exe", buffer)) + if (PhSearchFilePath(tempCommandLine->Buffer, L".exe", buffer)) { *FullFileName = PhCreateString(buffer); } @@ -5030,7 +4977,7 @@ BOOLEAN PhParseCommandLineFuzzy( *(remainingPart.Buffer - 1) = 0; } - result = PhpSearchFilePath(temp.Buffer, L".exe", buffer); + result = PhSearchFilePath(temp.Buffer, L".exe", buffer); if (found) { @@ -5065,6 +5012,60 @@ BOOLEAN PhParseCommandLineFuzzy( return FALSE; } +BOOLEAN PhSearchFilePath( + _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; +} + + PPH_STRING PhGetCacheDirectory( VOID ) @@ -5353,10 +5354,16 @@ BOOLEAN PhExtractIcon( _In_ HICON *IconSmall ) { + static PH_INITONCE initOnce = PH_INITONCE_INIT; static UINT (WINAPI *PrivateExtractIconExW)(PCWSTR, INT, HICON*, HICON*, UINT) = NULL; - if (!PrivateExtractIconExW) - PrivateExtractIconExW = PhGetModuleProcAddress(L"user32.dll", "PrivateExtractIconExW"); + if (PhBeginInitOnce(&initOnce)) + { + if (!PrivateExtractIconExW) + PrivateExtractIconExW = PhGetModuleProcAddress(L"user32.dll", "PrivateExtractIconExW"); + + PhEndInitOnce(&initOnce); + } if (!PrivateExtractIconExW) return FALSE; From 49a1d795226205c5cf25fac16450ccf4bc8e3e50 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 18 Dec 2017 02:56:42 +1100 Subject: [PATCH 633/839] Partial revert bfd5a9fd --- phlib/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/settings.c b/phlib/settings.c index 0ed937f79648..fd5ce5fd3e2f 100644 --- a/phlib/settings.c +++ b/phlib/settings.c @@ -83,7 +83,7 @@ VOID PhSettingsInitialization( PhIgnoredSettings = PhCreateList(4); PhAddDefaultSettings(); - //PhUpdateCachedSettings(); + PhUpdateCachedSettings(); } BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( From 0afaccf3d089a90189f736e216695c81020205e0 Mon Sep 17 00:00:00 2001 From: diversenok <30962924+diversenok@users.noreply.github.com> Date: Mon, 18 Dec 2017 03:02:23 +0300 Subject: [PATCH 634/839] Ampersands everywhere (#206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Ampersands for many menu items • New macro: PhCreateEMenuSeparator() • Separators creation standardization --- ProcessHacker/ProcessHacker.rc | 162 ++++++++++++++++---------------- ProcessHacker/appsup.c | 2 +- ProcessHacker/hndlmenu.c | 10 +- ProcessHacker/mainwnd.c | 24 ++--- ProcessHacker/memrslt.c | 2 +- ProcessHacker/mwpgproc.c | 16 ++-- ProcessHacker/plugman.c | 4 +- ProcessHacker/prpgmem.c | 6 +- ProcessHacker/prpgmod.c | 6 +- ProcessHacker/prpgwmi.c | 8 +- phlib/include/emenu.h | 5 + plugins/ExtendedServices/main.c | 16 ++-- plugins/ExtendedTools/iconext.c | 6 +- plugins/ExtendedTools/main.c | 8 +- plugins/HardwareDevices/main.c | 2 +- plugins/NetworkTools/main.c | 20 ++-- plugins/NetworkTools/tracert.c | 2 +- plugins/OnlineChecks/main.c | 26 ++--- plugins/ToolStatus/main.c | 12 +-- plugins/Updater/main.c | 2 +- plugins/UserNotes/main.c | 12 +-- plugins/WindowExplorer/main.c | 6 +- tools/peview/pdbprp.c | 2 +- 23 files changed, 182 insertions(+), 177 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 793a20ab1c10..a4ed4608a3c8 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -81,7 +81,7 @@ BEGIN POPUP "&Hacker" BEGIN MENUITEM "&Run...\aCtrl+R", ID_HACKER_RUN - MENUITEM "Run as administrator...", ID_HACKER_RUNASADMINISTRATOR + MENUITEM "Run as ad&ministrator...", 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 @@ -123,35 +123,35 @@ BEGIN 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 + 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 + 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 "&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 END POPUP "&Users" @@ -181,32 +181,32 @@ BEGIN MENUITEM "&Token", ID_THREAD_TOKEN POPUP "Analy&ze" BEGIN - MENUITEM "Wait", ID_ANALYZE_WAIT + 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 + 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" + 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 + 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" + 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 + 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 @@ -267,39 +267,39 @@ BEGIN 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 "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 + 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 + 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" + MENUITEM "&Detach from debugger", ID_MISCELLANEOUS_DETACHFROMDEBUGGER + MENUITEM "GDI &handles", ID_MISCELLANEOUS_GDIHANDLES + MENUITEM "&Inject DLL...", ID_MISCELLANEOUS_INJECTDLL + 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 + 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 @@ -307,15 +307,15 @@ BEGIN 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 "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 + MENUITEM "&Close", ID_WINDOW_CLOSE END MENUITEM SEPARATOR - MENUITEM "Search online\aCtrl+M", ID_PROCESS_SEARCHONLINE + 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 @@ -398,15 +398,15 @@ BEGIN MENUITEM "System &information", ID_ICON_SYSTEMINFORMATION POPUP "N&otifications" BEGIN - MENUITEM "Enable all", ID_NOTIFICATIONS_ENABLEALL - MENUITEM "Disable all", ID_NOTIFICATIONS_DISABLEALL + 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 + 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 @@ -422,7 +422,7 @@ BEGIN MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE MENUITEM SEPARATOR MENUITEM "R&estart", ID_COMPUTER_RESTART - MENUITEM "Restart to boot options", ID_COMPUTER_RESTARTBOOTOPTIONS + MENUITEM "Restart to boot &options", ID_COMPUTER_RESTARTBOOTOPTIONS MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID END @@ -459,11 +459,11 @@ IDR_EMPTYMEMLISTS MENU BEGIN POPUP "Empty" BEGIN - MENUITEM "Combine memory lists", ID_EMPTY_COMBINEMEMORYLISTS - 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 + MENUITEM "&Combine memory lists", ID_EMPTY_COMBINEMEMORYLISTS + 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 diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index dcf264aca322..caab2c547dcd 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1617,7 +1617,7 @@ VOID PhInitializeTreeNewColumnMenuEx( if (resetSortMenuItem) PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1); - PhInsertEMenuItem(Data->Menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1); + PhInsertEMenuItem(Data->Menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, -1); if (TreeNew_GetFixedColumn(Data->TreeNewHandle)) diff --git a/ProcessHacker/hndlmenu.c b/ProcessHacker/hndlmenu.c index 8b86873713e0..bb24b7938a41 100644 --- a/ProcessHacker/hndlmenu.c +++ b/ProcessHacker/hndlmenu.c @@ -48,25 +48,25 @@ VOID PhInsertHandleObjectPropertiesEMenuItems( 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); } } diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index fc0b3fef3eef..5df7a059dca7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2866,23 +2866,23 @@ VOID PhAddMiniProcessMenuItems( // Priority - priorityMenu = PhCreateEMenuItem(0, 0, L"Priority", NULL, ProcessId); + 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); + 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 - ioPriorityMenu = PhCreateEMenuItem(0, 0, L"I/O priority", NULL, ProcessId); + 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); + 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 diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index efe9d74ff961..f4b3140a86d9 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -633,7 +633,7 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( menu = PhCreateEMenu(); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MEMORY_READWRITEMEMORY, L"Read/Write memory", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), -1); GetCursorPos(&point); diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index acaa84fa7bc8..0a5edbf21902 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -113,10 +113,10 @@ BOOLEAN PhMwpProcessesPageCallback( 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; @@ -138,10 +138,10 @@ BOOLEAN PhMwpProcessesPageCallback( } } - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), startIndex + 4); - PhInsertEMenuItem(menu, menuItem = PhCreateEMenuItem(0, ID_VIEW_ORGANIZECOLUMNSETS, L"Organize column sets...", NULL, NULL), startIndex + 5); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SAVECOLUMNSET, L"Save column set...", NULL, NULL), startIndex + 6); - PhInsertEMenuItem(menu, columnSetMenuItem = PhCreateEMenuItem(0, 0, L"&Load column set", NULL, NULL), startIndex + 7); + 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. { diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index b05852f3bb21..70d9d552b4d6 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -732,9 +732,9 @@ INT_PTR CALLBACK PhpPluginsDlgProc( menu = PhCreateEMenu(); //PhInsertEMenuItem(menu, uninstallItem = PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_UNINSTALL, L"Uninstall", NULL, NULL), -1); - //PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + //PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_DISABLE, L"Disable", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_PROPERTIES, L"Properties", NULL, NULL), -1); //if (!PhGetOwnTokenAttributes().Elevated) diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index b835125aca20..c6ffe297f24d 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -639,14 +639,14 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( PhInsertEMenuItem(menu, freeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_FREE, L"Hide free pages", NULL, NULL), -1); PhInsertEMenuItem(menu, reservedItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_RESERVED, L"Hide reserved pages", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, privateItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_PRIVATE, L"Highlight private pages", NULL, NULL), -1); PhInsertEMenuItem(menu, systemItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_SYSTEM, L"Highlight system pages", NULL, NULL), -1); PhInsertEMenuItem(menu, cfgItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_CFG, L"Highlight CFG pages", NULL, NULL), -1); PhInsertEMenuItem(menu, typeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_EXECUTE, L"Highlight executable pages", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_READ_ADDRESS, L"Read/Write &address...", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_STRINGS, L"Strings...", NULL, NULL), -1); if (memoryContext->ListContext.HideFreeRegions) diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 9ab53e012387..496cfe371017 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -684,14 +684,14 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhInsertEMenuItem(menu, mappedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MAPPED_OPTION, L"Hide mapped", NULL, NULL), -1); PhInsertEMenuItem(menu, staticItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_STATIC_OPTION, L"Hide static", NULL, NULL), -1); PhInsertEMenuItem(menu, verifiedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_SIGNED_OPTION, L"Hide verified", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, dotnetItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION, L"Highlight .NET modules", NULL, NULL), -1); PhInsertEMenuItem(menu, immersiveItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION, L"Highlight immersive modules", NULL, NULL), -1); PhInsertEMenuItem(menu, relocatedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION, L"Highlight relocated modules", NULL, NULL), -1); PhInsertEMenuItem(menu, untrustedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION, L"Highlight untrusted modules", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MODULE_FLAGS_LOAD_MODULE_OPTION, L"Load module", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, stringsItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MODULE_STRINGS_OPTION, L"Strings...", NULL, NULL), -1); if (modulesContext->ListContext.HideDynamicModules) diff --git a/ProcessHacker/prpgwmi.c b/ProcessHacker/prpgwmi.c index d24f7c4d63ca..22d21aeaaac0 100644 --- a/ProcessHacker/prpgwmi.c +++ b/ProcessHacker/prpgwmi.c @@ -677,10 +677,10 @@ INT_PTR CALLBACK PhpProcessWmiProvidersDlgProc( menu = PhCreateEMenu(); if (PhGetIntegerSetting(L"WmiProviderEnableHiddenMenu")) { - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Suspend", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 2, L"Resume", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 3, L"Unload", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + 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, 5, L"&Copy", NULL, NULL), -1); diff --git a/phlib/include/emenu.h b/phlib/include/emenu.h index 95c39f355d64..f6ece558f6fa 100644 --- a/phlib/include/emenu.h +++ b/phlib/include/emenu.h @@ -58,6 +58,11 @@ VOID PhDestroyEMenuItem( _In_ PPH_EMENU_ITEM Item ); +/** +* Creates a menu separator. +*/ +#define PhCreateEMenuSeparator() PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL) + #define PH_EMENU_FIND_DESCEND 0x1 #define PH_EMENU_FIND_STARTSWITH 0x2 #define PH_EMENU_FIND_LITERAL 0x4 diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index a84cdc669ae4..343d328d02db 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -154,7 +154,7 @@ VOID NTAPI ProcessMenuInitializingCallback( // * There are no extra submenus. if (serviceList->Count != 1) { - servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Services", NULL); + servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Ser&vices", NULL); } // Create and add a menu item for each service. @@ -174,7 +174,7 @@ VOID NTAPI ProcessMenuInitializingCallback( if (serviceList->Count == 1) { // "Service (Xxx)" - escapedName = PhaFormatString(L"Service (%s)", escapedName->Buffer); + escapedName = PhaFormatString(L"Ser&vice (%s)", escapedName->Buffer); } serviceMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, escapedName->Buffer, NULL); @@ -185,11 +185,11 @@ VOID NTAPI ProcessMenuInitializingCallback( 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); + PhInsertEMenuItem(serviceMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_GOTOSERVICE, L"&Go to service", serviceItem), -1); + PhInsertEMenuItem(serviceMenuItem, startMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_START, L"Sta&rt", 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"St&op", serviceItem), -1); // Massive copy and paste from mainwnd.c. // == START == @@ -383,7 +383,7 @@ VOID NTAPI ServiceMenuInitializingCallback( PhInsertEMenuItem( menuInfo->Menu, - PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"Restart", menuInfo->u.Service.Services[0]), + PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"R&estart", menuInfo->u.Service.Services[0]), indexOfMenuItem + 1 ); } diff --git a/plugins/ExtendedTools/iconext.c b/plugins/ExtendedTools/iconext.c index e125bf364e64..b073696f3ef4 100644 --- a/plugins/ExtendedTools/iconext.c +++ b/plugins/ExtendedTools/iconext.c @@ -85,7 +85,7 @@ VOID EtRegisterNotifyIcons( PluginInstance, GPU_ICON_ID, NULL, - L"GPU history", + L"&GPU history", PH_NF_ICON_SHOW_MINIINFO | (EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), &data ); @@ -96,7 +96,7 @@ VOID EtRegisterNotifyIcons( PluginInstance, DISK_ICON_ID, NULL, - L"Disk history", + L"&Disk history", PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), &data ); @@ -107,7 +107,7 @@ VOID EtRegisterNotifyIcons( PluginInstance, NETWORK_ICON_ID, NULL, - L"Network history", + L"&Network history", PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), &data ); diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index fe84e010489f..f552223ac6de 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -181,8 +181,8 @@ VOID NTAPI ProcessMenuInitializingCallback( 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); + 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); } } @@ -207,7 +207,7 @@ VOID NTAPI ThreadMenuInitializingCallback( insertIndex = 0; PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_CANCELIO, - L"Cancel I/O", threadItem), insertIndex); + L"Ca&ncel I/O", threadItem), insertIndex); if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; } @@ -250,7 +250,7 @@ VOID NTAPI ModuleMenuInitializingCallback( ModuleProcessId = menuInfo->u.Module.ProcessId; PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_MODULE_SERVICES, - L"Services", moduleItem), insertIndex); + L"Ser&vices", moduleItem), insertIndex); if (!moduleItem) menuItem->Flags |= PH_EMENU_DISABLED; } diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index 19366f4c7515..379add20b60c 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -241,7 +241,7 @@ VOID ShowDeviceMenu( 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, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Properties", NULL, NULL), -1); selectedItem = PhShowEMenu( diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index cff7a526fb14..b40be0fb3dd3 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -260,12 +260,12 @@ VOID NTAPI MainMenuInitializingCallback( 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); + 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, PhCreateEMenuSeparator(), -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); } @@ -285,10 +285,10 @@ VOID NTAPI NetworkMenuInitializingCallback( 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); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), 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) { diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 87a986b61e71..a0683dc89945 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -700,7 +700,7 @@ INT_PTR CALLBACK TracertDlgProc( 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); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MENU_ACTION_COPY, L"Copy", NULL, NULL), -1); PhInsertCopyCellEMenuItem(menu, MENU_ACTION_COPY, context->TreeNewHandle, contextMenuEvent->Column); diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index c199c8070458..09caed0e07a7 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -260,10 +260,10 @@ VOID NTAPI MainMenuInitializingCallback( 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); + 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, PhCreateEMenuSeparator(), -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); @@ -281,19 +281,19 @@ PPH_EMENU_ITEM CreateSendToMenu( 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); + sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d 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 (ProcessesMenu && (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); + PhInsertEMenuItem(Parent, PhCreateEMenuSeparator(), insertIndex + 2); } else { - PhInsertEMenuItem(Parent, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); + PhInsertEMenuItem(Parent, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(Parent, sendToMenu, -1); } @@ -359,10 +359,10 @@ VOID NTAPI ServiceMenuInitializingCallback( else serviceItem = NULL; - sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Send to", NULL); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"virustotal.com", serviceItem ? serviceItem : NULL), -1); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", serviceItem ? serviceItem : NULL), -1); - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); + sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"&virustotal.com", serviceItem ? serviceItem : NULL), -1); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.&jotti.org", serviceItem ? serviceItem : NULL), -1); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menuInfo->Menu, sendToMenu, -1); if (!serviceItem) diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index ae24130f3e41..9ca760f3e568 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -211,7 +211,7 @@ VOID ShowCustomizeMenu( 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, PhCreateEMenuSeparator(), -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); @@ -806,7 +806,7 @@ LRESULT CALLBACK MainWndSubclassProc( if (buttonInfo.fsStyle == BTNS_SEP) { // Add separators to menu. - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); } else { @@ -847,10 +847,10 @@ LRESULT CALLBACK MainWndSubclassProc( // 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, PhCreateEMenuSeparator(), -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, PhCreateEMenuSeparator(), -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); @@ -975,10 +975,10 @@ LRESULT CALLBACK MainWndSubclassProc( 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, PhCreateEMenuSeparator(), -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, PhCreateEMenuSeparator(), -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); diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index 9d9e795e2607..251a6987195e 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -52,7 +52,7 @@ VOID NTAPI MainMenuInitializingCallback( if (menuInfo->u.MainMenu.SubMenuIndex != 4) return; - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, UPDATE_MENUITEM, L"Check for updates", NULL), 0); + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, UPDATE_MENUITEM, L"Check for &updates", NULL), 0); } VOID NTAPI MenuItemCallback( diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 06df65c71879..69c0ca077f65 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -912,7 +912,7 @@ VOID AddSavePriorityMenuItemsAndHook( //PhRemoveEMenuItem(affinityMenuItem, affinityMenuItem, 0); // Insert standard menu-items - PhInsertEMenuItem(affinityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); + PhInsertEMenuItem(affinityMenuItem, PhCreateEMenuSeparator(), -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); @@ -932,7 +932,7 @@ VOID AddSavePriorityMenuItemsAndHook( // Priority if (priorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Priority", 0)) { - PhInsertEMenuItem(priorityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); + PhInsertEMenuItem(priorityMenuItem, PhCreateEMenuSeparator(), -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); @@ -952,7 +952,7 @@ VOID AddSavePriorityMenuItemsAndHook( // 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, PhCreateEMenuSeparator(), -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); @@ -1001,9 +1001,9 @@ VOID ProcessMenuInitializingCallback( 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); + 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(); diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index e6a4aadfa86f..ad0658fd76d9 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -143,13 +143,13 @@ VOID NTAPI MainMenuInitializingCallback( else insertIndex = 0; - PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_WINDOWS, L"Windows", NULL), insertIndex); + 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); + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_DESKTOPWINDOWS, L"Deskto&p Windows...", NULL), insertIndex); } } @@ -194,7 +194,7 @@ VOID NTAPI ThreadMenuInitializingCallback( insertIndex = 0; PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_WINDOWS, - L"Windows", threadItem), insertIndex); + L"&Windows", threadItem), insertIndex); if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; } diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index 438436d75740..fb8bc689a367 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -78,7 +78,7 @@ VOID PhInitializeTreeNewColumnMenuEx( if (resetSortMenuItem) PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1); - PhInsertEMenuItem(Data->Menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1); + PhInsertEMenuItem(Data->Menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, -1); if (TreeNew_GetFixedColumn(Data->TreeNewHandle)) From 74735873a1b02f32bb7836d0c3898c1aa44bba7f Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 18 Dec 2017 11:08:57 +1100 Subject: [PATCH 635/839] Fix runas service regression from 2cfb6835 --- phlib/util.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/phlib/util.c b/phlib/util.c index d21a2fe255d5..bf4185367d1b 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2588,6 +2588,19 @@ NTSTATUS PhCreateProcessWin32Ex( PhMoveReference(&fileName, PhCreateString(cmdlineArgList[0])); LocalFree(cmdlineArgList); } + + if (!RtlDoesFileExists_U(fileName->Buffer)) + { + WCHAR buffer[MAX_PATH]; + + // The user typed a name without a path so attempt to locate the executable. + if (PhSearchFilePath(fileName->Buffer, L".exe", buffer)) + PhMoveReference(&fileName, PhCreateString(buffer)); + else + fileName = NULL; + } + else + fileName = NULL; } newFlags = 0; From 89849dcfe58d1b3ce62ab87d19da0c7b79fa623f Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 18 Dec 2017 11:09:38 +1100 Subject: [PATCH 636/839] Fix PhFindEMenuItem crash for seperators #205 --- phlib/util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phlib/util.c b/phlib/util.c index bf4185367d1b..f7162474085f 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1105,6 +1105,9 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( { WCHAR t; + if (!A || !B) + return -1; + if (!IgnoreCase) { while (TRUE) From 01d1e3aa03bbe925623f30d8213e64e878a9f64e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 18 Dec 2017 11:16:25 +1100 Subject: [PATCH 637/839] Move emenu type --- phlib/include/emenu.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/phlib/include/emenu.h b/phlib/include/emenu.h index f6ece558f6fa..8bab5f575b8a 100644 --- a/phlib/include/emenu.h +++ b/phlib/include/emenu.h @@ -58,11 +58,6 @@ VOID PhDestroyEMenuItem( _In_ PPH_EMENU_ITEM Item ); -/** -* Creates a menu separator. -*/ -#define PhCreateEMenuSeparator() PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL) - #define PH_EMENU_FIND_DESCEND 0x1 #define PH_EMENU_FIND_STARTSWITH 0x2 #define PH_EMENU_FIND_LITERAL 0x4 @@ -181,6 +176,14 @@ PPH_EMENU_ITEM PhShowEMenu( // Convenience functions +FORCEINLINE +PPH_EMENU_ITEM PhCreateEMenuSeparator( + VOID + ) +{ + return PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL); +} + PHLIBAPI BOOLEAN PhSetFlagsEMenuItem( _Inout_ PPH_EMENU_ITEM Item, From 7a481ef6ee98d6ec36b94aab1416fbabbf09e0c6 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 18 Dec 2017 23:58:56 +1100 Subject: [PATCH 638/839] Fix spacing --- ProcessHacker/memlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index 87b45fcbdd9a..2bad4c6b43bc 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -974,7 +974,7 @@ VOID PhGetSelectedMemoryNodes( PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; if (node->Node.Selected) - PhAddItemArray(&array, &node); + PhAddItemArray(&array, &node); } *NumberOfMemoryNodes = (ULONG)array.Count; From 61dba75f72503e009d2d6fe95cad2e8e7865c3ee Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 19 Dec 2017 03:32:20 +1100 Subject: [PATCH 639/839] Add PhMainWndEarlyExit --- ProcessHacker/mainwnd.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 5df7a059dca7..0d1e77077371 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -60,6 +60,7 @@ PHAPPAPI HWND PhMainWndHandle; BOOLEAN PhMainWndExiting = FALSE; +BOOLEAN PhMainWndEarlyExit = FALSE; HMENU PhMainWndMenuHandle; PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration; @@ -493,6 +494,8 @@ VOID PhMwpOnDestroy( VOID ) { + PhMainWndExiting = TRUE; + PhSetIntegerSetting(L"MainWindowTabRestoreIndex", TabCtrl_GetCurSel(TabControlHandle)); // Notify pages and plugins that we are shutting down. @@ -502,8 +505,8 @@ VOID PhMwpOnDestroy( if (PhPluginsEnabled) PhUnloadPlugins(); - if (!PhMainWndExiting) - ProcessHacker_SaveAllSettings(PhMainWndHandle); + if (!PhMainWndEarlyExit) + PhMwpSaveSettings(); PhNfUninitialization(); @@ -1836,7 +1839,7 @@ ULONG_PTR PhMwpOnUserMessage( { case WM_PH_ACTIVATE: { - if (!PhMainWndExiting) + if (!PhMainWndEarlyExit && !PhMainWndExiting) { if (WParam != 0) { @@ -1882,12 +1885,12 @@ ULONG_PTR PhMwpOnUserMessage( case WM_PH_PREPARE_FOR_EARLY_SHUTDOWN: { PhMwpSaveSettings(); - PhMainWndExiting = TRUE; + PhMainWndEarlyExit = TRUE; } break; case WM_PH_CANCEL_EARLY_SHUTDOWN: { - PhMainWndExiting = FALSE; + PhMainWndEarlyExit = FALSE; } break; case WM_PH_DELAYED_LOAD_COMPLETED: From 02ab65119e916fafda12184df440304c5dcdf40e Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 07:42:26 +1100 Subject: [PATCH 640/839] Fix crash --- phlib/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index f7162474085f..a78c8142c484 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2592,7 +2592,7 @@ NTSTATUS PhCreateProcessWin32Ex( LocalFree(cmdlineArgList); } - if (!RtlDoesFileExists_U(fileName->Buffer)) + if (fileName && !RtlDoesFileExists_U(fileName->Buffer)) { WCHAR buffer[MAX_PATH]; @@ -2600,10 +2600,8 @@ NTSTATUS PhCreateProcessWin32Ex( if (PhSearchFilePath(fileName->Buffer, L".exe", buffer)) PhMoveReference(&fileName, PhCreateString(buffer)); else - fileName = NULL; + PhClearReference(&fileName); } - else - fileName = NULL; } newFlags = 0; From fe73013a1139f97d6cf8779c57f6e3faf3ddb084 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 07:46:18 +1100 Subject: [PATCH 641/839] Add LdrFindResource_U/LdrEnumResources --- phnt/include/ntldr.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 190afe199733..f8f086c352ce 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -583,6 +583,50 @@ LdrAccessResource( _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 + +NTSTATUS +NTAPI +LdrFindResource_U( + _In_ PVOID BaseAddress, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry + ); + +typedef struct _LDR_ENUM_RESOURCE_INFO +{ + ULONG_PTR Type; + ULONG_PTR Name; + ULONG_PTR Language; + PVOID Data; + SIZE_T Size; + ULONG_PTR Reserved; +} LDR_ENUM_RESOURCE_INFO, *PLDR_ENUM_RESOURCE_INFO; + +#define NAME_FROM_RESOURCE_ENTRY(RootDirectory, Entry) \ + ((Entry)->NameIsString ? (ULONG_PTR)PTR_ADD_OFFSET((RootDirectory), (Entry)->NameOffset) : (Entry)->Id) + +NTSTATUS +NTAPI +LdrEnumResources( + _In_ PVOID BaseAddress, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Inout_ ULONG *ResourceCount, + _Out_writes_to_(*ResourceCount,*ResourceCount) LDR_ENUM_RESOURCE_INFO *Resources + ); + NTSYSAPI NTSTATUS NTAPI From 28bf431a477d6cd446b427bdb63b58de3bc258f7 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 08:09:00 +1100 Subject: [PATCH 642/839] Update PhLoadResource --- phlib/util.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index a78c8142c484..17f0186cc211 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5237,22 +5237,19 @@ BOOLEAN PhLoadResource( _Out_ PVOID *ResourceBuffer ) { + LDR_RESOURCE_INFO resourceInfo; + PIMAGE_RESOURCE_DATA_ENTRY resourceData; ULONG resourceLength; - PVOID resourceInfo; PVOID resourceBuffer; - resourceInfo = FindResource(DllBase, Name, Type); + resourceInfo.Type = (ULONG_PTR)Type; + resourceInfo.Name = (ULONG_PTR)Name; + resourceInfo.Language = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); - if (!resourceInfo) + if (!NT_SUCCESS(LdrFindResource_U(DllBase, &resourceInfo, RESOURCE_DATA_LEVEL, &resourceData))) return FALSE; - if (!NT_SUCCESS(LdrAccessResource(DllBase, resourceInfo, &resourceBuffer, &resourceLength))) - return FALSE; - - if (!resourceBuffer) - return FALSE; - - if (resourceLength == 0) + if (!NT_SUCCESS(LdrAccessResource(DllBase, resourceData, &resourceBuffer, &resourceLength))) return FALSE; if (ResourceLength) From c3def7ef99cc818b36acf281b889a11462f219d5 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 08:09:55 +1100 Subject: [PATCH 643/839] Fix PS_SYSTEM_DLL_INIT_BLOCK version check --- ProcessHacker/memprv.c | 2 +- ProcessHacker/mtgndlg.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 0b38809c1d1e..747b9a8b9135 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -494,7 +494,7 @@ NTSTATUS PhpUpdateMemoryRegionTypes( PVOID cfgBitmapAddress = NULL; PVOID cfgBitmapWow64Address = NULL; - if (ldrInitBlock.Size >= UFIELD_OFFSET(PS_SYSTEM_DLL_INIT_BLOCK, Wow64CfgBitMap)) + if (RTL_CONTAINS_FIELD(&ldrInitBlock, ldrInitBlock.Size, Wow64CfgBitMap)) { cfgBitmapAddress = (PVOID)ldrInitBlock.CfgBitMap; cfgBitmapWow64Address = (PVOID)ldrInitBlock.Wow64CfgBitMap; diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index 80317341f046..a9c5c8ef7ed6 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -222,7 +222,7 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); } - if (context->SystemDllInitBlock) + if (context->SystemDllInitBlock && RTL_CONTAINS_FIELD(context->SystemDllInitBlock, context->SystemDllInitBlock->Size, MitigationOptionsMap)) { if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_ON) { From 39dc233eb6e59177b53bb195c2f9910342077518 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 08:10:44 +1100 Subject: [PATCH 644/839] Fix wmi provider tab not using user-configured browse executable --- ProcessHacker/prpgwmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/prpgwmi.c b/ProcessHacker/prpgwmi.c index 22d21aeaaac0..24ca81fd52d7 100644 --- a/ProcessHacker/prpgwmi.c +++ b/ProcessHacker/prpgwmi.c @@ -716,7 +716,13 @@ INT_PTR CALLBACK PhpProcessWmiProvidersDlgProc( { if (!PhIsNullOrEmptyString(entry->FileName) && RtlDoesFileExists_U(entry->FileName->Buffer)) { - PhShellExploreFile(hwndDlg, entry->FileName->Buffer); + PhShellExecuteUserString( + hwndDlg, + L"FileBrowseExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); } } break; From d32be6cc691c3449aadf97a59d6c9bcfef0987dd Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 20 Dec 2017 21:37:35 +1100 Subject: [PATCH 645/839] Fix LdrEnumResources rs3 symbols --- phnt/include/ntldr.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index f8f086c352ce..a2cf3000b477 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -604,15 +604,23 @@ LdrFindResource_U( _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry ); -typedef struct _LDR_ENUM_RESOURCE_INFO +// private +typedef struct _LDR_ENUM_RESOURCE_ENTRY { - ULONG_PTR Type; - ULONG_PTR Name; - ULONG_PTR Language; + union + { + ULONG_PTR NameOrId; + PIMAGE_RESOURCE_DIRECTORY_STRING Name; + struct + { + USHORT Id; + USHORT NameIsPresent; + }; + } Path[3]; PVOID Data; - SIZE_T Size; - ULONG_PTR Reserved; -} LDR_ENUM_RESOURCE_INFO, *PLDR_ENUM_RESOURCE_INFO; + 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) @@ -624,7 +632,7 @@ LdrEnumResources( _In_ PLDR_RESOURCE_INFO ResourceInfo, _In_ ULONG Level, _Inout_ ULONG *ResourceCount, - _Out_writes_to_(*ResourceCount,*ResourceCount) LDR_ENUM_RESOURCE_INFO *Resources + _Out_writes_to_opt_(*ResourceCount, *ResourceCount) PLDR_ENUM_RESOURCE_ENTRY Resources ); NTSYSAPI From 22665ad456f42b5cb920d81000f883338887844d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 21 Dec 2017 12:12:20 +1100 Subject: [PATCH 646/839] Fix kph build errors --- phnt/include/ntldr.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index a2cf3000b477..2951d7d7fab4 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -677,6 +677,8 @@ 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 @@ -731,4 +733,6 @@ LdrQueryImageFileExecutionOptions( _Out_opt_ PULONG RetunedLength ); +#endif // (PHNT_MODE != PHNT_MODE_KERNEL) + #endif From 3aa5a55c05d475b768d6f2e18a1d7c8aa4b7e17d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 21 Dec 2017 12:49:23 +1100 Subject: [PATCH 647/839] Fix types --- phnt/include/ntldr.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 2951d7d7fab4..d03cd284e4e0 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -595,6 +595,7 @@ typedef struct _LDR_RESOURCE_INFO #define RESOURCE_LANGUAGE_LEVEL 2 #define RESOURCE_DATA_LEVEL 3 +NTSYSAPI NTSTATUS NTAPI LdrFindResource_U( @@ -604,6 +605,16 @@ LdrFindResource_U( _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry ); +NTSYSAPI +NTSTATUS +NTAPI +LdrFindResourceDirectory_U( + _In_ PVOID BaseAddress, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Out_ PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory + ); + // private typedef struct _LDR_ENUM_RESOURCE_ENTRY { @@ -625,6 +636,7 @@ typedef struct _LDR_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( From 32af4dfe9432b36f8b63c70667f2bd103f7cb03b Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 21 Dec 2017 12:57:24 +1100 Subject: [PATCH 648/839] Add PhGetMappedImageResources --- phlib/include/mapimg.h | 27 ++++++++ phlib/mapimg.c | 140 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 1 deletion(-) diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index 8590b1b50f67..88cdb031ab60 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -454,6 +454,33 @@ PhGetMappedImageCfgEntry( _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 + ); + #ifdef __cplusplus } #endif diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 1ddb46aa6510..24523ff12cc9 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1496,4 +1496,142 @@ NTSTATUS PhGetMappedImageCfgEntry( } return STATUS_SUCCESS; -} \ No newline at end of file +} + +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) + 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) + 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) + 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 = PTR_ADD_OFFSET(MappedImage->ViewBase, resourceData->OffsetToData); + Resources->ResourceEntries[resourceIndex++].Size = resourceData->Size; + } + } + } + +CleanupExit: + return status; +} From e11ff90b70659d600556b1d90f8e8af0aa0ce59c Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 21 Dec 2017 12:57:50 +1100 Subject: [PATCH 649/839] Fix space --- phlib/mapimg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 24523ff12cc9..5a5ddb846056 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1635,3 +1635,4 @@ NTSTATUS PhGetMappedImageResources( CleanupExit: return status; } + \ No newline at end of file From fa734e51c062962965d99a954ab3f3ee0b5d06d9 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 21 Dec 2017 13:00:52 +1100 Subject: [PATCH 650/839] peview: Add module resources tab --- tools/peview/include/peview.h | 7 + tools/peview/peprp.c | 11 ++ tools/peview/peview.rc | 16 +++ tools/peview/peview.vcxproj | 1 + tools/peview/peview.vcxproj.filters | 3 + tools/peview/resource.h | 2 +- tools/peview/resprp.c | 209 ++++++++++++++++++++++++++++ tools/peview/settings.c | 5 +- 8 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 tools/peview/resprp.c diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index b3cc6ccdab5d..dc3f53bc9512 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -315,4 +315,11 @@ INT_PTR CALLBACK PvpPeCgfDlgProc( _In_ LPARAM lParam ); +INT_PTR CALLBACK PvpPeResourcesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + #endif diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index c13e90a75d9d..56a469d4f2bd 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -129,6 +129,17 @@ VOID PvPeProperties( 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 && diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index c9dd19af9d33..785a4906fba3 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -116,6 +116,14 @@ BEGIN TOPMARGIN, 3 BOTTOMMARGIN, 277 END + + IDD_PERESOURCES, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END END #endif // APSTUDIO_INVOKED @@ -221,6 +229,14 @@ BEGIN 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 + ///////////////////////////////////////////////////////////////////////////// // diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 9ea3c2dce967..ca02ad300e64 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -222,6 +222,7 @@ + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index d81436dc0011..977007a3472b 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -60,6 +60,9 @@ Source Files + + Source Files + diff --git a/tools/peview/resource.h b/tools/peview/resource.h index 010f9d43fd37..29c9488f2611 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -10,6 +10,7 @@ #define IDD_PELOADCONFIG 106 #define IDD_PECFG 107 #define IDD_PESYMBOLS 108 +#define IDD_PERESOURCES 109 #define IDB_SEARCH_ACTIVE 110 #define IDB_SEARCH_INACTIVE 111 #define IDB_SEARCH_ACTIVE_BMP 112 @@ -35,7 +36,6 @@ #define IDC_SYMSEARCH 1017 #define IDC_NAME 1019 #define IDC_COMPANYNAME_LINK 1020 -#define IDC_BUTTON1 1021 #define IDC_STOP 1021 #define IDC_PROGRESS 1022 diff --git a/tools/peview/resprp.c b/tools/peview/resprp.c new file mode 100644 index 000000000000..0016ecc90fbc --- /dev/null +++ b/tools/peview/resprp.c @@ -0,0 +1,209 @@ +/* + * 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 + +PWSTR PvpGetResourceTypeString(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"; + } + + return L"ERROR"; +} + +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: + { + NTSTATUS status; + 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, 80, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 150, L"Type"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Language"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Size"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageResourcesListViewColumns", lvHandle); + + status = PhGetMappedImageResources(&resources, &PvMappedImage); + + if (NT_SUCCESS(status)) + { + for (i = 0; i < resources.NumberOfEntries; i++) + { + PPH_STRING string; + WCHAR number[PH_INT32_STR_LEN_1]; + + entry = resources.ResourceEntries[i]; + + PhPrintUInt64(number, ++count); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + if (IS_INTRESOURCE(entry.Name)) + { + PhPrintUInt32(number, (ULONG)entry.Name); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, 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, 1, string->Buffer); + PhDereferenceObject(string); + } + + if (IS_INTRESOURCE(entry.Type)) + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, 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, 2, string->Buffer); + PhDereferenceObject(string); + } + + if (IS_INTRESOURCE(entry.Language)) + { + WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; + + PhPrintUInt32(number, (ULONG)entry.Language); + + if (LCIDToLocaleName((ULONG)entry.Language, localeName, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES)) + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, PhaFormatString(L"%s (%s)", number, localeName)->Buffer); + else + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, 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, 3, string->Buffer); + PhDereferenceObject(string); + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, 4, PhaFormatSize(entry.Size, -1)->Buffer); + } + + if (resources.ResourceEntries) + PhFree(resources.ResourceEntries); + } + else + { + PhShowStatus(hwndDlg, L"Unable to enumerate module resources.", status, 0); + } + + 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; + } + + return FALSE; +} diff --git a/tools/peview/settings.c b/tools/peview/settings.c index 224075b64973..dc97ffcf2492 100644 --- a/tools/peview/settings.c +++ b/tools/peview/settings.c @@ -35,10 +35,11 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"MainWindowPage", L"General"); PhpAddIntegerPairSetting(L"MainWindowPosition", L"150,150"); PhpAddScalableIntegerPairSetting(L"MainWindowSize", L"@96|550,580"); - PhpAddStringSetting(L"ImageCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); PhpAddStringSetting(L"ImageExportsListViewColumns", L""); PhpAddStringSetting(L"ImageImportsListViewColumns", L""); - PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageResourcesListViewColumns", L""); PhpAddStringSetting(L"LibListViewColumns", L""); PhpAddStringSetting(L"PdbTreeListColumns", L""); } From 633cbebd54918d6ffd315086689b42d4f08c102d Mon Sep 17 00:00:00 2001 From: lucasg Date: Fri, 22 Dec 2017 09:54:17 +0100 Subject: [PATCH 651/839] Tagging Apiset memory page (#210) --- ProcessHacker/include/memprv.h | 3 ++- ProcessHacker/memlist.c | 2 ++ ProcessHacker/memprv.c | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/include/memprv.h b/ProcessHacker/include/memprv.h index dcd62a55fbea..1a1fc8914b56 100644 --- a/ProcessHacker/include/memprv.h +++ b/ProcessHacker/include/memprv.h @@ -22,7 +22,8 @@ typedef enum _PH_MEMORY_REGION_TYPE HeapSegmentRegion, HeapSegment32Region, CfgBitmapRegion, - CfgBitmap32Region + CfgBitmap32Region, + ApiSetMapRegion, } PH_MEMORY_REGION_TYPE; typedef struct _PH_MEMORY_ITEM diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index 2bad4c6b43bc..a182eaaee435 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -462,6 +462,8 @@ PPH_STRING PhGetMemoryRegionUseText( case CfgBitmap32Region: return PhFormatString(L"CFG Bitmap%s", type == CfgBitmap32Region ? L" 32-bit" : L""); + case ApiSetMapRegion: + return PhFormatString(L"API Set schema"); default: return PhReferenceEmptyString(); } diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 747b9a8b9135..14f8e49e1ca2 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -534,6 +534,31 @@ NTSTATUS PhpUpdateMemoryRegionTypes( } #endif + + // ApiSet schema map + { + PVOID peb; + PVOID apiSetMap; + PROCESS_BASIC_INFORMATION basicInfo; + + + if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) + { + peb = basicInfo.PebBaseAddress; + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb, FIELD_OFFSET(PEB, ApiSetMap)), + &apiSetMap, + sizeof(PVOID), + NULL + ))) + { + PhpSetMemoryRegionType(List, apiSetMap, TRUE, ApiSetMapRegion); + } + } + } + PhFree(processes); return STATUS_SUCCESS; From 5e091f2a8563b8eaf48d71659ebc1480a4d77737 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 23 Dec 2017 16:21:13 +1100 Subject: [PATCH 652/839] Fix ApiSetMap tagging for 32bit processes --- ProcessHacker/memlist.c | 2 +- ProcessHacker/memprv.c | 51 +++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index a182eaaee435..7eec2d32af0c 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -463,7 +463,7 @@ PPH_STRING PhGetMemoryRegionUseText( return PhFormatString(L"CFG Bitmap%s", type == CfgBitmap32Region ? L" 32-bit" : L""); case ApiSetMapRegion: - return PhFormatString(L"API Set schema"); + return PhFormatString(L"ApiSetMap"); default: return PhReferenceEmptyString(); } diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 14f8e49e1ca2..26a01daaf4c9 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -275,11 +275,13 @@ NTSTATUS PhpUpdateMemoryRegionTypes( 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) @@ -308,6 +310,18 @@ NTSTATUS PhpUpdateMemoryRegionTypes( 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 @@ -338,6 +352,18 @@ NTSTATUS PhpUpdateMemoryRegionTypes( 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 } @@ -534,31 +560,6 @@ NTSTATUS PhpUpdateMemoryRegionTypes( } #endif - - // ApiSet schema map - { - PVOID peb; - PVOID apiSetMap; - PROCESS_BASIC_INFORMATION basicInfo; - - - if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) - { - peb = basicInfo.PebBaseAddress; - - if (NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb, FIELD_OFFSET(PEB, ApiSetMap)), - &apiSetMap, - sizeof(PVOID), - NULL - ))) - { - PhpSetMemoryRegionType(List, apiSetMap, TRUE, ApiSetMapRegion); - } - } - } - PhFree(processes); return STATUS_SUCCESS; From d453dd676d3671ae6e899df2ab6cc35539e9bfba Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 23 Dec 2017 17:12:17 +1100 Subject: [PATCH 653/839] Fix find handles regex type filter --- ProcessHacker/findobj.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 1e06baf50a0d..adf2b7a9471e 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -769,15 +769,10 @@ static BOOLEAN MatchTypeString( _In_ PPH_STRINGREF Input ) { - if (Context->SearchRegexCompiledExpression && Context->SearchRegexMatchData) + if (PhEqualString2(Context->SearchTypeString, L"Everything", FALSE)) return TRUE; - else - { - if (PhEqualString2(Context->SearchTypeString, L"Everything", FALSE)) - return TRUE; - return PhFindStringInStringRef(Input, &Context->SearchTypeString->sr, TRUE) != -1; - } + return PhFindStringInStringRef(Input, &Context->SearchTypeString->sr, TRUE) != -1; } typedef struct _SEARCH_HANDLE_CONTEXT From aa98c2b6a65eab5c5cd8a7ddb969251719e5ca7b Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 24 Dec 2017 03:46:59 +1100 Subject: [PATCH 654/839] Improve find handles window layout --- ProcessHacker/ProcessHacker.rc | 9 ++++----- ProcessHacker/findobj.c | 8 +++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index a4ed4608a3c8..a42887027506 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -725,11 +725,10 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS CAPTION "Find Handles or DLLs" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - LTEXT "Filter:",IDC_STATIC,7,7,19,8 - EDITTEXT IDC_FILTER,29,4,134,13,ES_AUTOHSCROLL - PUSHBUTTON "Find",IDOK,301,3,50,14 - CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,6,34,10 - COMBOBOX IDC_FILTERTYPE,165,4,93,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + 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 diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index adf2b7a9471e..9b531b0fb34e 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -1120,15 +1120,15 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( context->WindowHandle = hwndDlg; context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_TREELIST); - PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), L"Search Handles or DLLs"); + PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), L"Find Handles or DLLs"); PhpPopulateObjectTypes(GetDlgItem(hwndDlg, IDC_FILTERTYPE)); InitializeHandleObjectTree(context); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, 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); @@ -1160,10 +1160,8 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( ); } - Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_FILTER), TRUE); Edit_SetSel(GetDlgItem(hwndDlg, IDC_FILTER), 0, -1); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); } break; case WM_DESTROY: From 232579a75074ef3f0a59ef65b66b36a3c093fed7 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Wed, 27 Dec 2017 10:55:28 +0100 Subject: [PATCH 655/839] Sort advanced options by name initially (#213) --- ProcessHacker/options.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 31ef37cd061c..115b54a70252 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1532,6 +1532,7 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( PhSetExtendedListView(listviewHandle); PhEnumSettings(PhpOptionsSettingsCallback, listviewHandle); + ExtendedListView_SortItems(listviewHandle); } break; case WM_DESTROY: From 49753ad8cd649006ec0ae0a5e7a6d436d6cf43aa Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 27 Dec 2017 22:40:26 +1100 Subject: [PATCH 656/839] Revert 6ad67a6 --- ProcessHacker/runas.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 592fd75192c2..bc53241c45c5 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -370,7 +370,6 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { NTSTATUS status; PPH_STRING program; - PPH_STRING programEscaped; PPH_STRING userName; PPH_STRING password; PPH_STRING logonTypeString; @@ -386,25 +385,6 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (PhIsNullOrEmptyString(program)) break; - if (RtlDoesFileExists_U(program->Buffer)) - { - // Escape the path. (dmex: poor man's PathQuoteSpaces) - if (!PhStartsWithString2(program, L"\"", FALSE) && PhFindCharInString(program, 0, L' ') != -1) - programEscaped = PhaConcatStrings(3, L"\"", PhGetString(program), L"\""); - else - programEscaped = program; - } - else - { - WCHAR buffer[MAX_PATH]; - - // The user typed a name without a path so attempt to locate the executable. - if (PhSearchFilePath(program->Buffer, L".exe", buffer)) - programEscaped = PhaConcatStrings(3, L"\"", buffer, L"\""); - else - programEscaped = NULL; - } - // Fix up the user name if it doesn't have a domain. if (PhFindCharInString(userName, 0, '\\') == -1) { @@ -454,7 +434,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( PhpSplitUserName(userName->Buffer, &domainPart, &userPart); memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); - createInfo.CommandLine = PhGetString(programEscaped); + createInfo.CommandLine = PhGetString(program); createInfo.UserName = PhGetString(userPart); createInfo.DomainName = PhGetString(domainPart); createInfo.Password = PhGetStringOrEmpty(password); @@ -480,7 +460,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { status = PhExecuteRunAsCommand2( hwndDlg, - PhGetString(programEscaped), + PhGetString(program), userName->Buffer, PhGetStringOrEmpty(password), logonType, From 0e201784d4fe38e6b0a6c8a7fd7ed0bf62b9488e Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Wed, 27 Dec 2017 19:07:51 +0100 Subject: [PATCH 657/839] Add aggregation support to more performance counters (#212) Fixes #99 --- ProcessHacker/proctree.c | 215 ++++++++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 58 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index ceee54151cad..edaabb63e496 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2152,8 +2152,12 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getCellText->Text = node->PeakWorkingSetText->sr; break; case PHPRTLC_PRIVATEWS: - PhMoveReference(&node->PrivateWsText, PhFormatSize(processItem->WorkingSetPrivateSize, -1)); - getCellText->Text = node->PrivateWsText->sr; + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, WorkingSetPrivateSize), &value); + PhMoveReference(&node->PrivateWsText, PhFormatSize(value, -1)); + getCellText->Text = node->PrivateWsText->sr; + } break; case PHPRTLC_SHAREDWS: PhpUpdateProcessNodeWsCounters(node); @@ -2166,16 +2170,24 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getCellText->Text = node->ShareableWsText->sr; break; case PHPRTLC_VIRTUALSIZE: - PhMoveReference(&node->VirtualSizeText, PhFormatSize(processItem->VmCounters.VirtualSize, -1)); - getCellText->Text = node->VirtualSizeText->sr; + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.VirtualSize), &value); + PhMoveReference(&node->VirtualSizeText, PhFormatSize(value, -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; + { + 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); @@ -2202,12 +2214,22 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( } break; case PHPRTLC_GDIHANDLES: - PhpUpdateProcessNodeGdiUserHandles(node); - PhpFormatInt32GroupDigits(node->GdiHandles, node->GdiHandlesText, sizeof(node->GdiHandlesText), &getCellText->Text); + { + 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); - PhpFormatInt32GroupDigits(node->UserHandles, node->UserHandlesText, sizeof(node->UserHandlesText), &getCellText->Text); + { + 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: { @@ -2413,87 +2435,148 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_CONTEXTSWITCHES: - if (processItem->ContextSwitchesDelta.Value != 0) { - PhMoveReference(&node->ContextSwitchesText, PhFormatUInt64(processItem->ContextSwitchesDelta.Value, TRUE)); - getCellText->Text = node->ContextSwitchesText->sr; + 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 + 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; + 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: - if (processItem->PageFaultsDelta.Delta != 0) { - PhMoveReference(&node->PageFaultsDeltaText, PhFormatUInt64(processItem->PageFaultsDelta.Delta, TRUE)); - getCellText->Text = node->PageFaultsDeltaText->sr; + 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: - if (processItem->IoReadCountDelta.Value != 0) { - PhMoveReference(&node->IoGroupText[0], PhFormatUInt64(processItem->IoReadCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[0]->sr; + 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: - if (processItem->IoWriteCountDelta.Value != 0) { - PhMoveReference(&node->IoGroupText[1], PhFormatUInt64(processItem->IoWriteCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[1]->sr; + 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: - if (processItem->IoOtherCountDelta.Value != 0) { - PhMoveReference(&node->IoGroupText[2], PhFormatUInt64(processItem->IoOtherCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[2]->sr; + 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: - if (processItem->IoReadDelta.Value != 0) { - PhMoveReference(&node->IoGroupText[3], PhFormatSize(processItem->IoReadDelta.Value, -1)); - getCellText->Text = node->IoGroupText[3]->sr; + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[3], PhFormatSize(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; + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[4], PhFormatSize(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; + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[5], PhFormatSize(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; + 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: - if (processItem->IoWriteCountDelta.Delta != 0) { - PhMoveReference(&node->IoGroupText[7], PhFormatUInt64(processItem->IoWriteCountDelta.Delta, TRUE)); - getCellText->Text = node->IoGroupText[7]->sr; + 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: - if (processItem->IoOtherCountDelta.Delta != 0) { - PhMoveReference(&node->IoGroupText[8], PhFormatUInt64(processItem->IoOtherCountDelta.Delta, TRUE)); - getCellText->Text = node->IoGroupText[8]->sr; + 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: @@ -2521,36 +2604,52 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( } break; case PHPRTLC_PAGEDPOOL: - PhMoveReference(&node->PagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPagedPoolUsage, -1)); - getCellText->Text = node->PagedPoolText->sr; + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPagedPoolUsage), &value); + PhMoveReference(&node->PagedPoolText, PhFormatSize(value, -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; + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaNonPagedPoolUsage), &value); + PhMoveReference(&node->NonPagedPoolText, PhFormatSize(value, -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; + { + PhpUpdateProcessNodeQuotaLimits(node); + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MinimumWorkingSetSize), &value); + PhMoveReference(&node->MinimumWorkingSetText, PhFormatSize(value, -1)); + getCellText->Text = node->MinimumWorkingSetText->sr; + } break; case PHPRTLC_MAXIMUMWORKINGSET: - PhpUpdateProcessNodeQuotaLimits(node); - PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(node->MaximumWorkingSetSize, -1)); - getCellText->Text = node->MaximumWorkingSetText->sr; + { + PhpUpdateProcessNodeQuotaLimits(node); + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MaximumWorkingSetSize), &value); + PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(value, -1)); + getCellText->Text = node->MaximumWorkingSetText->sr; + } break; case PHPRTLC_PRIVATEBYTESDELTA: { - LONG_PTR delta; + LONG_PTR delta = 0; - delta = processItem->PrivateBytesDelta.Delta; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PrivateBytesDelta.Delta), &delta); if (delta != 0) { From 7835a1e80e5081777371b813db07ff7d286ae75a Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Wed, 27 Dec 2017 22:00:59 +0100 Subject: [PATCH 658/839] Alternative dbghelp.dll path (#214) Sometimes Windbg is installed in "Program Files" (instead of "Program Files (x86)"). --- ProcessHacker/appsup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index caab2c547dcd..9b389494655f 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1019,6 +1019,7 @@ PPH_STRING PhFindDbghelpPath( { #ifdef _WIN64 { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll" }, + { CSIDL_PROGRAM_FILES, 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" } From 7a066005ea3401a4a3181110c4a55d6452e047f8 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 1 Jan 2018 16:21:06 +1100 Subject: [PATCH 659/839] peview: update resource column order --- tools/peview/resprp.c | 80 +++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/tools/peview/resprp.c b/tools/peview/resprp.c index 0016ecc90fbc..59ac129adfae 100644 --- a/tools/peview/resprp.c +++ b/tools/peview/resprp.c @@ -22,7 +22,9 @@ #include -PWSTR PvpGetResourceTypeString(ULONG_PTR Type) +static PWSTR PvpGetResourceTypeString( + _In_ ULONG_PTR Type + ) { switch (Type) { @@ -63,6 +65,15 @@ PWSTR PvpGetResourceTypeString(ULONG_PTR Type) return L"ERROR"; } +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; + INT_PTR CALLBACK PvpPeResourcesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -80,7 +91,6 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( { case WM_INITDIALOG: { - NTSTATUS status; HWND lvHandle; PH_MAPPED_IMAGE_RESOURCES resources; PH_IMAGE_RESOURCE_ENTRY entry; @@ -92,20 +102,18 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( PhSetListViewStyle(lvHandle, TRUE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Name"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 150, L"Type"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Language"); - PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Size"); + 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"); PhSetExtendedListView(lvHandle); PhLoadListViewColumnsFromSetting(L"ImageResourcesListViewColumns", lvHandle); - status = PhGetMappedImageResources(&resources, &PvMappedImage); - - if (NT_SUCCESS(status)) + if (NT_SUCCESS(PhGetMappedImageResources(&resources, &PvMappedImage))) { for (i = 0; i < resources.NumberOfEntries; i++) { - PPH_STRING string; + PVOID string; WCHAR number[PH_INT32_STR_LEN_1]; entry = resources.ResourceEntries[i]; @@ -113,62 +121,60 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( PhPrintUInt64(number, ++count); lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); - if (IS_INTRESOURCE(entry.Name)) + if (IS_INTRESOURCE(entry.Type)) { - PhPrintUInt32(number, (ULONG)entry.Name); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, number); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, PvpGetResourceTypeString(entry.Type)); } else { - PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Name; + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Type; + + string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, string->Buffer); - PhDereferenceObject(string); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, string); + PhFree(string); } - if (IS_INTRESOURCE(entry.Type)) + if (IS_INTRESOURCE(entry.Name)) { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PvpGetResourceTypeString(entry.Type)); + 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.Type; + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Name; - string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, string->Buffer); - PhDereferenceObject(string); + string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_NAME, string); + PhFree(string); } if (IS_INTRESOURCE(entry.Language)) { - WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; + WCHAR name[LOCALE_NAME_MAX_LENGTH]; PhPrintUInt32(number, (ULONG)entry.Language); - if (LCIDToLocaleName((ULONG)entry.Language, localeName, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES)) - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, PhaFormatString(L"%s (%s)", number, localeName)->Buffer); + if (LCIDToLocaleName((ULONG)entry.Language, name, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES)) + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, PhaFormatString(L"%s (%s)", number, name)->Buffer); else - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, number); + 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, 3, string->Buffer); - PhDereferenceObject(string); + string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, string); + PhFree(string); } - PhSetListViewSubItem(lvHandle, lvItemIndex, 4, PhaFormatSize(entry.Size, -1)->Buffer); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_SIZE, PhaFormatSize(entry.Size, -1)->Buffer); } - if (resources.ResourceEntries) - PhFree(resources.ResourceEntries); - } - else - { - PhShowStatus(hwndDlg, L"Unable to enumerate module resources.", status, 0); + PhFree(resources.ResourceEntries); } ExtendedListView_SortItems(lvHandle); From 6d475b8617aa9f622b5886da712b227cbbdbab96 Mon Sep 17 00:00:00 2001 From: David Krutsko Date: Sat, 6 Jan 2018 10:22:25 -0500 Subject: [PATCH 660/839] Added new system information classes (#219) --- phnt/include/ntexapi.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 033d7963f31a..f39ecbd000a0 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1393,6 +1393,8 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemProcessorIdleMaskInformation, // since REDSTONE3 SystemSecureDumpEncryptionInformation, SystemWriteConstraintInformation, // SYSTEM_WRITE_CONSTRAINT_INFORMATION + SystemKernelVaShadowInformation, // SYSTEM_KERNEL_VA_SHADOW_INFORMATION + SystemSpeculationControlInformation = 201, // SYSTEM_SPECULATION_CONTROL_INFORMATION MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; @@ -3078,6 +3080,44 @@ typedef struct _SYSTEM_WRITE_CONSTRAINT_INFORMATION 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 Reserved : 28; + }; + }; +} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, *PSYSTEM_KERNEL_VA_SHADOW_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 Reserved : 24; + }; + }; +} SYSTEM_SPECULATION_CONTROL_INFORMATION, *PSYSTEM_SPECULATION_CONTROL_INFORMATION; + #if (PHNT_MODE != PHNT_MODE_KERNEL) NTSYSCALLAPI From f6e08a704886aa5a71c4d8d37c315a703c284ebb Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 03:14:16 +1100 Subject: [PATCH 661/839] ELF image support for WSL process modules (#220) --- phlib/include/exlf.h | 420 ++++++++++++++++++++++ phlib/include/mapimg.h | 264 +++++++++----- phlib/mapexlf.c | 517 ++++++++++++++++++++++++++++ phlib/mapimg.c | 70 +++- phlib/phlib.vcxproj | 2 + phlib/phlib.vcxproj.filters | 6 + tools/peview/exlfexports.c | 126 +++++++ tools/peview/exlfimports.c | 122 +++++++ tools/peview/exlfprp.c | 390 +++++++++++++++++++++ tools/peview/include/peview.h | 37 ++ tools/peview/main.c | 34 +- tools/peview/peprp.c | 16 +- tools/peview/peview.rc | 25 ++ tools/peview/peview.vcxproj | 5 + tools/peview/peview.vcxproj.filters | 58 ++-- tools/peview/resource.h | 3 + tools/peview/settings.c | 3 + 17 files changed, 1968 insertions(+), 130 deletions(-) create mode 100644 phlib/include/exlf.h create mode 100644 phlib/mapexlf.c create mode 100644 tools/peview/exlfexports.c create mode 100644 tools/peview/exlfimports.c create mode 100644 tools/peview/exlfprp.c diff --git a/phlib/include/exlf.h b/phlib/include/exlf.h new file mode 100644 index 000000000000..736b88ee0c2a --- /dev/null +++ b/phlib/include/exlf.h @@ -0,0 +1,420 @@ +#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_ENCODING 32 +#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_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 +#define STB_GLOBAL 1 +#define STB_WEAK 2 + +#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/mapimg.h b/phlib/include/mapimg.h index 88cdb031ab60..95f9a90faf45 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -5,19 +5,38 @@ extern "C" { #endif +#include + typedef struct _PH_MAPPED_IMAGE { + USHORT Signature; PVOID ViewBase; SIZE_T Size; - union { - PIMAGE_NT_HEADERS32 NtHeaders32; - PIMAGE_NT_HEADERS NtHeaders; + 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; + }; + }; }; - - ULONG NumberOfSections; - PIMAGE_SECTION_HEADER Sections; - USHORT Magic; } PH_MAPPED_IMAGE, *PPH_MAPPED_IMAGE; PHLIBAPI @@ -39,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 @@ -285,6 +314,101 @@ 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 + ); + // maplib struct _PH_MAPPED_ARCHIVE; @@ -386,99 +510,61 @@ 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; }; }; - - 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 + UCHAR TypeInfo; + ULONGLONG Address; + ULONGLONG Size; + WCHAR Name[0x80]; + WCHAR Module[0x80]; +} PH_ELF_IMAGE_SYMBOL_ENTRY, *PPH_ELF_IMAGE_SYMBOL_ENTRY; + +BOOLEAN PhGetMappedWslImageSymbols( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *ImageSymbols ); -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 +VOID PhFreeMappedWslImageSymbols( + _In_ PPH_LIST ImageSymbols ); #ifdef __cplusplus diff --git a/phlib/mapexlf.c b/phlib/mapexlf.c new file mode 100644 index 000000000000..796d517f9e82 --- /dev/null +++ b/phlib/mapexlf.c @@ -0,0 +1,517 @@ +/* + * 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 (strcmp(Name, PTR_ADD_OFFSET(stringTable, section->sh_name)) == 0) + { + 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; +} + +// TODO: Check this is actually correct. +// https://stackoverflow.com/questions/18296276/base-address-of-elf +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 = PhAllocate(sizeof(PH_ELF_VERSION_RECORD)); + memset(versionInfo, 0, 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; +} + +// TODO: Optimize this function. +BOOLEAN PhGetMappedWslImageSymbols( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *ImageSymbols + ) +{ + PELF64_IMAGE_SECTION_HEADER section; + PPH_LIST symbols = PhCreateList(2000); + + if (section = PhGetMappedWslImageSectionByType(MappedWslImage, SHT_SYMTAB)) + { + // TODO: Need to find a WSL binary with a symbol table. + // SHT_SYMTAB should be parsed idential to SHT_DYNSYM? + } + + if (section = PhGetMappedWslImageSectionByType(MappedWslImage, SHT_DYNSYM)) + { + ULONGLONG count; + ULONGLONG i; + PELF_IMAGE_SYMBOL_ENTRY entry; + PVOID stringTable; + PELF_VERSION_TABLE versionTable; + PPH_LIST versionRecords; + + 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); + + versionTable = PhGetMappedWslImageSectionDataByType(MappedWslImage, SHT_SUNW_versym); + versionRecords = PhpParseMappedWslImageVersionRecords(MappedWslImage, stringTable); + + for (i = 1; i < count; i++) + { + if (entry[i].st_shndx == SHN_UNDEF) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY import; + + import = PhAllocate(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + memset(import, 0, sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + + import->ImportSymbol = TRUE; + import->Address = entry[i].st_value; + import->Size = entry[i].st_size; + import->TypeInfo = entry[i].st_info; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[i].st_name), + -1, + import->Name, + sizeof(import->Name), + NULL + ); + + // import library name + if (versionTable && versionRecords) + { + PSTR moduleName; + + if (moduleName = PhpFindWslImageVersionRecordName(versionRecords, versionTable[i].vs_vers)) + { + PhCopyStringZFromBytes( + moduleName, + -1, + import->Module, + sizeof(import->Module), + NULL + ); + } + } + + PhAddItemList(symbols, import); + } + else if (entry[i].st_shndx != SHN_UNDEF && entry[i].st_value != 0) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY export; + + if (ELF_ST_TYPE(entry[i].st_info) == STT_SECTION) // Ignore section symbol types. + continue; + + export = PhAllocate(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + memset(export, 0, sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + + export->ExportSymbol = TRUE; + export->Address = entry[i].st_value; + export->Size = entry[i].st_size; + export->TypeInfo = entry[i].st_info; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[i].st_name), + -1, + export->Name, + sizeof(export->Name), + NULL + ); + + PhAddItemList(symbols, export); + } + else + { + PPH_ELF_IMAGE_SYMBOL_ENTRY export; + + export = PhAllocate(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + memset(export, 0, sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + + export->UnknownSymbol = TRUE; + export->Address = entry[i].st_value; + export->Size = entry[i].st_size; + export->TypeInfo = entry[i].st_info; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[i].st_name), + -1, + export->Name, + sizeof(export->Name), + NULL + ); + + PhAddItemList(symbols, export); + } + } + + if (versionRecords) + PhpFreeMappedWslImageVersionRecords(versionRecords); + } + + *ImageSymbols = symbols; + + return TRUE; +} + +VOID PhFreeMappedWslImageSymbols( + _In_ PPH_LIST ImageSymbols + ) +{ + for (ULONG i = 0; i < ImageSymbols->Count; i++) + PhFree(ImageSymbols->Items[i]); + + PhDereferenceObject(ImageSymbols); +} \ No newline at end of file diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 5a5ddb846056..5f9086de1dd7 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -130,21 +130,23 @@ NTSTATUS PhLoadMappedImage( ) { NTSTATUS status; + PVOID viewBase; + SIZE_T size; status = PhMapViewOfEntireFile( FileName, FileHandle, ReadOnly, - &MappedImage->ViewBase, - &MappedImage->Size + &viewBase, + &size ); if (NT_SUCCESS(status)) { status = PhInitializeMappedImage( MappedImage, - MappedImage->ViewBase, - MappedImage->Size + viewBase, + size ); if (!NT_SUCCESS(status)) @@ -156,6 +158,66 @@ NTSTATUS PhLoadMappedImage( 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)) + { + PUSHORT imageHeaderSignature = viewBase; + + __try + { + PhProbeAddress(imageHeaderSignature, sizeof(USHORT), viewBase, size, 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + MappedImage->Signature = *imageHeaderSignature; + + switch (MappedImage->Signature) + { + case IMAGE_DOS_SIGNATURE: + { + status = PhInitializeMappedImage( + MappedImage, + viewBase, + size + ); + } + break; + case IMAGE_ELF_SIGNATURE: + { + status = PhInitializeMappedWslImage( + MappedImage, + viewBase, + size + ); + } + break; + } + } + + return status; +} + NTSTATUS PhUnloadMappedImage( _Inout_ PPH_MAPPED_IMAGE MappedImage ) diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index 8582f9290f38..b8ae87a725c6 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -176,6 +176,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index 2ebb7ab5f4b3..088326bcfc68 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -218,6 +218,9 @@ Json-C + + Source Files + @@ -460,5 +463,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/tools/peview/exlfexports.c b/tools/peview/exlfexports.c new file mode 100644 index 000000000000..80088eed1d04 --- /dev/null +++ b/tools/peview/exlfexports.c @@ -0,0 +1,126 @@ +/* + * 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; + + PhPrintUInt64(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, -1)->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolTypeName(export->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 5, PvpGetSymbolBindingName(export->TypeInfo)); + } + + 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, 250, L"Size"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Type"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Binding"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageExportsListViewColumns", lvHandle); + + PvpProcessElfExports(lvHandle); + 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; + } + + return FALSE; +} diff --git a/tools/peview/exlfimports.c b/tools/peview/exlfimports.c new file mode 100644 index 000000000000..d8f4df98ec89 --- /dev/null +++ b/tools/peview/exlfimports.c @@ -0,0 +1,122 @@ +/* + * 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, PhaFormatSize(import->Size, -1)->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolTypeName(import->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 5, PvpGetSymbolBindingName(import->TypeInfo)); + } + + 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"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageImportsListViewColumns", lvHandle); + + PvpProcessElfImports(lvHandle); + ExtendedListView_SortItems(lvHandle); + + 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: + { + PvHandleListViewNotifyForCopy(lParam, 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..3dca74aca3e8 --- /dev/null +++ b/tools/peview/exlfprp.c @@ -0,0 +1,390 @@ +/* + * 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 +#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"; + } + + return L"***ERROR***"; +} + +VOID PvExlfProperties( + VOID + ) +{ + PPV_PROPCONTEXT propContext; + + if (propContext = PvCreatePropContext(PvFileName)) + { + PPV_PROPPAGECONTEXT newPage; + + // General + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_ELFGENERAL), + PvpExlfGeneralDlgProc, + 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); + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } +} + +VOID PvpSetWslImageType( + _In_ HWND hwndDlg + ) +{ + PWSTR type = L"N/A"; + + switch (PvMappedImage.Header->e_type) + { + case ET_DYN: + type = L"Dynamic"; + break; + case ET_EXEC: + type = L"Executable"; + break; + default: + type = L"ERROR"; + break; + } + + SetDlgItemText(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; + } + + SetDlgItemText(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)); + SetDlgItemText(hwndDlg, IDC_IMAGEBASE, string->Buffer); + PhDereferenceObject(string); + } + else if (PvMappedImage.Header->e_ident[EI_CLASS] == ELFCLASS64) + { + string = PhFormatString(L"0x%I64x", PhGetMappedWslImageBaseAddress(&PvMappedImage)); + SetDlgItemText(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); + SetDlgItemText(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); + SetDlgItemText(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, -1)->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); + + 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_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/include/peview.h b/tools/peview/include/peview.h index dc3f53bc9512..912c73869622 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -58,6 +58,12 @@ VOID PvLibProperties( VOID ); +// exlfprp + +VOID PvExlfProperties( + VOID + ); + // misc PPH_STRING PvResolveShortcutTarget( @@ -322,4 +328,35 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( _In_ LPARAM lParam ); +// ELF + +PWSTR PvpGetSymbolTypeName( + _In_ UCHAR TypeInfo + ); + +PWSTR PvpGetSymbolBindingName( + _In_ UCHAR TypeInfo + ); + +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 + ); + #endif diff --git a/tools/peview/main.c b/tools/peview/main.c index 2338bf6d2a59..a0a3e54ef6a5 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -88,7 +88,7 @@ INT WINAPI wWinMain( CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFilter(fileDialog, filters, ARRAYSIZE(filters)); if (PhShowFileDialog(NULL, fileDialog)) { @@ -116,7 +116,37 @@ INT WINAPI wWinMain( else if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) PvPdbProperties(); else - PvPeProperties(); + { + NTSTATUS status; + + status = PhLoadMappedImageEx( + PvFileName->Buffer, + NULL, + TRUE, + &PvMappedImage + ); + + 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); + else + PhShowStatus(NULL, L"Unable to load the file.", status, 0); + } PeSaveSettings(); diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 56a469d4f2bd..265271a3a3ce 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -54,23 +54,11 @@ 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 (PvpLoadDbgHelp(&PvSymbolProvider)) { // Load current PE pdb @@ -145,7 +133,7 @@ VOID PvPeProperties( entry->VirtualAddress && (PvImageCor20Header = PhMappedImageRvaToVa(&PvMappedImage, entry->VirtualAddress, NULL))) { - status = STATUS_SUCCESS; + NTSTATUS status = STATUS_SUCCESS; __try { @@ -199,8 +187,6 @@ VOID PvPeProperties( PhDereferenceObject(propContext); } - - PhUnloadMappedImage(&PvMappedImage); } static NTSTATUS CheckSumImageThreadStart( diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 785a4906fba3..c762a3799c3a 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -124,6 +124,14 @@ BEGIN TOPMARGIN, 3 BOTTOMMARGIN, 277 END + + IDD_ELFGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 273 + END END #endif // APSTUDIO_INVOKED @@ -237,6 +245,23 @@ 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,7,53,8 + LTEXT "Static",IDC_TARGETMACHINE,78,7,215,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,75,286,197 + LTEXT "Sections:",IDC_STATIC,7,64,30,8 + LTEXT "Entry point:",IDC_STATIC,7,47,39,8 + LTEXT "Static",IDC_ENTRYPOINT,78,47,215,8 + LTEXT "Image base:",IDC_STATIC,7,34,41,8 + LTEXT "Static",IDC_IMAGEBASE,78,34,215,8 + LTEXT "Image type:",IDC_STATIC,7,20,40,8 + LTEXT "Static",IDC_IMAGETYPE,78,20,215,8 +END + ///////////////////////////////////////////////////////////////////////////// // diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index ca02ad300e64..b0d32756b17c 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -94,6 +94,7 @@ true true true + true noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) @@ -123,6 +124,7 @@ true true true + true noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;windowscodecs.lib;%(AdditionalDependencies) @@ -212,6 +214,9 @@ + + + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index 977007a3472b..9058c1d666c7 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -13,17 +13,20 @@ {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 @@ -36,32 +39,47 @@ 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 + Source Files\Pages\Windows - Source Files - - - Source Files + Source Files\Pages\Windows - - Source Files + + Source Files\Pages\Windows - Source Files + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Linux + + + Source Files\Pages\Linux + + + Source Files\Pages\Linux diff --git a/tools/peview/resource.h b/tools/peview/resource.h index 29c9488f2611..67a136b18427 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -12,6 +12,7 @@ #define IDD_PESYMBOLS 108 #define IDD_PERESOURCES 109 #define IDB_SEARCH_ACTIVE 110 +#define IDD_ELFGENERAL 110 #define IDB_SEARCH_INACTIVE 111 #define IDB_SEARCH_ACTIVE_BMP 112 #define IDB_SEARCH_INACTIVE_BMP 113 @@ -34,6 +35,8 @@ #define IDC_IMAGEBASE 1015 #define IDC_ENTRYPOINT 1016 #define IDC_SYMSEARCH 1017 +#define IDC_IMAGEBASE2 1017 +#define IDC_IMAGETYPE 1017 #define IDC_NAME 1019 #define IDC_COMPANYNAME_LINK 1020 #define IDC_STOP 1021 diff --git a/tools/peview/settings.c b/tools/peview/settings.c index dc97ffcf2492..02933ce572b8 100644 --- a/tools/peview/settings.c +++ b/tools/peview/settings.c @@ -42,6 +42,9 @@ VOID PhAddDefaultSettings( PhpAddStringSetting(L"ImageResourcesListViewColumns", L""); PhpAddStringSetting(L"LibListViewColumns", L""); PhpAddStringSetting(L"PdbTreeListColumns", L""); + + // Wsl properties + PhpAddStringSetting(L"GeneralWslTreeListColumns", L""); } VOID PhUpdateCachedSettings( From 2482628937399d9c5505b5b76ced445e9e7cee70 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 03:17:55 +1100 Subject: [PATCH 662/839] Fix general options checkbox support #218 --- ProcessHacker/options.c | 71 ++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 115b54a70252..0a533db00fe6 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1313,50 +1313,63 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( // } //} - if (header->code == LVN_ITEMCHANGED) + switch (header->code) { - LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; - - if (listView->uChanged & LVIF_STATE) + case NM_CLICK: { - switch (listView->uNewState & LVIS_STATEIMAGEMASK) + LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; + BOOLEAN itemChecked; + + itemChecked = ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem) == BST_CHECKED; + ListView_SetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem, !itemChecked); + } + break; + case LVN_ITEMCHANGED: + { + LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; + + if (listView->uChanged & LVIF_STATE) { - case INDEXTOSTATEIMAGEMASK(2): // checked + switch (listView->uNewState & LVIS_STATEIMAGEMASK) { - switch (listView->iItem) + case INDEXTOSTATEIMAGEMASK(2): // checked { - case PHP_OPTIONS_INDEX_START_ATLOGON: + switch (listView->iItem) { - PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, FALSE); + case PHP_OPTIONS_INDEX_START_ATLOGON: + { + PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, FALSE); + } + break; + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(FALSE); + } + break; } - break; - case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: - { - PhpOptionsShowHideTreeViewItem(FALSE); - } - break; } - } - break; - case INDEXTOSTATEIMAGEMASK(1): // unchecked - { - switch (listView->iItem) + break; + case INDEXTOSTATEIMAGEMASK(1): // unchecked { - case PHP_OPTIONS_INDEX_START_ATLOGON: + switch (listView->iItem) { - PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, TRUE); + case PHP_OPTIONS_INDEX_START_ATLOGON: + { + PhpSetListViewItemState(listView->hdr.hwndFrom, PHP_OPTIONS_INDEX_START_HIDDEN, TRUE); + } + break; + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(TRUE); + } + break; } - break; - case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: - { - PhpOptionsShowHideTreeViewItem(TRUE); - } - break; } + break; } - break; } } + break; } } break; From 327fc3c0ed81bbd4e38f8e79a795cbebd2466f0a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 03:36:59 +1100 Subject: [PATCH 663/839] peview: fix elf imports column typo --- tools/peview/exlfimports.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/peview/exlfimports.c b/tools/peview/exlfimports.c index d8f4df98ec89..3170d6dbc785 100644 --- a/tools/peview/exlfimports.c +++ b/tools/peview/exlfimports.c @@ -45,9 +45,8 @@ VOID PvpProcessElfImports( PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, import->Module); PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, import->Name); - //PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PhaFormatSize(import->Size, -1)->Buffer); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolTypeName(import->TypeInfo)); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 5, PvpGetSymbolBindingName(import->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PvpGetSymbolTypeName(import->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolBindingName(import->TypeInfo)); } PhFreeMappedWslImageSymbols(imports); From e0c6384595d63720f399adcce0f60cd938fbac0a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 04:30:07 +1100 Subject: [PATCH 664/839] Fix general options checkbox regresion #218 --- ProcessHacker/options.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 0a533db00fe6..f0d777b1f738 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1318,10 +1318,22 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( case NM_CLICK: { LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; - BOOLEAN itemChecked; - - itemChecked = ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem) == BST_CHECKED; - ListView_SetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem, !itemChecked); + 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_ITEMCHANGED: From 6bfff70cb23f79722ffe038212429dbba7814b6f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 05:00:52 +1100 Subject: [PATCH 665/839] Add missing checkbox state #218 --- ProcessHacker/options.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index f0d777b1f738..d9a46dad16b8 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1320,6 +1320,15 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; LVHITTESTINFO lvHitInfo; + // HACK: Don't change the checkbox state for the 'Start hidden' item when the 'Start at logon' item hasn't been enabled. + if ( + itemActivate->iItem == PHP_OPTIONS_INDEX_START_HIDDEN && + ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), PHP_OPTIONS_INDEX_START_ATLOGON) != BST_CHECKED + ) + { + break; + } + lvHitInfo.pt = itemActivate->ptAction; if (ListView_HitTest(GetDlgItem(hwndDlg, IDC_SETTINGS), &lvHitInfo) != -1) From f32c37abfc2839be2e82ff9b322f5364f280f960 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Sun, 7 Jan 2018 08:50:51 +0100 Subject: [PATCH 666/839] Add "Protection" column to process tree (#221) * Store process protection info in PH_PROCESS_ITEM * Format process protection in separate function * Add "Protection" column to process tree --- ProcessHacker/include/procprv.h | 1 + ProcessHacker/include/proctree.h | 4 +- ProcessHacker/procprv.c | 18 ++++++ ProcessHacker/proctree.c | 19 ++++++- ProcessHacker/prpggen.c | 95 +++++++++++++++----------------- 5 files changed, 84 insertions(+), 53 deletions(-) diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 1237d1c8995a..b8726974e22b 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -231,6 +231,7 @@ typedef struct _PH_PROCESS_ITEM ULONGLONG ProcessSequenceNumber; PH_KNOWN_PROCESS_TYPE KnownProcessType; + PS_PROTECTION Protection; ULONG JobObjectId; } PH_PROCESS_ITEM, *PPH_PROCESS_ITEM; // end_phapppub diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index 65dba46ed220..889486cc13bb 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -91,8 +91,9 @@ #define PHPRTLC_FILESIZE 78 #define PHPRTLC_SUBPROCESSCOUNT 79 #define PHPRTLC_JOBOBJECTID 80 +#define PHPRTLC_PROTECTION 81 -#define PHPRTLC_MAXIMUM 81 +#define PHPRTLC_MAXIMUM 82 #define PHPRTLC_IOGROUP_COUNT 9 #define PHPN_WSCOUNTERS 0x1 @@ -217,6 +218,7 @@ typedef struct _PH_PROCESS_NODE PPH_STRING FileSizeText; PPH_STRING SubprocessCountText; WCHAR JobObjectIdText[PH_INT32_STR_LEN_1]; + PPH_STRING ProtectionText; // Graph buffers PH_GRAPH_BUFFERS CpuGraphBuffers; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 1671ce92029a..eeb780c3d7f5 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1453,6 +1453,24 @@ VOID PhpFillProcessItem( ); } + // Protection + if (ProcessItem->QueryHandle) + { + if (WindowsVersion >= WINDOWS_8_1) + { + NtQueryInformationProcess( + ProcessItem->QueryHandle, + ProcessProtectionInformation, + &ProcessItem->Protection, + sizeof(ProcessItem->Protection), + NULL); + } + } + else + { + ProcessItem->Protection.Level = (UCHAR)-1; + } + // On Windows 8.1 and above, processes without threads are reflected processes // which will not terminate if we have a handle open. if (Process->NumberOfThreads == 0) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index edaabb63e496..817713a15e2a 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -210,6 +210,7 @@ VOID PhInitializeProcessTreeList( PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); PhAddTreeNewColumnEx(hwnd, PHPRTLC_SUBPROCESSCOUNT, FALSE, L"Subprocesses", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); PhAddTreeNewColumnEx(hwnd, PHPRTLC_JOBOBJECTID, FALSE, L"Job Object ID", 50, PH_ALIGN_LEFT, -1, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PROTECTION, FALSE, L"Protection", 105, PH_ALIGN_LEFT, -1, 0, TRUE); TreeNew_SetRedraw(hwnd, TRUE); @@ -599,6 +600,7 @@ VOID PhpRemoveProcessNode( PhClearReference(&ProcessNode->FileModifiedTimeText); PhClearReference(&ProcessNode->FileSizeText); PhClearReference(&ProcessNode->SubprocessCountText); + PhClearReference(&ProcessNode->ProtectionText); PhDeleteGraphBuffers(&ProcessNode->CpuGraphBuffers); PhDeleteGraphBuffers(&ProcessNode->PrivateGraphBuffers); @@ -1869,6 +1871,12 @@ BEGIN_SORT_FUNCTION(JobObjectId) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(Protection) +{ + sortResult = intcmp((CHAR)processItem1->Protection.Level, (CHAR)processItem2->Protection.Level); +} +END_SORT_FUNCTION + BOOLEAN NTAPI PhpProcessTreeNewCallback( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, @@ -1989,7 +1997,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( SORT_FUNCTION(FileModifiedTime), SORT_FUNCTION(FileSize), SORT_FUNCTION(Subprocesses), - SORT_FUNCTION(JobObjectId) + SORT_FUNCTION(JobObjectId), + SORT_FUNCTION(Protection), }; int (__cdecl *sortFunction)(const void *, const void *); @@ -2790,6 +2799,14 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( } } break; + case PHPRTLC_PROTECTION: + { + extern PPH_STRING PhpFormatProcessProtection(_In_ PPH_PROCESS_ITEM ProcessItem); + + PhMoveReference(&node->ProtectionText, PhpFormatProcessProtection(processItem)); + getCellText->Text = node->ProtectionText->sr; + } + break; default: return FALSE; } diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index ad5c5c54bac6..4f12e13b9b8d 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -38,6 +38,47 @@ static PWSTR ProtectedSignerStrings[] = { L"", L" (Authenticode)", L" (CodeGen)", L" (Antimalware)", L" (Lsa)", L" (Windows)", L" (WinTcb)", L" (WinSystem)", L" (StoreApp)" }; +PPH_STRING PhpFormatProcessProtection(_In_ PPH_PROCESS_ITEM ProcessItem) +{ + if (ProcessItem->Protection.Level != (UCHAR)-1) + { + 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, _In_ ACCESS_MASK DesiredAccess, @@ -334,61 +375,13 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( #ifdef _WIN64 } #endif + NtClose(processHandle); + processHandle = NULL; } // Protection - SetDlgItemText(hwndDlg, IDC_PROTECTION, L"N/A"); - - if (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); + SetDlgItemText(hwndDlg, IDC_PROTECTION, PH_AUTO_T(PH_STRING, PhpFormatProcessProtection(processItem))->Buffer); #ifdef _WIN64 if (processItem->IsWow64Valid) From 9ca557b3b6b406c7bad0c6180985508d458c1c55 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 19:24:07 +1100 Subject: [PATCH 667/839] Tidy up #221 --- ProcessHacker/include/phapp.h | 6 ++++++ ProcessHacker/procprv.c | 23 +++++++++++------------ ProcessHacker/proctree.c | 9 +++++---- ProcessHacker/prpggen.c | 16 ++++++++-------- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index ea1fda8a0e3c..651992748e26 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -810,4 +810,10 @@ HPROPSHEETPAGE PhCreateTokenPage( _In_opt_ DLGPROC HookProc ); +// prpggen + +PPH_STRING PhGetProcessItemProtectionText( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + #endif diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index eeb780c3d7f5..b9a6fc1b79cd 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1454,22 +1454,21 @@ VOID PhpFillProcessItem( } // Protection - if (ProcessItem->QueryHandle) + if (WindowsVersion >= WINDOWS_8_1 && ProcessItem->QueryHandle) { - if (WindowsVersion >= WINDOWS_8_1) + PS_PROTECTION protection; + + if (NT_SUCCESS(NtQueryInformationProcess( + ProcessItem->QueryHandle, + ProcessProtectionInformation, + &protection, + sizeof(PS_PROTECTION), + NULL + ))) { - NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessProtectionInformation, - &ProcessItem->Protection, - sizeof(ProcessItem->Protection), - NULL); + ProcessItem->Protection.Level = protection.Level; } } - else - { - ProcessItem->Protection.Level = (UCHAR)-1; - } // On Windows 8.1 and above, processes without threads are reflected processes // which will not terminate if we have a handle open. diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 817713a15e2a..ce0bb323eaa2 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2801,10 +2801,11 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_PROTECTION: { - extern PPH_STRING PhpFormatProcessProtection(_In_ PPH_PROCESS_ITEM ProcessItem); - - PhMoveReference(&node->ProtectionText, PhpFormatProcessProtection(processItem)); - getCellText->Text = node->ProtectionText->sr; + if (processItem->Protection.Level != 0) + { + PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); + getCellText->Text = node->ProtectionText->sr; + } } break; default: diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 4f12e13b9b8d..c22312acc594 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -38,11 +38,13 @@ static PWSTR ProtectedSignerStrings[] = { L"", L" (Authenticode)", L" (CodeGen)", L" (Antimalware)", L" (Lsa)", L" (Windows)", L" (WinTcb)", L" (WinSystem)", L" (StoreApp)" }; -PPH_STRING PhpFormatProcessProtection(_In_ PPH_PROCESS_ITEM ProcessItem) +PPH_STRING PhGetProcessItemProtectionText( + _In_ PPH_PROCESS_ITEM ProcessItem + ) { - if (ProcessItem->Protection.Level != (UCHAR)-1) + if (WindowsVersion >= WINDOWS_8_1) { - if (WindowsVersion >= WINDOWS_8_1) + if (ProcessItem->Protection.Level != 0) { PWSTR type; PWSTR signer; @@ -350,13 +352,11 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( SetDlgItemText(hwndDlg, IDC_PEBADDRESS, L"N/A"); - PhOpenProcess( + if (NT_SUCCESS(PhOpenProcess( &processHandle, ProcessQueryAccess, processItem->ProcessId - ); - - if (processHandle) + ))) { PhGetProcessBasicInformation(processHandle, &basicInfo); #ifdef _WIN64 @@ -381,7 +381,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( // Protection - SetDlgItemText(hwndDlg, IDC_PROTECTION, PH_AUTO_T(PH_STRING, PhpFormatProcessProtection(processItem))->Buffer); + SetDlgItemText(hwndDlg, IDC_PROTECTION, PH_AUTO_T(PH_STRING, PhGetProcessItemProtectionText(processItem))->Buffer); #ifdef _WIN64 if (processItem->IsWow64Valid) From 63ada7c2d543ca2b24df44a2a2702c2eee838a2e Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 22:02:33 +1100 Subject: [PATCH 668/839] Revert commit 9ca557b --- ProcessHacker/procprv.c | 29 ++++++++++++++++++----------- ProcessHacker/proctree.c | 7 ++----- ProcessHacker/prpggen.c | 28 ++++++++++++++-------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index b9a6fc1b79cd..ef63c2daab28 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1454,21 +1454,28 @@ VOID PhpFillProcessItem( } // Protection - if (WindowsVersion >= WINDOWS_8_1 && ProcessItem->QueryHandle) + if (ProcessItem->QueryHandle) { - PS_PROTECTION protection; - - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessProtectionInformation, - &protection, - sizeof(PS_PROTECTION), - NULL - ))) + if (WindowsVersion >= WINDOWS_8_1) { - ProcessItem->Protection.Level = protection.Level; + PS_PROTECTION protection; + + if (NT_SUCCESS(NtQueryInformationProcess( + ProcessItem->QueryHandle, + ProcessProtectionInformation, + &protection, + sizeof(PS_PROTECTION), + NULL + ))) + { + ProcessItem->Protection.Level = protection.Level; + } } } + else + { + ProcessItem->Protection.Level = UCHAR_MAX; + } // On Windows 8.1 and above, processes without threads are reflected processes // which will not terminate if we have a handle open. diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index ce0bb323eaa2..1bba410d58d3 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2801,11 +2801,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_PROTECTION: { - if (processItem->Protection.Level != 0) - { - PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); - getCellText->Text = node->ProtectionText->sr; - } + PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); + getCellText->Text = node->ProtectionText->sr; } break; default: diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index c22312acc594..c1c197b2c79d 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -42,27 +42,27 @@ PPH_STRING PhGetProcessItemProtectionText( _In_ PPH_PROCESS_ITEM ProcessItem ) { - if (WindowsVersion >= WINDOWS_8_1) + if (ProcessItem->Protection.Level != UCHAR_MAX) { - if (ProcessItem->Protection.Level != 0) + 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; + 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)) From 0168d9c9d78440df23bb28a1b1854c2e74a6d095 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 7 Jan 2018 22:53:53 +1100 Subject: [PATCH 669/839] Add double click checkbox state #218 --- ProcessHacker/options.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index d9a46dad16b8..15e6f4e6d531 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1316,6 +1316,7 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( switch (header->code) { case NM_CLICK: + case NM_DBLCLK: { LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; LVHITTESTINFO lvHitInfo; From 02b2c0afcb1117f6cbab0ba4280c074c65286fd5 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 Jan 2018 04:15:30 +1100 Subject: [PATCH 670/839] Remove dll injection feature --- ProcessHacker/actions.c | 56 -------------- ProcessHacker/cmdmode.c | 22 ------ ProcessHacker/include/actions.h | 8 -- ProcessHacker/prpgmod.c | 20 +---- ProcessHacker/resource.h | 1 - phlib/include/phnative.h | 9 --- phlib/native.c | 132 -------------------------------- 7 files changed, 4 insertions(+), 244 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 485f4a2ea2ef..2e905a3cf5a3 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1663,62 +1663,6 @@ BOOLEAN PhUiDetachFromDebuggerProcess( 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, diff --git a/ProcessHacker/cmdmode.c b/ProcessHacker/cmdmode.c index 6dcd32625fbe..9ea771073c32 100644 --- a/ProcessHacker/cmdmode.c +++ b/ProcessHacker/cmdmode.c @@ -226,28 +226,6 @@ NTSTATUS PhCommandModeStart( 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) diff --git a/ProcessHacker/include/actions.h b/ProcessHacker/include/actions.h index dd484c00333d..62cb0d758b04 100644 --- a/ProcessHacker/include/actions.h +++ b/ProcessHacker/include/actions.h @@ -188,14 +188,6 @@ PhUiDetachFromDebuggerProcess( _In_ PPH_PROCESS_ITEM Process ); -PHAPPAPI -BOOLEAN -NTAPI -PhUiInjectDllProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ); - PHAPPAPI BOOLEAN NTAPI diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index 496cfe371017..ab2cdc1c647e 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -690,8 +690,6 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhInsertEMenuItem(menu, relocatedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION, L"Highlight relocated modules", NULL, NULL), -1); PhInsertEMenuItem(menu, untrustedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION, L"Highlight untrusted modules", NULL, NULL), -1); PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MODULE_FLAGS_LOAD_MODULE_OPTION, L"Load module", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); PhInsertEMenuItem(menu, stringsItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MODULE_STRINGS_OPTION, L"Strings...", NULL, NULL), -1); if (modulesContext->ListContext.HideDynamicModules) @@ -724,20 +722,10 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( if (selectedItem && selectedItem->Id) { - if (selectedItem->Id == PH_MODULE_FLAGS_LOAD_MODULE_OPTION) - { - PhReferenceObject(processItem); - PhUiInjectDllProcess(hwndDlg, processItem); - PhDereferenceObject(processItem); - } - else - { - PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); - - PhSaveSettingsModuleList(&modulesContext->ListContext); - - PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); - } + PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); + PhSaveSettingsModuleList(&modulesContext->ListContext); + + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); } PhDestroyEMenu(menu); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 82c94b02a94a..7b5748ed6395 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -560,7 +560,6 @@ #define ID_PROCESS_AFFINITY 40035 #define ID_PROCESS_CREATEDUMPFILE 40036 #define ID_MISCELLANEOUS_DETACHFROMDEBUGGER 40039 -#define ID_MISCELLANEOUS_INJECTDLL 40041 #define ID_PRIORITY_REALTIME 40048 #define ID_PRIORITY_HIGH 40049 #define ID_WINDOW_BRINGTOFRONT 40055 diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 14467aa734e5..8dcb3aa2b9a9 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -270,15 +270,6 @@ PhGetProcessWsCounters( _Out_ PPH_PROCESS_WS_COUNTERS WsCounters ); -PHLIBAPI -NTSTATUS -NTAPI -PhInjectDllProcess( - _In_ HANDLE ProcessHandle, - _In_ PWSTR FileName, - _In_opt_ PLARGE_INTEGER Timeout - ); - PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/native.c b/phlib/native.c index 4f66d48c3d4c..dadad9f0eaaf 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1208,138 +1208,6 @@ NTSTATUS PhGetProcessWsCounters( 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; - - 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. * From 486e6af165ff186a0a2fbe5296e722ef776ce12a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 Jan 2018 04:27:26 +1100 Subject: [PATCH 671/839] Remove dll injection menu --- ProcessHacker/mainwnd.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 0d1e77077371..23c41d4d95f0 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1094,18 +1094,6 @@ VOID PhMwpOnCommand( } } 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: From 98eb7ebed563d22164ff8258c7459d3db1da237c Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 Jan 2018 04:32:07 +1100 Subject: [PATCH 672/839] (missing file from previous commit) --- ProcessHacker/ProcessHacker.rc | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index a42887027506..c3de1b33c431 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -292,7 +292,6 @@ BEGIN BEGIN MENUITEM "&Detach from debugger", ID_MISCELLANEOUS_DETACHFROMDEBUGGER MENUITEM "GDI &handles", ID_MISCELLANEOUS_GDIHANDLES - MENUITEM "&Inject DLL...", ID_MISCELLANEOUS_INJECTDLL POPUP "Pa&ge priority" BEGIN MENUITEM "&Normal", ID_PAGEPRIORITY_NORMAL From 65da06e5d6ec21f884c78079967a1020ef54fb94 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 8 Jan 2018 04:35:38 +1100 Subject: [PATCH 673/839] Fix build --- ProcessHacker/ProcessHacker.def | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index b77f3d95a2d3..6fe9f2e81071 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -283,7 +283,6 @@ EXPORTS PhGetTokenPrivileges PhGetTokenUser PhImpersonateClientOfNamedPipe - PhInjectDllProcess PhListenNamedPipe PhOpenKey PhLoadAppKey From 13f70744c2d2f2f8014db8088d5f222091399aa5 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 14 Jan 2018 00:56:15 +1100 Subject: [PATCH 674/839] peview: Add symbol tab enumeration workaround for data symbols, Fix symbol tab sorting --- tools/peview/include/peview.h | 1 - tools/peview/pdb.c | 48 +++++++++++++++--- tools/peview/pdbprp.c | 92 ++++++++++++++++++++++++----------- 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 912c73869622..75fdeaf11ce3 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -134,7 +134,6 @@ typedef struct _PV_SYMBOL_NODE { PH_TREENEW_NODE Node; - ULONG64 Index; PV_SYMBOL_TYPE Type; ULONG Size; ULONG64 Address; diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 53239934e5f2..d18dec6485c6 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -59,6 +59,18 @@ typedef BOOL (WINAPI *_SymEnumTypesW)( _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 @@ -112,6 +124,7 @@ _SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; _SymGetTypeFromNameW SymGetTypeFromNameW_I = NULL; _SymGetTypeInfo SymGetTypeInfo_I = NULL; _SymSetContext SymSetContext_I = NULL; +_SymSearchW SymSearchW_I = NULL; ULONG SearchResultsAddIndex = 0; PPH_LIST SearchResults = NULL; @@ -1932,9 +1945,8 @@ BOOL CALLBACK EnumCallbackProc( PPDB_SYMBOL_CONTEXT context = Context; PPV_SYMBOL_NODE symbol; - // TODO: Remove filter - //if (SymbolInfo->Tag != SymTagPublicSymbol) - // break; + if (SymbolInfo->Address == 0) + break; symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); @@ -1942,10 +1954,17 @@ BOOL CALLBACK EnumCallbackProc( symbol->Type = PV_SYMBOL_TYPE_SYMBOL; 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); + 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); @@ -2327,6 +2346,7 @@ NTSTATUS PeDumpFileSymbols( SymGetTypeFromNameW_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeFromNameW", 0); SymGetTypeInfo_I = PhGetProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); SymSetContext_I = PhGetProcedureAddress(dbghelpHandle, "SymSetContext", 0); + SymSearchW_I = PhGetProcedureAddress(dbghelpHandle, "SymSearchW", 0); SymSetOptions_I( SymGetOptions_I() | @@ -2372,9 +2392,25 @@ NTSTATUS PeDumpFileSymbols( //ShowModuleSymbolInfo(symbolBaseAddress); SymEnumSymbolsW_I(NtCurrentProcess(), Context->BaseAddress, NULL, EnumCallbackProc, Context); - // Enumerate user defined types - //PrintUserDefinedTypes(Context); + // Enumerate user defined types. SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); + //PrintUserDefinedTypes(Context); // Commented out due to verbosity. + + // 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: diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index fb8bc689a367..e523b8e302c5 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -406,7 +406,7 @@ ULONG SymbolNodeHashtableHashFunction( _In_ PVOID Entry ) { - return PhHashInt64((*(PPV_SYMBOL_NODE*)Entry)->Index); + return PhHashStringRef(&(*(PPV_SYMBOL_NODE*)Entry)->Name->sr, TRUE); } VOID PvSymbolAddTreeNode( @@ -414,21 +414,20 @@ VOID PvSymbolAddTreeNode( _In_ PPV_SYMBOL_NODE Entry ) { - static ULONG64 index = 0; - PhInitializeTreeNewNode(&Entry->Node); - Entry->Index = index++; 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) + if (PhAddEntryHashtable(Context->NodeHashtable, &Entry)) // HACK { - Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); + PhAddItemList(Context->NodeList, Entry); + + if (Context->FilterSupport.NodeList) + { + Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); + } } } @@ -437,15 +436,21 @@ PPV_SYMBOL_NODE PvFindSymbolNode( _In_ PPH_STRING Name ) { - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - PPV_SYMBOL_NODE entry = Context->NodeList->Items[i]; + PV_SYMBOL_NODE lookupSymbolNode; + PPV_SYMBOL_NODE lookupSymbolNodePtr = &lookupSymbolNode; + PPV_SYMBOL_NODE *threadNode; - if (PhEqualString(entry->Name, Name, TRUE)) - return entry; - } + lookupSymbolNode.Name = Name; - return NULL; + threadNode = (PPV_SYMBOL_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupSymbolNodePtr + ); + + if (threadNode) + return *threadNode; + else + return NULL; } VOID PvRemoveSymbolNode( @@ -482,36 +487,51 @@ VOID PvDestroySymbolNode( int sortResult = 0; #define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ - \ + /*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(Symbol) +BEGIN_SORT_FUNCTION(Type) { - sortResult = PhCompareString(node1->Name, node2->Name, FALSE); + 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(Name) +BEGIN_SORT_FUNCTION(Data) { - sortResult = PhCompareString(node1->Name, node2->Name, FALSE); + 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 + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context ) { PPDB_SYMBOL_CONTEXT context; @@ -530,9 +550,11 @@ BOOLEAN NTAPI PvSymbolTreeNewCallback( { static PVOID sortFunctions[] = { - SORT_FUNCTION(Name), + SORT_FUNCTION(Type), SORT_FUNCTION(VA), - SORT_FUNCTION(Symbol) + SORT_FUNCTION(Symbol), + SORT_FUNCTION(Data), + SORT_FUNCTION(Size) }; int (__cdecl *sortFunction)(void *, const void *, const void *); @@ -765,7 +787,7 @@ VOID PvInitializeSymbolTree( 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, 0, NoSortOrder); + TreeNew_SetSort(TreeNewHandle, TREE_COLUMN_ITEM_VA, AscendingSortOrder); PPH_STRING settings = PhGetStringSetting(L"PdbTreeListColumns"); PhCmLoadSettings(TreeNewHandle, &settings->sr); @@ -1043,6 +1065,18 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( //NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL); //SearchStop = FALSE; + + //if (context->UpdateTimerHandle) + //{ + // RtlDeleteTimer(context->TimerQueueHandle, context->UpdateTimerHandle, NULL); + // context->UpdateTimerHandle = NULL; + //} + + //if (context->TimerQueueHandle) + //{ + // RtlDeleteTimerQueue(context->TimerQueueHandle); + // context->TimerQueueHandle = NULL; + //} } break; case WM_NOTIFY: From 247bb7ce433173b014ebbf20ecfe32b4184f2ee2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 14 Jan 2018 00:57:23 +1100 Subject: [PATCH 675/839] peview: Fix resources tab string termination --- tools/peview/resprp.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/peview/resprp.c b/tools/peview/resprp.c index 59ac129adfae..0877b0b24f23 100644 --- a/tools/peview/resprp.c +++ b/tools/peview/resprp.c @@ -113,7 +113,7 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( { for (i = 0; i < resources.NumberOfEntries; i++) { - PVOID string; + PPH_STRING string; WCHAR number[PH_INT32_STR_LEN_1]; entry = resources.ResourceEntries[i]; @@ -129,10 +129,10 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( { PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Type; - string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, string); - PhFree(string); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, string->Buffer); + PhDereferenceObject(string); } if (IS_INTRESOURCE(entry.Name)) @@ -144,10 +144,10 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( { PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Name; - string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_NAME, string); - PhFree(string); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_NAME, string->Buffer); + PhDereferenceObject(string); } if (IS_INTRESOURCE(entry.Language)) @@ -165,10 +165,10 @@ INT_PTR CALLBACK PvpPeResourcesDlgProc( { PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Language; - string = PhAllocateCopy(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); - PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, string); - PhFree(string); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, string->Buffer); + PhDereferenceObject(string); } PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_SIZE, PhaFormatSize(entry.Size, -1)->Buffer); From 1bf61b3ac1c792f134d2ddcb3a19fa46c455e297 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 14 Jan 2018 21:53:16 +1100 Subject: [PATCH 676/839] peview: fix typo --- tools/peview/exlfexports.c | 2 +- tools/peview/exlfimports.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/peview/exlfexports.c b/tools/peview/exlfexports.c index 80088eed1d04..fda3971a57f0 100644 --- a/tools/peview/exlfexports.c +++ b/tools/peview/exlfexports.c @@ -41,7 +41,7 @@ VOID PvpProcessElfExports( if (!export->ExportSymbol) continue; - PhPrintUInt64(number, count++); + PhPrintUInt64(number, ++count); lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); PhPrintPointer(pointer, (PVOID)export->Address); diff --git a/tools/peview/exlfimports.c b/tools/peview/exlfimports.c index 3170d6dbc785..9e6368e40968 100644 --- a/tools/peview/exlfimports.c +++ b/tools/peview/exlfimports.c @@ -40,7 +40,7 @@ VOID PvpProcessElfImports( if (!import->ImportSymbol) continue; - PhPrintUInt64(number, count++); + PhPrintUInt64(number, ++count); lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, import->Module); From 870f86fe6785a9df9f3b922f44ba3b4a32999555 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 Jan 2018 02:07:27 +1100 Subject: [PATCH 677/839] Move process flags for PR #222 --- ProcessHacker/procprv.c | 45 ++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index ef63c2daab28..e487607e27d3 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -105,16 +105,10 @@ typedef struct _PH_PROCESS_QUERY_S1_DATA 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 IsBeingDebugged : 1; ULONG IsImmersive : 1; - ULONG Spare : 21; + ULONG Spare : 26; }; }; } PH_PROCESS_QUERY_S1_DATA, *PPH_PROCESS_QUERY_S1_DATA; @@ -991,21 +985,6 @@ VOID PhpProcessQueryStage1( } } - // 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; - } - } - // Debugged if (processHandleLimited) { @@ -1046,7 +1025,7 @@ VOID PhpProcessQueryStage1( processId, processHandle, #ifdef _WIN64 - processQueryFlags | PH_CLR_NO_WOW64_CHECK | (Data->IsWow64 ? PH_CLR_KNOWN_IS_WOW64 : 0), + processQueryFlags | PH_CLR_NO_WOW64_CHECK | (processItem->IsWow64 ? PH_CLR_KNOWN_IS_WOW64 : 0), #else processQueryFlags, #endif @@ -1309,11 +1288,6 @@ VOID PhpFillProcessItemStage1( 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; processItem->IsBeingDebugged = Data->IsBeingDebugged; processItem->IsImmersive = Data->IsImmersive; @@ -1377,6 +1351,21 @@ VOID PhpFillProcessItem( 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 From 1553dc0272bb36b648a1e6ba4efd89fdf8f78595 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 15 Jan 2018 12:14:19 +1100 Subject: [PATCH 678/839] peview: Add symbols tab copy menu --- tools/peview/include/peview.h | 1 + tools/peview/pdb.c | 12 ++- tools/peview/pdbprp.c | 176 ++++++++++++++++++++++++++++++---- tools/peview/resource.h | 4 - 4 files changed, 171 insertions(+), 22 deletions(-) diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 75fdeaf11ce3..105ddca4d85c 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -92,6 +92,7 @@ VOID PeSaveSettings( // symbols #define WM_PV_SEARCH_FINISHED (WM_APP + 701) +#define WM_PV_SEARCH_SHOWMENU (WM_APP + 702) extern ULONG SearchResultsAddIndex; extern PPH_LIST SearchResults; diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index d18dec6485c6..2a668eba64f6 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -1305,8 +1305,13 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( switch (Info.Tag) { case SymTagBaseType: - PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.BaseTypeInfo.BaseType, Info.BaseTypeInfo.Length)); - PhAppendStringBuilder2(TypeName, L" "); + { + 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); @@ -1435,7 +1440,10 @@ PPH_STRING SymbolInfo_GetTypeName( typeVarName = VarName; if (!SymbolInfo_GetTypeNameHelper(Index, Context, &typeVarName, &typeNamesb)) + { + PhDeleteStringBuilder(&typeNamesb); return NULL; + } return PhFinalStringBuilderString(&typeNamesb); } diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index e523b8e302c5..d6b471f28854 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -25,6 +25,8 @@ #include #include "colmgr.h" +#pragma region copied from appsup.c + VOID PhInitializeTreeNewColumnMenu( _Inout_ PPH_TN_COLUMN_MENU_DATA Data ) @@ -356,6 +358,123 @@ VOID PhApplyTreeNewFilters( 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 @@ -687,9 +806,9 @@ BOOLEAN NTAPI PvSymbolTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - //SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y)); + SendMessage(context->ParentWindowHandle, WM_PV_SEARCH_SHOWMENU, 0, (LPARAM)contextMenu); } return TRUE; case TreeNewHeaderRightClick: @@ -978,9 +1097,6 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( PvInitializeSymbolTree(context, hwndDlg, context->TreeNewHandle); PhAddTreeNewFilter(GetSymbolListFilterSupport(context), PvSymbolTreeFilterCallback, context); - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); - SearchResults = PhCreateList(0x1000); context->UdtList = PhCreateList(0x100); @@ -1079,23 +1195,51 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( //} } break; - case WM_NOTIFY: + case WM_PV_SEARCH_SHOWMENU: { - LPNMHDR header = (LPNMHDR)lParam; - LPPSHNOTIFY pageNotify = (LPPSHNOTIFY)header; + 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); - switch (pageNotify->hdr.code) + if (numberOfSymbolNodes != 0) { - case PSN_SETACTIVE: - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(GetParent(hwndDlg), IDCANCEL), TRUE); // HACK - break; - case PSN_QUERYINITIALFOCUS: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(GetParent(hwndDlg), IDCANCEL)); - return TRUE; + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_SYMBOL_COPY, L"Copy", NULL, NULL), -1); + 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 != -1) + { + 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; diff --git a/tools/peview/resource.h b/tools/peview/resource.h index 67a136b18427..7c67918dd9ab 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -24,7 +24,6 @@ #define IDC_CHARACTERISTICS 1007 #define IDC_LIST 1008 #define IDC_FILEICON 1009 -#define IDC_SECTION 1009 #define IDC_TIMESTAMP 1010 #define IDC_RUNTIMEVERSION 1011 #define IDC_FILE 1011 @@ -35,12 +34,9 @@ #define IDC_IMAGEBASE 1015 #define IDC_ENTRYPOINT 1016 #define IDC_SYMSEARCH 1017 -#define IDC_IMAGEBASE2 1017 #define IDC_IMAGETYPE 1017 #define IDC_NAME 1019 #define IDC_COMPANYNAME_LINK 1020 -#define IDC_STOP 1021 -#define IDC_PROGRESS 1022 // Next default values for new objects // From 08ce139e2d3ae7cee20709232e9495b29387431f Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Mon, 15 Jan 2018 08:06:33 +0100 Subject: [PATCH 679/839] Add option to control tree list border (TreeListBorderEnable) (#225) * Don't show border in main tabs * Add option to control tree list border (TreeListBorderEnable) --- ProcessHacker/mainwnd.c | 8 +++++--- ProcessHacker/settings.c | 1 + plugins/ExtendedTools/disktab.c | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 23c41d4d95f0..659779f72d12 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -394,6 +394,7 @@ VOID PhMwpInitializeControls( ) { ULONG thinRows; + ULONG treelistBorder; TabControlHandle = CreateWindow( WC_TABCONTROL, @@ -412,11 +413,12 @@ VOID PhMwpInitializeControls( BringWindowToTop(TabControlHandle); thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; + treelistBorder = PhGetIntegerSetting(L"TreeListBorderEnable") ? WS_BORDER : 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, + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | TN_STYLE_ANIMATE_DIVIDER | thinRows | treelistBorder, 0, 0, 3, @@ -431,7 +433,7 @@ VOID PhMwpInitializeControls( PhMwpServiceTreeNewHandle = CreateWindow( PH_TREENEW_CLASSNAME, NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, 0, 0, 3, @@ -446,7 +448,7 @@ VOID PhMwpInitializeControls( PhMwpNetworkTreeNewHandle = CreateWindow( PH_TREENEW_CLASSNAME, NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, 0, 0, 3, diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index d7235af1a9e2..2021120adece 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -164,6 +164,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"TokenSplitterEnable", L"0"); PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); + PhpAddIntegerSetting(L"TreeListBorderEnable", L"1"); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms PhpAddIntegerSetting(L"WmiProviderEnableHiddenMenu", L"0"); PhpAddStringSetting(L"WmiProviderListViewColumns", L""); diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index f669e3cc93ae..55a873d0d19e 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -91,12 +91,14 @@ BOOLEAN EtpDiskPageCallback( 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 | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, 0, 0, 3, From 7ff4a1a81356ea848bbbcc9493af328ad87adb4c Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Mon, 15 Jan 2018 11:35:36 +0100 Subject: [PATCH 680/839] Improve behavior of "Protection" column in process tree (#222) --- ProcessHacker/procprv.c | 8 ++++++++ ProcessHacker/proctree.c | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index e487607e27d3..75136885980f 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1460,9 +1460,17 @@ VOID PhpFillProcessItem( ProcessItem->Protection.Level = protection.Level; } } + else + { + // "emulate" PS_PROTECTION info for older OSes + if (ProcessItem->IsProtectedProcess) + ProcessItem->Protection.Type = PsProtectedTypeProtected; + } } else { + // we weren't able to get protection info + // lets signalize that with special value ProcessItem->Protection.Level = UCHAR_MAX; } diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 1bba410d58d3..d8332a80d8ee 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2801,8 +2801,11 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( break; case PHPRTLC_PROTECTION: { - PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); - getCellText->Text = node->ProtectionText->sr; + if (processItem->Protection.Level != 0 && processItem->Protection.Level != UCHAR_MAX) + { + PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); + getCellText->Text = node->ProtectionText->sr; + } } break; default: From b9aa907034b29ee1a579bdbc92742f3524415b0a Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 Jan 2018 16:23:04 +1100 Subject: [PATCH 681/839] peview: Add missing ELF image unique bindings --- phlib/include/exlf.h | 12 +++++++++--- tools/peview/exlfprp.c | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/phlib/include/exlf.h b/phlib/include/exlf.h index 736b88ee0c2a..35ad1c93b7a1 100644 --- a/phlib/include/exlf.h +++ b/phlib/include/exlf.h @@ -185,9 +185,15 @@ #define DT_HIPROC 0x7fffffff // symbol table section -#define STB_LOCAL 0 -#define STB_GLOBAL 1 -#define STB_WEAK 2 +#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 diff --git a/tools/peview/exlfprp.c b/tools/peview/exlfprp.c index 3dca74aca3e8..d9e67e3a0cf4 100644 --- a/tools/peview/exlfprp.c +++ b/tools/peview/exlfprp.c @@ -63,6 +63,8 @@ PWSTR PvpGetSymbolBindingName( return L"Global"; case STB_WEAK: return L"Weak"; + case STB_GNU_UNIQUE: + return L"Unique"; } return L"***ERROR***"; From d14385247cacdd5425879574525ec73f12e34377 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 Jan 2018 16:24:02 +1100 Subject: [PATCH 682/839] peview: Add hack for SymSearchW crashes --- tools/peview/pdb.c | 50 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 2a668eba64f6..8912fb428071 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -1889,12 +1889,14 @@ BOOL CALLBACK EnumCallbackProc( symDataKind = SymbolInfo_DataKindStr(dataKindType); - //if (dataKindType == DataIsLocal || - // dataKindType == DataIsParam || - // dataKindType == DataIsObjectPtr) - //{ - // break; - //} + if ( + dataKindType == DataIsLocal || + dataKindType == DataIsParam || + dataKindType == DataIsObjectPtr + ) + { + break; + } symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); @@ -2404,21 +2406,27 @@ NTSTATUS PeDumpFileSymbols( SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); //PrintUserDefinedTypes(Context); // Commented out due to verbosity. - // 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 - ); + 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: From b4af9dfc6b1652be1278011db9b106233e426a89 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 Jan 2018 16:26:38 +1100 Subject: [PATCH 683/839] Prevent 32bit builds from running on 64bit due to NTAPI compatibility issues (release builds only) --- ProcessHacker/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 74c1e506236b..5386cd7ae90c 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -222,6 +222,7 @@ INT WINAPI wWinMain( 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 From 9626b8e21ed59a22163e21fe034daa5b78ce22f4 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Fri, 19 Jan 2018 13:36:45 +0100 Subject: [PATCH 684/839] GF Guard column improvements (#224) * Show "CF Guard" column only when CFG available * Show info in "CF Guard" column only when CFG mitigation is active for process --- ProcessHacker/include/procprv.h | 7 ++++++- ProcessHacker/modlist.c | 11 ++--------- ProcessHacker/modprv.c | 7 +++++++ ProcessHacker/procprv.c | 31 +++++++++++++++++++++++++++++++ ProcessHacker/proctree.c | 19 ++++--------------- 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index b8726974e22b..72ebd3ea04b1 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -168,8 +168,9 @@ typedef struct _PH_PROCESS_ITEM ULONG IsProtectedProcess : 1; ULONG IsSecureProcess : 1; ULONG IsSubsystemProcess : 1; + ULONG IsControlFlowGuardEnabled : 1; - ULONG Spare : 15; + ULONG Spare : 14; }; }; @@ -411,4 +412,8 @@ PhReferenceProcessItemForRecord( ); // end_phapppub +BOOLEAN PhProcessIsCFGuardEnabled( + _In_ HANDLE ProcessHandle +); + #endif diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index b6a24c56edd8..27089f52468a 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -774,15 +774,8 @@ BOOLEAN NTAPI PhpModuleTreeNewCallback( } 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"); - } + if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) + PhInitializeStringRef(&getCellText->Text, L"CF Guard"); break; case PHMOTLC_LOADTIME: { diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 7a39345db8aa..17a9c3179688 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -354,6 +354,7 @@ VOID PhModuleProviderUpdate( PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; PPH_LIST modules; ULONG i; + BOOLEAN cfGuardEnabled = FALSE; // If we didn't get a handle when we created the provider, // abort (unless this is the System process - in that case @@ -444,6 +445,8 @@ VOID PhModuleProviderUpdate( } } + cfGuardEnabled = PhProcessIsCFGuardEnabled(moduleProvider->ProcessHandle); + // Look for new modules. for (i = 0; i < modules->Count; i++) { @@ -527,6 +530,10 @@ VOID PhModuleProviderUpdate( } } + // remove CF Guard flag if CFG mitigation is not enabled for the process + if (!cfGuardEnabled) + moduleItem->ImageDllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_GUARD_CF; + if (NT_SUCCESS(PhQueryFullAttributesFileWin32(moduleItem->FileName->Buffer, &networkOpenInfo))) { moduleItem->FileLastWriteTime = networkOpenInfo.LastWriteTime; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 75136885980f..4ab5bcb163d2 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1474,6 +1474,10 @@ VOID PhpFillProcessItem( ProcessItem->Protection.Level = UCHAR_MAX; } + // Control Flow Guard + if (ProcessItem->QueryHandle) + ProcessItem->IsControlFlowGuardEnabled = PhProcessIsCFGuardEnabled(ProcessItem->QueryHandle); + // On Windows 8.1 and above, processes without threads are reflected processes // which will not terminate if we have a handle open. if (Process->NumberOfThreads == 0) @@ -2984,3 +2988,30 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( return processItem; } + +BOOLEAN PhProcessIsCFGuardEnabled( + _In_ HANDLE ProcessHandle +) +{ + BOOLEAN cfgEnabled = FALSE; + + if (WindowsVersion >= WINDOWS_8_1) + { + PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; + + policyInfo.Policy = ProcessControlFlowGuardPolicy; + + if (NT_SUCCESS(NtQueryInformationProcess( + ProcessHandle, + ProcessMitigationPolicy, + &policyInfo, + sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), + NULL + ))) + { + cfgEnabled = (policyInfo.ControlFlowGuardPolicy.EnableControlFlowGuard != 0); + } + } + + return cfgEnabled; +} diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index d8332a80d8ee..0247bb88506e 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1826,11 +1826,9 @@ 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 + node1->ProcessItem->IsControlFlowGuardEnabled, + node2->ProcessItem->IsControlFlowGuardEnabled ); } END_SORT_FUNCTION @@ -2736,17 +2734,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( } 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"); - } + if (processItem->IsControlFlowGuardEnabled) + PhInitializeStringRef(&getCellText->Text, L"CF Guard"); break; case PHPRTLC_TIMESTAMP: PhpUpdateProcessNodeImage(node); From fced060675af435b54347872e8c6a84ef7e110ce Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 00:13:24 +1100 Subject: [PATCH 685/839] Add PhGetProcessIsCFGuardEnabled --- phlib/include/phnativeinl.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index d2954d866481..17ffac82d1e5 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -376,6 +376,34 @@ PhGetProcessConsoleHostProcessId( return status; } +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; +} + /** * Sets a process' affinity mask. * From 43bbd2f8755d8d038c83f74ebf285522afb25bea Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 00:32:58 +1100 Subject: [PATCH 686/839] Add PhGetProcessProtection --- phlib/include/phnativeinl.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index 17ffac82d1e5..572dfda47a1c 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -376,6 +376,22 @@ PhGetProcessConsoleHostProcessId( return status; } +FORCEINLINE +NTSTATUS +PhGetProcessProtection( + _In_ HANDLE ProcessHandle, + _Out_ PPS_PROTECTION Protection + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessProtectionInformation, + Protection, + sizeof(PS_PROTECTION), + NULL + ); +} + FORCEINLINE NTSTATUS PhGetProcessIsCFGuardEnabled( From c8a3b3802198bb3e228c8f866f51b1814358896d Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 00:59:42 +1100 Subject: [PATCH 687/839] Improve PR #224 --- ProcessHacker/include/procprv.h | 4 --- ProcessHacker/modprv.c | 20 +++++++++---- ProcessHacker/procprv.c | 51 +++++++++------------------------ 3 files changed, 28 insertions(+), 47 deletions(-) diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 72ebd3ea04b1..7ee4e9f3711a 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -412,8 +412,4 @@ PhReferenceProcessItemForRecord( ); // end_phapppub -BOOLEAN PhProcessIsCFGuardEnabled( - _In_ HANDLE ProcessHandle -); - #endif diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 17a9c3179688..9a7589f415d6 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -84,6 +84,7 @@ PPH_MODULE_PROVIDER PhCreateModuleProvider( PhEmGetObjectSize(EmModuleProviderType, sizeof(PH_MODULE_PROVIDER)), PhModuleProviderType ); + memset(moduleProvider, 0, sizeof(PH_MODULE_PROVIDER)); moduleProvider->ModuleHashtable = PhCreateHashtable( sizeof(PPH_MODULE_ITEM), @@ -122,9 +123,21 @@ PPH_MODULE_PROVIDER PhCreateModuleProvider( moduleProvider->RunStatus = status; } - if (moduleProvider->ProcessHandle) + 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; + } + } + } + RtlInitializeSListHead(&moduleProvider->QueryListHead); PhEmCallObjectOperation(EmModuleProviderType, moduleProvider, EmObjectCreate); @@ -354,7 +367,6 @@ VOID PhModuleProviderUpdate( PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; PPH_LIST modules; ULONG i; - BOOLEAN cfGuardEnabled = FALSE; // If we didn't get a handle when we created the provider, // abort (unless this is the System process - in that case @@ -445,8 +457,6 @@ VOID PhModuleProviderUpdate( } } - cfGuardEnabled = PhProcessIsCFGuardEnabled(moduleProvider->ProcessHandle); - // Look for new modules. for (i = 0; i < modules->Count; i++) { @@ -531,7 +541,7 @@ VOID PhModuleProviderUpdate( } // remove CF Guard flag if CFG mitigation is not enabled for the process - if (!cfGuardEnabled) + if (!moduleProvider->ControlFlowGuardEnabled) moduleItem->ImageDllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_GUARD_CF; if (NT_SUCCESS(PhQueryFullAttributesFileWin32(moduleItem->FileName->Buffer, &networkOpenInfo))) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 4ab5bcb163d2..ce3e38e025f8 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1449,34 +1449,35 @@ VOID PhpFillProcessItem( { PS_PROTECTION protection; - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessProtectionInformation, - &protection, - sizeof(PS_PROTECTION), - NULL - ))) + if (NT_SUCCESS(PhGetProcessProtection(ProcessItem->QueryHandle, &protection))) { ProcessItem->Protection.Level = protection.Level; } } else { - // "emulate" PS_PROTECTION info for older OSes + // HACK: 'emulate' the PS_PROTECTION info for older OSes. if (ProcessItem->IsProtectedProcess) ProcessItem->Protection.Type = PsProtectedTypeProtected; } } else { - // we weren't able to get protection info - // lets signalize that with special value + // 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. ProcessItem->Protection.Level = UCHAR_MAX; } // Control Flow Guard - if (ProcessItem->QueryHandle) - ProcessItem->IsControlFlowGuardEnabled = PhProcessIsCFGuardEnabled(ProcessItem->QueryHandle); + 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. @@ -2989,29 +2990,3 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( return processItem; } -BOOLEAN PhProcessIsCFGuardEnabled( - _In_ HANDLE ProcessHandle -) -{ - BOOLEAN cfgEnabled = FALSE; - - if (WindowsVersion >= WINDOWS_8_1) - { - PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; - - policyInfo.Policy = ProcessControlFlowGuardPolicy; - - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessHandle, - ProcessMitigationPolicy, - &policyInfo, - sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), - NULL - ))) - { - cfgEnabled = (policyInfo.ControlFlowGuardPolicy.EnableControlFlowGuard != 0); - } - } - - return cfgEnabled; -} From f4e8de1d30fa4eee862194e34cedecb24b1be8ae Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:00:39 +1100 Subject: [PATCH 688/839] Update process tree node cache --- ProcessHacker/proctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 0247bb88506e..f5f3196bb93f 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -646,7 +646,7 @@ VOID PhTickProcessNodes( // 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 + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS | PHPRTLC_CFGUARD | PHPRTLC_PROTECTION; // Items that always remain valid // Invalidate graph buffers. node->CpuGraphBuffers.Valid = FALSE; From b8ff1d37b66a5731050cfa69cbdf6aa566828537 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:06:59 +1100 Subject: [PATCH 689/839] Revert "Update process tree node cache" This reverts commit f4e8de1d30fa4eee862194e34cedecb24b1be8ae. --- ProcessHacker/proctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index f5f3196bb93f..0247bb88506e 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -646,7 +646,7 @@ VOID PhTickProcessNodes( // 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 | PHPRTLC_CFGUARD | PHPRTLC_PROTECTION; // Items that always remain valid + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS; // Items that always remain valid // Invalidate graph buffers. node->CpuGraphBuffers.Valid = FALSE; From 08d57997d34c95166b403ee3b7a2e2bae79def56 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:18:07 +1100 Subject: [PATCH 690/839] Improve process tree sorting --- ProcessHacker/proctree.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 0247bb88506e..02a0b0e7e840 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1664,7 +1664,7 @@ BEGIN_SORT_FUNCTION(Virtualized) { PhpUpdateProcessNodeToken(node1); PhpUpdateProcessNodeToken(node2); - sortResult = intcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled); + sortResult = ucharcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled); } END_SORT_FUNCTION @@ -1798,7 +1798,7 @@ BEGIN_SORT_FUNCTION(Subsystem) { PhpUpdateProcessNodeImage(node1); PhpUpdateProcessNodeImage(node2); - sortResult = intcmp(node1->ImageSubsystem, node2->ImageSubsystem); + sortResult = ushortcmp(node1->ImageSubsystem, node2->ImageSubsystem); } END_SORT_FUNCTION @@ -1826,10 +1826,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(CfGuard) { - sortResult = intcmp( - node1->ProcessItem->IsControlFlowGuardEnabled, - node2->ProcessItem->IsControlFlowGuardEnabled - ); + sortResult = uintcmp(node1->ProcessItem->IsControlFlowGuardEnabled, node2->ProcessItem->IsControlFlowGuardEnabled); } END_SORT_FUNCTION @@ -1871,7 +1868,9 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(Protection) { - sortResult = intcmp((CHAR)processItem1->Protection.Level, (CHAR)processItem2->Protection.Level); + // 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 From db4d7d4c7edec33d2ca69ff50d2c7d0b1b1072b7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:31:08 +1100 Subject: [PATCH 691/839] Improve mini info window popup location calculation (experimental) --- ProcessHacker/miniinfo.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index ccef3ad0b9c1..f6b151608b98 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -35,6 +35,8 @@ #include #include #include + +#include #include static HWND PhMipContainerWindow = NULL; @@ -699,38 +701,43 @@ VOID PhMipCalculateWindowRectangle( { PH_RECTANGLE bounds; - if (memcmp(&monitorInfo.rcWork, &monitorInfo.rcMonitor, sizeof(RECT)) == 0) + if (RtlEqualMemory(&monitorInfo.rcWork, &monitorInfo.rcMonitor, sizeof(RECT))) { - HWND trayWindow; - RECT taskbarRect; + 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 ((trayWindow = FindWindow(L"Shell_TrayWnd", NULL)) && - GetMonitorInfo(MonitorFromWindow(trayWindow, MONITOR_DEFAULTTOPRIMARY), &monitorInfo) && // Just in case - GetWindowRect(trayWindow, &taskbarRect)) + 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.right < monitorMidX) + if (taskbarRect.rc.right < monitorMidX) { // Left - monitorInfo.rcWork.left += taskbarRect.right - taskbarRect.left; + monitorInfo.rcWork.left += taskbarRect.rc.right - taskbarRect.rc.left; } - else if (taskbarRect.bottom < monitorMidY) + else if (taskbarRect.rc.bottom < monitorMidY) { // Top - monitorInfo.rcWork.top += taskbarRect.bottom - taskbarRect.top; + monitorInfo.rcWork.top += taskbarRect.rc.bottom - taskbarRect.rc.top; } - else if (taskbarRect.left > monitorMidX) + else if (taskbarRect.rc.left > monitorMidX) { // Right - monitorInfo.rcWork.right -= taskbarRect.right - taskbarRect.left; + monitorInfo.rcWork.right -= taskbarRect.rc.right - taskbarRect.rc.left; } - else if (taskbarRect.top > monitorMidY) + else if (taskbarRect.rc.top > monitorMidY) { // Bottom - monitorInfo.rcWork.bottom -= taskbarRect.bottom - taskbarRect.top; + monitorInfo.rcWork.bottom -= taskbarRect.rc.bottom - taskbarRect.rc.top; } } } From 6351e9527aea8b975fd21a647efd49ab3febef10 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:32:27 +1100 Subject: [PATCH 692/839] Fix build (missing file from commit c8a3b38) --- ProcessHacker/include/modprv.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ProcessHacker/include/modprv.h b/ProcessHacker/include/modprv.h index 05e36ef3b664..6a47722515bc 100644 --- a/ProcessHacker/include/modprv.h +++ b/ProcessHacker/include/modprv.h @@ -51,6 +51,16 @@ typedef struct _PH_MODULE_PROVIDER PPH_STRING PackageFullName; SLIST_HEADER QueryListHead; NTSTATUS RunStatus; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN ControlFlowGuardEnabled : 1; + BOOLEAN Spare : 7; + }; + }; } PH_MODULE_PROVIDER, *PPH_MODULE_PROVIDER; // end_phapppub From 74c5cd4f7fa2345ffe34c4567c33e80d0764b8bf Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:41:33 +1100 Subject: [PATCH 693/839] Plugins: Add PhHttpSocketSetCredentials --- plugins/CommonUtil/http.c | 19 ++++++++++++++++++- plugins/CommonUtil/http.h | 9 +++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/plugins/CommonUtil/http.c b/plugins/CommonUtil/http.c index e909ae5605c1..35e30725e209 100644 --- a/plugins/CommonUtil/http.c +++ b/plugins/CommonUtil/http.c @@ -492,4 +492,21 @@ PPH_STRING PhHttpSocketGetErrorMessage( } return message; -} \ No newline at end of file +} + +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 + ); +} + diff --git a/plugins/CommonUtil/http.h b/plugins/CommonUtil/http.h index 5636f4f1523d..52f56dc2db26 100644 --- a/plugins/CommonUtil/http.h +++ b/plugins/CommonUtil/http.h @@ -160,6 +160,15 @@ PhHttpSocketGetErrorMessage( _In_ ULONG ErrorCode ); +_Check_return_ +BOOLEAN +NTAPI +PhHttpSocketSetCredentials( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PCWSTR Name, + _In_ PCWSTR Value + ); + #ifdef __cplusplus } #endif From 69bb84b21e186ae5d70fdb7fc83e89fd9d2bb348 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:45:00 +1100 Subject: [PATCH 694/839] Add workaround for ProcessSequenceNumber bug when running under wow64 --- ProcessHacker/procprv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index ce3e38e025f8..d7dd2fa7d73e 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1317,7 +1317,7 @@ VOID PhpFillProcessItemExtension( { PSYSTEM_PROCESS_INFORMATION_EXTENSION processExtension; - if (WindowsVersion < WINDOWS_10_RS3) + if (WindowsVersion < WINDOWS_10_RS3 || PhIsExecutingInWow64()) return; processExtension = PH_PROCESS_EXTENSION(Process); @@ -2059,7 +2059,7 @@ VOID PhProcessProviderUpdate( { PPH_PROCESS_ITEM processItem; - if (WindowsVersion >= WINDOWS_10_RS3) + 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 @@ -2128,7 +2128,7 @@ VOID PhProcessProviderUpdate( processEntry = (PSYSTEM_PROCESS_INFORMATION)processEntry->UniqueProcessKey; } - if (WindowsVersion >= WINDOWS_10_RS3) + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) { if (!processEntry || PH_PROCESS_EXTENSION(processEntry)->ProcessSequenceNumber != processItem->ProcessSequenceNumber) processRemoved = TRUE; @@ -2936,7 +2936,7 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForParent( parentProcessItem = PhpLookupProcessItem(ProcessItem->ParentProcessId); - if (WindowsVersion >= WINDOWS_10_RS3) + 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. @@ -2970,7 +2970,7 @@ PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( processItem = PhpLookupProcessItem(Record->ProcessId); - if (WindowsVersion >= WINDOWS_10_RS3) + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) { if (processItem && processItem->ProcessSequenceNumber == Record->ProcessSequenceNumber) PhReferenceObject(processItem); From 6da8decc50f8e1ac6cc6e5a98104ee7c05bd1d97 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:46:14 +1100 Subject: [PATCH 695/839] Add PhGetServiceNameForModuleReference --- phlib/include/svcsup.h | 8 ++++++++ phlib/svcsup.c | 45 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/phlib/include/svcsup.h b/phlib/include/svcsup.h index ab3b9437c6c4..a968f54e21da 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 diff --git a/phlib/svcsup.c b/phlib/svcsup.c index 07e33948a58e..e5625cf6b93d 100644 --- a/phlib/svcsup.c +++ b/phlib/svcsup.c @@ -50,7 +50,7 @@ static PH_KEY_VALUE_PAIR PhpServiceTypePairs[] = 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), + SIP(L"User share process (instance)", SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE) }; static PH_KEY_VALUE_PAIR PhpServiceStartTypePairs[] = @@ -460,6 +460,49 @@ PPH_STRING PhGetServiceNameFromTag( 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 = PhGetModuleProcAddress(L"advapi32.dll", "I_QueryTagInformation"); + + 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, From 2c87ebeee8179466d60197c009cbe7a3c25d97de Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 02:54:55 +1100 Subject: [PATCH 696/839] Improve ProcessorBrandString query (experimental) --- ProcessHacker/syssccpu.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ProcessHacker/syssccpu.c b/ProcessHacker/syssccpu.c index a08ab77f489a..e26d1aa480e5 100644 --- a/ProcessHacker/syssccpu.c +++ b/ProcessHacker/syssccpu.c @@ -824,13 +824,23 @@ 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); + PhZeroExtendToUtf16Buffer(brandString, 48, BrandString); BrandString[48] = 0; } From 6fa0c7f21d10b1858b669403fc8a2d80bcc6e437 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 03:50:15 +1100 Subject: [PATCH 697/839] phlib: Add appmodel support functions --- phlib/appresolver.c | 242 ++++++++++++++++ phlib/include/appresolver.h | 43 +++ phlib/include/appresolverp.h | 527 +++++++++++++++++++++++++++++++++++ phlib/phlib.vcxproj | 3 + phlib/phlib.vcxproj.filters | 9 + 5 files changed, 824 insertions(+) create mode 100644 phlib/appresolver.c create mode 100644 phlib/include/appresolver.h create mode 100644 phlib/include/appresolverp.h diff --git a/phlib/appresolver.c b/phlib/appresolver.c new file mode 100644 index 000000000000..4082a433206b --- /dev/null +++ b/phlib/appresolver.c @@ -0,0 +1,242 @@ +/* + * 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 . + */ + +#define COBJMACROS +#define CINTERFACE +#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; +} + +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; +} + +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/include/appresolver.h b/phlib/include/appresolver.h new file mode 100644 index 000000000000..f1c31a91cb9e --- /dev/null +++ b/phlib/include/appresolver.h @@ -0,0 +1,43 @@ +/* + * 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 + ); + +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..c556904470e6 --- /dev/null +++ b/phlib/include/appresolverp.h @@ -0,0 +1,527 @@ +/* + * 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_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 } }; + +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) \ + ((This)->lpVtbl->GetNamedResource(This)) +#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/phlib.vcxproj b/phlib/phlib.vcxproj index b8ae87a725c6..c518c7699a96 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -140,6 +140,7 @@ + @@ -208,6 +209,8 @@ + + diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index 088326bcfc68..146a23ef377e 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -221,6 +221,9 @@ Source Files + + Source Files + @@ -466,5 +469,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file From 1573163d4ce2c021951969c1ffc287a42a9634bb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 03:56:03 +1100 Subject: [PATCH 698/839] Improve process AppId column (now shows the real AppUserModelId for all processes) --- ProcessHacker/proctree.c | 127 +++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 60 deletions(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 02a0b0e7e840..6288e19e5924 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -1125,71 +1126,77 @@ static VOID PhpUpdateProcessNodeAppId( if (!(ProcessNode->ValidMask & PHPN_APPID)) { ULONG windowFlags; - PPH_STRING windowTitle; + PPH_STRING applicationUserModelId; PhClearReference(&ProcessNode->AppIdText); - if (ProcessNode->ProcessItem->QueryHandle) + if (PhAppResolverGetAppIdForProcess(ProcessNode->ProcessItem->ProcessId, &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); - // } - //} - //else - - if (NT_SUCCESS(PhGetProcessWindowTitle( - ProcessNode->ProcessItem->QueryHandle, - &windowFlags, - &windowTitle - ))) + ProcessNode->AppIdText = applicationUserModelId; + } + else + { + if (ProcessNode->ProcessItem->QueryHandle) { - if (windowFlags & STARTF_TITLEISAPPID) - ProcessNode->AppIdText = windowTitle; - else - PhDereferenceObject(windowTitle); + 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); + // } + //} } } From 8348df1a4ae5686dfd5e47a68496f2d24bad33cb Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 03:58:30 +1100 Subject: [PATCH 699/839] Add AppId column to process node cache --- ProcessHacker/proctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 6288e19e5924..641dd4c0836e 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -647,7 +647,7 @@ VOID PhTickProcessNodes( // 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 + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS | PHPN_APPID; // Items that always remain valid // Invalidate graph buffers. node->CpuGraphBuffers.Valid = FALSE; From 3f1572babb6616f49ba7fac35c1d55029fba562b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 05:56:46 +1100 Subject: [PATCH 700/839] Add text trayicon support, Update View->Tray Icons menu --- ProcessHacker/ProcessHacker.def | 1 + ProcessHacker/ProcessHacker.rc | 9 +- ProcessHacker/include/mainwndp.h | 4 - ProcessHacker/include/notifico.h | 64 ++- ProcessHacker/include/notificop.h | 81 ++- ProcessHacker/include/phplug.h | 42 +- ProcessHacker/mainwnd.c | 126 +--- ProcessHacker/notifico.c | 920 ++++++++++++++++++++---------- ProcessHacker/plugin.c | 34 +- ProcessHacker/procprv.c | 2 +- ProcessHacker/resource.h | 3 +- ProcessHacker/settings.c | 5 +- phlib/graph.c | 98 ++++ phlib/include/graph.h | 9 + plugins/ExtendedTools/exttools.h | 2 +- plugins/ExtendedTools/iconext.c | 308 +++++++++- plugins/ExtendedTools/main.c | 18 +- 17 files changed, 1213 insertions(+), 513 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 6fe9f2e81071..16e4545ff152 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -466,6 +466,7 @@ EXPORTS ; graph PhDeleteGraphState PhDrawGraphDirect + PhDrawTrayIconText PhGetDrawInfoGraphBuffers PhGraphStateGetDrawInfo PhInitializeGraphState diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index c3de1b33c431..f96309641440 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -109,14 +109,7 @@ BEGIN 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 "&Tray icons", ID_VIEW_TRAYICONS MENUITEM SEPARATOR MENUITEM "
    ", ID_VIEW_SECTIONPLACEHOLDER MENUITEM SEPARATOR diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 13d25a772259..21351beb4ae3 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -185,10 +185,6 @@ VOID PhMwpInitializeSubMenu( _In_ ULONG Index ); -PPH_EMENU_ITEM PhMwpFindTrayIconsMenuItem( - _In_ PPH_EMENU Menu - ); - VOID PhMwpInitializeSectionMenuItems( _In_ PPH_EMENU Menu, _In_ ULONG StartIndex diff --git a/ProcessHacker/include/notifico.h b/ProcessHacker/include/notifico.h index b0a8d613c34e..d37459f2d095 100644 --- a/ProcessHacker/include/notifico.h +++ b/ProcessHacker/include/notifico.h @@ -1,14 +1,23 @@ #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 +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; + +#define PH_TRAY_ICON_ID_PLUGIN 0x80 #define PH_ICON_LIMIT 0x80000000 #define PH_ICON_ALL 0xffffffff @@ -29,7 +38,6 @@ typedef VOID (NTAPI *PPH_NF_BEGIN_BITMAP)( typedef struct _PH_NF_POINTERS { - PPH_NF_UPDATE_REGISTERED_ICON UpdateRegisteredIcon; PPH_NF_BEGIN_BITMAP BeginBitmap; } PH_NF_POINTERS, *PPH_NF_POINTERS; @@ -63,8 +71,9 @@ typedef struct _PH_NF_MSG_SHOWMINIINFOSECTION_DATA // Structures and internal functions -#define PH_NF_ICON_UNAVAILABLE 0x1 -#define PH_NF_ICON_SHOW_MINIINFO 0x2 +#define PH_NF_ICON_ENABLED 0x1 +#define PH_NF_ICON_UNAVAILABLE 0x2 +#define PH_NF_ICON_NOSHOW_MINIINFO 0x4 // end_phapppub // begin_phapppub @@ -85,6 +94,9 @@ typedef struct _PH_NF_ICON ULONG IconId; 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 @@ -110,16 +122,8 @@ VOID PhNfForwardMessage( _In_ ULONG_PTR LParam ); -ULONG PhNfGetMaximumIconId( - VOID - ); - -ULONG PhNfTestIconMask( - _In_ ULONG Id - ); - VOID PhNfSetVisibleIcon( - _In_ ULONG Id, + _In_ PPH_NF_ICON Icon, _In_ BOOLEAN Visible ); @@ -135,9 +139,18 @@ HICON PhNfBitmapToIcon( _In_ HBITMAP Bitmap ); +struct _PH_NF_ICON *PhNfPluginRegisterIcon( + _In_ struct _PH_PLUGIN * Plugin, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData + ); + PPH_NF_ICON PhNfRegisterIcon( _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, + _In_ ULONG Id, _In_opt_ PVOID Context, _In_ PWSTR Text, _In_ ULONG Flags, @@ -154,6 +167,10 @@ PPH_NF_ICON PhNfFindIcon( _In_ ULONG SubId ); +BOOLEAN PhNfIconsEnabled( + VOID + ); + VOID PhNfNotifyMiniInfoPinned( _In_ BOOLEAN Pinned ); @@ -168,4 +185,9 @@ typedef struct _PH_NF_ICON_REGISTRATION_DATA } PH_NF_ICON_REGISTRATION_DATA, *PPH_NF_ICON_REGISTRATION_DATA; // end_phapppub + +VOID PhShowTrayIconCustomizeDialog( + VOID +); + #endif diff --git a/ProcessHacker/include/notificop.h b/ProcessHacker/include/notificop.h index 00a993da9040..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( diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 2be5efb9d36c..a4a4c36d44b9 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -45,6 +45,7 @@ typedef enum _PH_GENERAL_CALLBACK GeneralCallbackMiniInformationInitializing = 32, // PPH_PLUGIN_MINIINFO_POINTERS Data [main thread] GeneralCallbackMiListSectionMenuInitializing = 33, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] GeneralCallbackOptionsWindowInitializing, // PPH_PLUGIN_OBJECT_PROPERTIES Data [main thread] + GeneralCallbackTrayIconsInitializing, GeneralCallbackMaximum } PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; @@ -327,6 +328,35 @@ typedef struct _PH_PLUGIN_MINIINFO_POINTERS } 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 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_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 { @@ -661,18 +691,6 @@ PhPluginGetObjectExtension( _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 diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 659779f72d12..0d7ef374a445 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -3,7 +3,7 @@ * Main window * * Copyright (C) 2009-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -199,7 +199,7 @@ BOOLEAN PhMainWndInitialization( UpdateWindow(PhMainWndHandle); - if ((PhStartupParameters.ShowHidden || PhGetIntegerSetting(L"StartHidden")) && PhNfTestIconMask(PH_ICON_ALL)) + if ((PhStartupParameters.ShowHidden || PhGetIntegerSetting(L"StartHidden")) && PhNfIconsEnabled()) ShowCommand = SW_HIDE; if (PhStartupParameters.ShowVisible) ShowCommand = SW_SHOW; @@ -544,7 +544,7 @@ VOID PhMwpOnCommand( { if (PhGetIntegerSetting(L"HideOnClose")) { - if (PhNfTestIconMask(PH_ICON_ALL)) + if (PhNfIconsEnabled()) ShowWindow(PhMainWndHandle, SW_HIDE); } else if (PhGetIntegerSetting(L"CloseOnEscape")) @@ -714,36 +714,6 @@ VOID PhMwpOnCommand( 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(); @@ -1544,7 +1514,7 @@ BOOLEAN PhMwpOnSysCommand( { case SC_CLOSE: { - if (PhGetIntegerSetting(L"HideOnClose") && PhNfTestIconMask(PH_ICON_ALL)) + if (PhGetIntegerSetting(L"HideOnClose") && PhNfIconsEnabled()) { ShowWindow(PhMainWndHandle, SW_HIDE); return TRUE; @@ -1556,7 +1526,7 @@ BOOLEAN PhMwpOnSysCommand( // Save the current window state because we may not have a chance to later. PhMwpSaveWindowState(); - if (PhGetIntegerSetting(L"HideOnMinimize") && PhNfTestIconMask(PH_ICON_ALL)) + if (PhGetIntegerSetting(L"HideOnMinimize") && PhNfIconsEnabled()) { ShowWindow(PhMainWndHandle, SW_HIDE); return TRUE; @@ -2325,7 +2295,7 @@ VOID PhMwpDispatchMenuCommand( PPH_NF_ICON icon; icon = menuItem->Context; - PhNfSetVisibleIcon(icon->IconId, !PhNfTestIconMask(icon->IconId)); + PhNfSetVisibleIcon(icon, !(icon->Flags & PH_NF_ICON_ENABLED)); } return; @@ -2459,71 +2429,31 @@ VOID PhMwpInitializeSubMenu( ULONG id; ULONG placeholderIndex; - trayIconsMenuItem = PhMwpFindTrayIconsMenuItem(Menu); - - if (trayIconsMenuItem) + if (trayIconsMenuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, ID_VIEW_TRAYICONS)) { - 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) + for (i = 0; i < PhTrayIconItemList->Count; i++) { - if (icon = PhNfGetIconById(id)) - { - PhInsertEMenuItem(trayIconsMenuItem, PhCreateEMenuItem(0, ID_TRAYICONS_REGISTERED, icon->Text, NULL, icon), -1); - } - } + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - // Update the text and check marks on the menu items. - - for (i = 0; i < trayIconsMenuItem->Items->Count; i++) - { - menuItem = trayIconsMenuItem->Items->Items[i]; + menuItem = PhCreateEMenuItem(0, ID_TRAYICONS_REGISTERED, icon->Text, NULL, icon); + PhInsertEMenuItem(trayIconsMenuItem, menuItem, -1); - id = -1; - icon = NULL; + // Update the text and check marks on the menu items. - switch (menuItem->Id) + if (icon->Flags & PH_NF_ICON_ENABLED) { - 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; + menuItem->Flags |= PH_EMENU_CHECKED; } - if (id != -1) + if (icon->Flags & PH_NF_ICON_UNAVAILABLE) { - if (PhNfTestIconMask(id)) - menuItem->Flags |= PH_EMENU_CHECKED; + PPH_STRING newText; - 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); - } + newText = PhaConcatStrings2(icon->Text, L" (Unavailable)"); + PhModifyEMenuItem(menuItem, PH_EMENU_MODIFY_TEXT, PH_EMENU_TEXT_OWNED, + PhAllocateCopy(newText->Buffer, newText->Length + sizeof(WCHAR)), NULL); } } } @@ -2592,24 +2522,6 @@ VOID PhMwpInitializeSubMenu( } } -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 diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 4b72394b0adf..e88f771870c2 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -3,6 +3,7 @@ * notification icon manager * * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -36,12 +37,6 @@ #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; @@ -57,127 +52,164 @@ static PH_NF_MSG_SHOWMINIINFOSECTION_DATA IconClickShowMiniInfoSectionData; static BOOLEAN IconClickUpDueToDown; static BOOLEAN IconDisableHover; +// Note: no lock is needed because we only ever modify the list on this same thread. +PPH_LIST PhTrayIconItemList = NULL; + VOID PhNfLoadStage1( VOID ) { - PPH_STRING iconList; - PH_STRINGREF part; - PH_STRINGREF remainingPart; + PhTrayIconItemList = PhCreateList(20); - PhNfpPointers.UpdateRegisteredIcon = PhNfpUpdateRegisteredIcon; PhNfpPointers.BeginBitmap = PhNfpBeginBitmap; +} + +VOID PhNfLoadSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRINGREF remaining; + + settingsString = PhGetStringSetting(L"IconSettings"); + remaining = settingsString->sr; - // Load settings for default icons. - PhNfIconMask = PhGetIntegerSetting(L"IconMask"); + if (remaining.Length == 0) + { + PPH_NF_ICON icon; - // Load settings for registered icons. + // Load default settings. + if (icon = PhNfGetIconById(PH_TRAY_ICON_ID_CPU_USAGE)) + icon->Flags |= PH_NF_ICON_ENABLED; - iconList = PhGetStringSetting(L"IconMaskList"); - remainingPart = iconList->sr; + PhDereferenceObject(settingsString); + return; + } - while (remainingPart.Length != 0) + while (remaining.Length != 0) { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + 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 (part.Length != 0) + if (flagsInteger) { - PH_STRINGREF pluginName; - ULONG subId; PPH_NF_ICON icon; - if (PhEmParseCompoundId(&part, &pluginName, &subId) && - (icon = PhNfFindIcon(&pluginName, subId))) + if (icon = PhNfFindIcon(&pluginNamePart, (ULONG)idInteger)) { - PhNfIconMask |= icon->IconId; + icon->Flags |= PH_NF_ICON_ENABLED; } } } - PhDereferenceObject(iconList); + PhDereferenceObject(settingsString); } -VOID PhNfLoadStage2( +VOID PhNfSaveSettings( VOID ) { - ULONG i; + PPH_STRING settingsString; + PH_STRING_BUILDER iconListBuilder; - PhNfMiniInfoEnabled = !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); + PhInitializeStringBuilder(&iconListBuilder, 100); - for (i = PH_ICON_MINIMUM; i != PhNfMaximumIconId; i <<= 1) + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - if (PhNfIconMask & i) - PhNfpAddNotifyIcon(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"" + ); } - PhRegisterCallback( - &PhProcessesUpdatedEvent, - PhNfpProcessesUpdatedHandler, - NULL, - &PhNfpProcessesUpdatedRegistration - ); + if (iconListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&iconListBuilder, 1); + + settingsString = PhFinalStringBuilderString(&iconListBuilder); + PhSetStringSetting2(L"IconSettings", &settingsString->sr); + PhDereferenceObject(settingsString); } -VOID PhNfSaveSettings( +VOID PhNfLoadStage2( VOID ) { - ULONG registeredIconMask; - - PhSetIntegerSetting(L"IconMask", PhNfIconMask & PH_ICON_DEFAULT_ALL); - - registeredIconMask = PhNfIconMask & ~PH_ICON_DEFAULT_ALL; + PhNfMiniInfoEnabled = !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); - if (registeredIconMask != 0) + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_USAGE, NULL, L"CPU &usage", 0, PhNfpCpuUsageIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_HISTORY, NULL, L"CPU &history", 0, PhNfpCpuHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_IO_HISTORY, NULL, L"&I/O history", 0, PhNfpIoHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_COMMIT_HISTORY, NULL, L"&Commit charge history", 0, PhNfpCommitHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_PHYSICAL_HISTORY, NULL, L"&Physical memory history", 0, PhNfpPhysicalHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_TEXT, NULL, L"CPU usage (text)", 0, PhNfpCpuUsageTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_IO_TEXT, NULL, L"IO usage (text)", 0, PhNfpIoUsageTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_COMMIT_TEXT, NULL, L"Commit usage (text)", 0, PhNfpCommitTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_PHYSICAL_TEXT, NULL, L"Physical usage (text)", 0, PhNfpPhysicalUsageTextIconUpdateCallback, NULL); + + if (PhPluginsEnabled) { - PH_STRING_BUILDER iconListBuilder; - ULONG i; + PH_TRAY_ICON_POINTERS pointers; - PhInitializeStringBuilder(&iconListBuilder, 60); + pointers.RegisterTrayIcon = PhNfPluginRegisterIcon; - 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 - ); - } - } - } + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackTrayIconsInitializing), &pointers); + } - if (iconListBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&iconListBuilder, 1); + // Load tray icon settings. + PhNfLoadSettings(); - PhSetStringSetting2(L"IconMaskList", &iconListBuilder.String->sr); - PhDeleteStringBuilder(&iconListBuilder); - } - else + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - PhSetStringSetting(L"IconMaskList", L""); + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpAddNotifyIcon(icon); } + + PhRegisterCallback( + &PhProcessesUpdatedEvent, + PhNfpProcessesUpdatedHandler, + NULL, + &PhNfpProcessesUpdatedRegistration + ); } 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) + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - if (PhNfIconMask & i) - PhNfpRemoveNotifyIcon(i); + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpRemoveNotifyIcon(icon); } } @@ -189,21 +221,33 @@ VOID PhNfForwardMessage( ULONG iconIndex = HIWORD(LParam); PPH_NF_ICON registeredIcon = NULL; - if (iconIndex < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON) && PhNfRegisteredIcons[iconIndex]) + if (iconIndex == 0) + return; + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - registeredIcon = PhNfRegisteredIcons[iconIndex]; + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - if (registeredIcon->MessageCallback) + if (icon->IconId == iconIndex) { - if (registeredIcon->MessageCallback( - registeredIcon, - WParam, - LParam, - registeredIcon->Context - )) - { - return; - } + registeredIcon = icon; + break; + } + } + + if (!registeredIcon) + return; + + if (registeredIcon->MessageCallback) + { + if (registeredIcon->MessageCallback( + registeredIcon, + WParam, + LParam, + registeredIcon->Context + )) + { + return; } } @@ -309,34 +353,20 @@ VOID PhNfForwardMessage( } } -ULONG PhNfGetMaximumIconId( - VOID - ) -{ - return PhNfMaximumIconId; -} - -ULONG PhNfTestIconMask( - _In_ ULONG Id - ) -{ - return PhNfIconMask & Id; -} - VOID PhNfSetVisibleIcon( - _In_ ULONG Id, + _In_ PPH_NF_ICON Icon, _In_ BOOLEAN Visible ) { if (Visible) { - PhNfIconMask |= Id; - PhNfpAddNotifyIcon(Id); + Icon->Flags |= PH_NF_ICON_ENABLED; + PhNfpAddNotifyIcon(Icon); } else { - PhNfIconMask &= ~Id; - PhNfpRemoveNotifyIcon(Id); + Icon->Flags &= ~PH_NF_ICON_ENABLED; + PhNfpRemoveNotifyIcon(Icon); } } @@ -348,22 +378,32 @@ BOOLEAN PhNfShowBalloonTip( _In_ ULONG Flags ) { - NOTIFYICONDATA notifyIcon = { NOTIFYICONDATA_V3_SIZE }; + NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; + PPH_NF_ICON registeredIcon = NULL; + ULONG iconID = Id; - if (Id == 0) + if (iconID == 0) { - // Choose the first visible icon. - Id = PhNfIconMask; - } + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - if (!_BitScanForward(&Id, Id)) - return FALSE; + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + iconID = icon->IconId; + break; + } + + if (iconID == 0) + return FALSE; + } notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = Id; + notifyIcon.uID = iconID; 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); + wcsncpy_s(notifyIcon.szInfoTitle, ARRAYSIZE(notifyIcon.szInfoTitle), Title, _TRUNCATE); + wcsncpy_s(notifyIcon.szInfo, ARRAYSIZE(notifyIcon.szInfo), Text, _TRUNCATE); notifyIcon.uTimeout = Timeout; notifyIcon.dwInfoFlags = Flags; @@ -391,7 +431,7 @@ HICON PhNfBitmapToIcon( PPH_NF_ICON PhNfRegisterIcon( _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, + _In_ ULONG Id, _In_opt_ PVOID Context, _In_ PWSTR Text, _In_ ULONG Flags, @@ -400,83 +440,112 @@ PPH_NF_ICON PhNfRegisterIcon( ) { 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->SubId = Id; icon->Context = Context; icon->Pointers = &PhNfpPointers; icon->Text = Text; icon->Flags = Flags; - icon->IconId = iconId; icon->UpdateCallback = UpdateCallback; icon->MessageCallback = MessageCallback; + icon->TextCache = PhReferenceEmptyString(); + icon->IconId = PhTrayIconItemList->Count + 1; // HACK - PhNfRegisteredIcons[iconIndex] = icon; + PhAddItemList(PhTrayIconItemList, icon); return icon; } +struct _PH_NF_ICON *PhNfPluginRegisterIcon( + _In_ struct _PH_PLUGIN * Plugin, + _In_ ULONG Id, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData + ) +{ + return PhNfRegisterIcon( + Plugin, + Id, + Context, + Text, + Flags, + RegistrationData->UpdateCallback, + RegistrationData->MessageCallback + ); +} + PPH_NF_ICON PhNfGetIconById( - _In_ ULONG Id + _In_ ULONG SubId ) { - ULONG iconIndex; + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - if (!_BitScanReverse(&iconIndex, Id)) - return NULL; + if (icon->SubId == SubId) + return icon; + } - return PhNfRegisteredIcons[iconIndex]; + return NULL; } PPH_NF_ICON PhNfFindIcon( - _In_ PPH_STRINGREF PluginName, + _In_opt_ PPH_STRINGREF PluginName, _In_ ULONG SubId ) { - ULONG i; - - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - if (PhNfRegisteredIcons[i]) + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (PluginName) { - if (PhNfRegisteredIcons[i]->SubId == SubId && - PhEqualStringRef(PluginName, &PhNfRegisteredIcons[i]->Plugin->AppContext.AppName, FALSE)) + if ( + icon->SubId == SubId && + (icon->Plugin ? PhEqualStringRef(PluginName, &icon->Plugin->AppContext.AppName, TRUE) : TRUE) + ) { - return PhNfRegisteredIcons[i]; + return icon; } } + else + { + if (icon->SubId == SubId) + 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 ) { - ULONG i; - ULONG id; - if (PhNfMiniInfoPinned != Pinned) { PhNfMiniInfoPinned = Pinned; @@ -484,14 +553,15 @@ VOID PhNfNotifyMiniInfoPinned( // 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++) + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - id = 1 << i; + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - if (PhNfIconMask & id) - { - PhNfpModifyNotifyIcon(id, NIF_TIP, PhNfIconTextCache[i], NULL); - } + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpModifyNotifyIcon(icon, NIF_TIP, icon->TextCache, NULL); } } } @@ -526,34 +596,26 @@ HICON PhNfpGetBlackIcon( } BOOLEAN PhNfpAddNotifyIcon( - _In_ ULONG Id + _In_ PPH_NF_ICON Icon ) { 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)) + if (PhMainWndExiting) return FALSE; notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = Id; + notifyIcon.uID = Icon->IconId; 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), + PhGetStringOrDefault(Icon->TextCache, PhApplicationName), _TRUNCATE ); notifyIcon.hIcon = PhNfpGetBlackIcon(); - if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (icon && !(icon->Flags & PH_NF_ICON_SHOW_MINIINFO))) + if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (Icon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) notifyIcon.uFlags |= NIF_SHOWTIP; Shell_NotifyIcon(NIM_ADD, ¬ifyIcon); @@ -565,20 +627,13 @@ BOOLEAN PhNfpAddNotifyIcon( } BOOLEAN PhNfpRemoveNotifyIcon( - _In_ ULONG Id + _In_ PPH_NF_ICON Icon ) { 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; + notifyIcon.uID = Icon->IconId; Shell_NotifyIcon(NIM_DELETE, ¬ifyIcon); @@ -586,88 +641,90 @@ BOOLEAN PhNfpRemoveNotifyIcon( } 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 ) { NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; - PPH_NF_ICON icon; - ULONG notifyId; - if (PhNfTerminating) - return FALSE; - if ((icon = PhNfGetIconById(Id)) && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) + if (PhMainWndExiting) return FALSE; - - if (!_BitScanForward(¬ifyId, Id)) + if (Icon->Flags & PH_NF_ICON_UNAVAILABLE) return FALSE; notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = notifyId; + notifyIcon.uID = Icon->IconId; notifyIcon.uFlags = Flags; + notifyIcon.hIcon = IconHandle; + + if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (Icon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) + notifyIcon.uFlags |= NIF_SHOWTIP; if (Flags & NIF_TIP) { - PhSwapReference(&PhNfIconTextCache[notifyId], Text); + PhSwapReference(&Icon->TextCache, Text); wcsncpy_s( notifyIcon.szTip, - sizeof(notifyIcon.szTip) / sizeof(WCHAR), + ARRAYSIZE(notifyIcon.szTip), 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); + 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 ) { - 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) + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) { - ULONG i; + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) - { - if (PhNfRegisteredIcons[i] && (registeredIconMask & PhNfRegisteredIcons[i]->IconId)) - { - PhNfpUpdateRegisteredIcon(PhNfRegisteredIcons[i]); - } - } + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpUpdateRegisteredIcon(icon); } } @@ -711,7 +768,7 @@ VOID PhNfpUpdateRegisteredIcon( flags |= NIF_TIP; if (flags != 0) - PhNfpModifyNotifyIcon(Icon->IconId, flags, newText, newIcon); + PhNfpModifyNotifyIcon(Icon, flags, newText, newIcon); if (newIcon && (updateFlags & PH_NF_UPDATE_IS_BITMAP)) DestroyIcon(newIcon); @@ -778,8 +835,12 @@ VOID PhNfpBeginBitmap2( *OldBitmap = SelectObject(Context->Hdc, Context->Bitmap); } -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 ) { static PH_GRAPH_DRAW_INFO drawInfo = @@ -789,7 +850,6 @@ VOID PhNfpUpdateIconCpuHistory( PH_GRAPH_USE_LINE_2, 2, RGB(0x00, 0x00, 0x00), - 16, NULL, NULL, @@ -806,15 +866,13 @@ VOID PhNfpUpdateIconCpuHistory( 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); + 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)); @@ -835,7 +893,8 @@ VOID PhNfpUpdateIconCpuHistory( PhDrawGraphDirect(hdc, bits, &drawInfo); SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; // Text @@ -862,17 +921,16 @@ VOID PhNfpUpdateIconCpuHistory( PhInitFormatC(&format[7], '%'); } - text = PhFormat(format, maxCpuProcessItem ? 8 : 3, 128); + *NewText = 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 +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 = @@ -882,7 +940,6 @@ VOID PhNfpUpdateIconIoHistory( PH_GRAPH_USE_LINE_2, 2, RGB(0x00, 0x00, 0x00), - 16, NULL, NULL, @@ -901,15 +958,13 @@ VOID PhNfpUpdateIconIoHistory( 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); + 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)); @@ -944,7 +999,8 @@ VOID PhNfpUpdateIconIoHistory( PhDrawGraphDirect(hdc, bits, &drawInfo); SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; // Text @@ -971,17 +1027,16 @@ VOID PhNfpUpdateIconIoHistory( PhInitFormatSR(&format[7], maxIoProcessItem->ProcessName->sr); } - text = PhFormat(format, maxIoProcessItem ? 8 : 6, 128); + *NewText = 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 +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 = @@ -991,7 +1046,6 @@ VOID PhNfpUpdateIconCommitHistory( 0, 2, RGB(0x00, 0x00, 0x00), - 16, NULL, NULL, @@ -1008,14 +1062,12 @@ VOID PhNfpUpdateIconCommitHistory( 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); + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); maxDataCount = drawInfo.Width / 2 + 1; lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); @@ -1035,7 +1087,8 @@ VOID PhNfpUpdateIconCommitHistory( PhDrawGraphDirect(hdc, bits, &drawInfo); SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; // Text @@ -1047,16 +1100,15 @@ VOID PhNfpUpdateIconCommitHistory( 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); + *NewText = PhFormat(format, 5, 96); } -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 ) { static PH_GRAPH_DRAW_INFO drawInfo = @@ -1066,7 +1118,6 @@ VOID PhNfpUpdateIconPhysicalHistory( 0, 2, RGB(0x00, 0x00, 0x00), - 16, NULL, NULL, @@ -1083,19 +1134,17 @@ VOID PhNfpUpdateIconPhysicalHistory( 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); + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); maxDataCount = drawInfo.Width / 2 + 1; lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - lineDataCount = min(maxDataCount, PhCommitHistory.Count); + lineDataCount = min(maxDataCount, PhPhysicalHistory.Count); for (i = 0; i < lineDataCount; i++) lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, i); @@ -1111,7 +1160,8 @@ VOID PhNfpUpdateIconPhysicalHistory( PhDrawGraphDirect(hdc, bits, &drawInfo); SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; // Text @@ -1124,16 +1174,15 @@ VOID PhNfpUpdateIconPhysicalHistory( 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); + *NewText = PhFormat(format, 5, 96); } -VOID PhNfpUpdateIconCpuUsage( - VOID +VOID PhNfpCpuUsageIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ) { ULONG width; @@ -1141,15 +1190,13 @@ VOID PhNfpUpdateIconCpuUsage( 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); + Icon->Pointers->BeginBitmap(&width, &height, &bitmap, NULL, &hdc, &oldBitmap); // This stuff is copied from CpuUsageIcon.cs (PH 1.x). { @@ -1226,7 +1273,8 @@ VOID PhNfpUpdateIconCpuUsage( } SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; // Text @@ -1248,13 +1296,289 @@ VOID PhNfpUpdateIconCpuUsage( } } - text = PhFormatString(L"CPU usage: %.2f%%%s", (PhCpuKernelUsage + PhCpuUserUsage) * 100, PhGetStringOrEmpty(maxCpuText)); + *NewText = 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); +// Text icons - DestroyIcon(icon); +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); + + SelectObject(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); + + SelectObject(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); + + SelectObject(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); + + SelectObject(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( @@ -1265,11 +1589,11 @@ BOOLEAN PhNfpGetShowMiniInfoSectionData( { BOOLEAN showMiniInfo = FALSE; - if (RegisteredIcon) + if (RegisteredIcon && RegisteredIcon->MessageCallback) { Data->SectionName = NULL; - if (RegisteredIcon->Flags & PH_NF_ICON_SHOW_MINIINFO) + if (!(RegisteredIcon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) { if (RegisteredIcon->MessageCallback) { @@ -1286,19 +1610,23 @@ BOOLEAN PhNfpGetShowMiniInfoSectionData( } else { - switch (1 << IconIndex) + switch (IconIndex) { - case PH_ICON_CPU_HISTORY: - case PH_ICON_CPU_USAGE: + 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_ICON_IO_HISTORY: + case PH_TRAY_ICON_ID_IO_HISTORY: + case PH_TRAY_ICON_ID_IO_TEXT: Data->SectionName = L"I/O"; break; - case PH_ICON_COMMIT_HISTORY: + case PH_TRAY_ICON_ID_COMMIT_HISTORY: + case PH_TRAY_ICON_ID_COMMIT_TEXT: Data->SectionName = L"Commit charge"; break; - case PH_ICON_PHYSICAL_HISTORY: + case PH_TRAY_ICON_ID_PHYSICAL_HISTORY: + case PH_TRAY_ICON_ID_PHYSICAL_TEXT: Data->SectionName = L"Physical memory"; break; } diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index a62dd703b31e..5dde37daed95 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -3,6 +3,7 @@ * plugin support * * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -913,39 +914,6 @@ PVOID PhPluginGetObjectExtension( ); } -/** - * 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. * diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index d7dd2fa7d73e..a7973d3403d1 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -3,7 +3,7 @@ * process provider * * Copyright (C) 2009-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 7b5748ed6395..1aac68d75d05 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -129,6 +129,7 @@ #define IDD_PLUGINSDISABLED 241 #define IDD_PROCWMIPROVIDERS 242 #define IDD_COLUMNSETS 243 +#define ID_VIEW_TRAYICONS 244 #define IDC_TERMINATE 1003 #define IDC_FILEICON 1005 #define IDC_FILE 1006 @@ -738,7 +739,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 244 +#define _APS_NEXT_RESOURCE_VALUE 245 #define _APS_NEXT_COMMAND_VALUE 40297 #define _APS_NEXT_CONTROL_VALUE 1404 #define _APS_NEXT_SYMED_VALUE 170 diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 2021120adece..3ab7aa1b0f65 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -3,7 +3,7 @@ * program settings cache * * Copyright (C) 2010-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -76,8 +76,7 @@ VOID PhAddDefaultSettings( 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""); + 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"); diff --git a/phlib/graph.c b/phlib/graph.c index 6bcc69ab67aa..86e6a4b767cc 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -3,6 +3,7 @@ * graph control * * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -632,6 +633,103 @@ VOID PhSetGraphText( DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle); } + +static HFONT PhpTrayIconFont( + 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( + _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 = SelectObject(hdc, DrawInfo->TextFont); + + DrawInfo->Text = *Text; + GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / 2, &textSize); + + // Calculate the box rectangle. + + boxRectangle.Width = textSize.cx; + boxRectangle.Height = textSize.cy; + boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / 2; + boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / 2; + + // 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, 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 | DT_SINGLELINE); + + if (oldFont) + SelectObject(hdc, oldFont); +} + VOID PhpCreateGraphContext( _Out_ PPHP_GRAPH_CONTEXT *Context ) diff --git a/phlib/include/graph.h b/phlib/include/graph.h index bc15a12d6039..2b10225c5954 100644 --- a/phlib/include/graph.h +++ b/phlib/include/graph.h @@ -92,6 +92,15 @@ VOID PhSetGraphText( _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 diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index 410615b3d068..c3ec61004af1 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -505,7 +505,7 @@ VOID EtGpuMiniInformationInitializing( // iconext VOID EtRegisterNotifyIcons( - VOID + _In_ PPH_TRAY_ICON_POINTERS Pointers ); // modsrv diff --git a/plugins/ExtendedTools/iconext.c b/plugins/ExtendedTools/iconext.c index b073696f3ef4..3bdec4ac9749 100644 --- a/plugins/ExtendedTools/iconext.c +++ b/plugins/ExtendedTools/iconext.c @@ -25,6 +25,9 @@ #define GPU_ICON_ID 1 #define DISK_ICON_ID 2 #define NETWORK_ICON_ID 3 +#define GPU_ICON_TEXT_ID 4 +#define DISK_ICON_TEXT_ID 5 +#define NETWORK_ICON_TEXT_ID 6 VOID EtpGpuIconUpdateCallback( _In_ struct _PH_NF_ICON *Icon, @@ -71,8 +74,32 @@ BOOLEAN EtpNetworkIconMessageCallback( _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 + ); + VOID EtRegisterNotifyIcons( - VOID + _In_ PPH_TRAY_ICON_POINTERS Pointers ) { PH_NF_ICON_REGISTRATION_DATA data; @@ -81,34 +108,67 @@ VOID EtRegisterNotifyIcons( data.UpdateCallback = EtpGpuIconUpdateCallback; data.MessageCallback = EtpGpuIconMessageCallback; - PhPluginRegisterIcon( + Pointers->RegisterTrayIcon( PluginInstance, GPU_ICON_ID, NULL, L"&GPU history", - PH_NF_ICON_SHOW_MINIINFO | (EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), + EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, &data ); data.UpdateCallback = EtpDiskIconUpdateCallback; data.MessageCallback = EtpDiskIconMessageCallback; - PhPluginRegisterIcon( + Pointers->RegisterTrayIcon( PluginInstance, DISK_ICON_ID, NULL, L"&Disk history", - PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, &data ); data.UpdateCallback = EtpNetworkIconUpdateCallback; data.MessageCallback = EtpNetworkIconMessageCallback; - PhPluginRegisterIcon( + Pointers->RegisterTrayIcon( PluginInstance, NETWORK_ICON_ID, NULL, L"&Network history", - PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpGpuTextIconUpdateCallback; + data.MessageCallback = EtpGpuIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + GPU_ICON_TEXT_ID, + NULL, + L"&GPU usage (Text)", + EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpDiskTextIconUpdateCallback; + data.MessageCallback = EtpDiskIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + DISK_ICON_TEXT_ID, + NULL, + L"&Disk usage (Text)", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpNetworkIconUpdateCallback; + data.MessageCallback = EtpNetworkIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + NETWORK_ICON_TEXT_ID, + NULL, + L"&Network usage (Text)", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, &data ); } @@ -464,3 +524,237 @@ BOOLEAN EtpNetworkIconMessageCallback( 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); + + 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); +} + +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); + + 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); +} + +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); + + 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); +} diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index f552223ac6de..0b745fd3e5ac 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -42,6 +42,7 @@ PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION SystemInformationInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION MiniInformationInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION TrayIconsInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; PH_CALLBACK_REGISTRATION NetworkItemsUpdatedCallbackRegistration; @@ -54,8 +55,6 @@ VOID NTAPI LoadCallback( { EtEtwStatisticsInitialization(); EtGpuMonitorInitialization(); - - EtRegisterNotifyIcons(); } VOID NTAPI UnloadCallback( @@ -299,6 +298,14 @@ VOID NTAPI MiniInformationInitializingCallback( EtEtwMiniInformationInitializing(Parameter); } +VOID NTAPI TrayIconsInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtRegisterNotifyIcons(Parameter); +} + VOID NTAPI ProcessesUpdatedCallback( _In_opt_ PVOID Parameter, _In_opt_ PVOID Context @@ -571,6 +578,13 @@ LOGICAL DllMain( &MiniInformationInitializingCallbackRegistration ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackTrayIconsInitializing), + TrayIconsInitializingCallback, + NULL, + &TrayIconsInitializingCallbackRegistration + ); + PhRegisterCallback( &PhProcessesUpdatedEvent, ProcessesUpdatedCallback, From e317b8e16b73d37011b696086270fe5bf9b3d3d9 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 05:58:09 +1100 Subject: [PATCH 701/839] Remove deprecated icons --- ProcessHacker/ProcessHacker.rc | 4 ---- ProcessHacker/ProcessHacker.vcxproj | 2 -- ProcessHacker/ProcessHacker.vcxproj.filters | 6 ------ ProcessHacker/resources/application_go.ico | Bin 9574 -> 0 bytes ProcessHacker/resources/cog_go.ico | Bin 9574 -> 0 bytes 5 files changed, 12 deletions(-) delete mode 100644 ProcessHacker/resources/application_go.ico delete mode 100644 ProcessHacker/resources/cog_go.ico diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index f96309641440..f057df71c700 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -58,10 +58,6 @@ 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" diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index f5b99fe1b1a1..196b6949ee97 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -404,9 +404,7 @@ - - diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 3a8cdc58efd6..550d5b2b5c2b 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -571,12 +571,6 @@ Resources - - Resources - - - Resources - Module diff --git a/ProcessHacker/resources/application_go.ico b/ProcessHacker/resources/application_go.ico deleted file mode 100644 index 0a72a48bd77ec0b36c4fe9ad3de5ab80e0df27fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9574 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 diff --git a/ProcessHacker/resources/cog_go.ico b/ProcessHacker/resources/cog_go.ico deleted file mode 100644 index d679437a07c48a69dbe683afec677fafd3e34879..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9574 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 SX Date: Sat, 20 Jan 2018 08:02:00 +1100 Subject: [PATCH 702/839] Always display tooltips even when the window doesn't have focus (https://wj32.org/processhacker/forums/viewtopic.php?t=186) --- phlib/graph.c | 2 +- phlib/treenew.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phlib/graph.c b/phlib/graph.c index 86e6a4b767cc..fb7032e4f921 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -1281,7 +1281,7 @@ LRESULT CALLBACK PhpGraphWndProc( context->TooltipHandle = CreateWindow( TOOLTIPS_CLASS, NULL, - WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX, + WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, diff --git a/phlib/treenew.c b/phlib/treenew.c index 639d57cf4a17..635759affda9 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -5735,7 +5735,7 @@ VOID PhTnpInitializeTooltips( WS_EX_TRANSPARENT, // solves double-click problem TOOLTIPS_CLASS, NULL, - WS_POPUP | TTS_NOPREFIX, + WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 0, 0, 0, From 6ea0d664be0acf3bba040aa8584505bad7fd3b7e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 09:08:02 +1100 Subject: [PATCH 703/839] Remove unused code --- ProcessHacker/notifico.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index e88f771870c2..29b0f66b87c8 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -142,9 +142,6 @@ VOID PhNfSaveSettings( ); } - if (iconListBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&iconListBuilder, 1); - settingsString = PhFinalStringBuilderString(&iconListBuilder); PhSetStringSetting2(L"IconSettings", &settingsString->sr); PhDereferenceObject(settingsString); From 8aab7a17ee6e91ce37eddc4f19a05501d19143c0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 Jan 2018 09:08:51 +1100 Subject: [PATCH 704/839] Fix refreshing plugin nodes after disabling a plugin --- ProcessHacker/plugman.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index 70d9d552b4d6..6d5462c518ea 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -3,7 +3,7 @@ * plugins * * Copyright (C) 2010-2011 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -782,9 +782,8 @@ INT_PTR CALLBACK PhpPluginsDlgProc( PhSetPluginDisabled(&baseNameRef, TRUE); - ClearPluginsTree(context); - PhpEnumerateLoadedPlugins(context); - TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + RemovePluginsNode(context, selectedNode); + SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); } break; From 528b134944066905c1fc5a126279559c97443712 Mon Sep 17 00:00:00 2001 From: lucasg Date: Sun, 21 Jan 2018 08:21:18 +0100 Subject: [PATCH 705/839] Make PH_IMAGE_RESOURCE_ENTRY.Data return a VA instead of a RVA (#227) PIMAGE_RESOURCE_DATA_ENTRY return a RVA per the PE-COFF specification, so we need to convert it into a VA before using it. --- phlib/mapimg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 5f9086de1dd7..8e8da7003868 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1688,7 +1688,7 @@ NTSTATUS PhGetMappedImageResources( 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 = PTR_ADD_OFFSET(MappedImage->ViewBase, resourceData->OffsetToData); + Resources->ResourceEntries[resourceIndex].Data = PhMappedImageRvaToVa(MappedImage, resourceData->OffsetToData, NULL); Resources->ResourceEntries[resourceIndex++].Size = resourceData->Size; } } From ffe6d9347ffa0e3956ac05899ecec9e8440683f3 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 Jan 2018 12:26:39 +1100 Subject: [PATCH 706/839] Update CustomBuildTools binaries --- tools/CustomBuildTool/Source Files/Build.cs | 3 ++- .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index ac7a3f1f6170..8dac9e821fc5 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -2,7 +2,7 @@ * Process Hacker Toolchain - * Build script * - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -118,6 +118,7 @@ public static class Build "dltmgr.h", "dspick.h", "emenu.h", + "exlf.h", "fastlock.h", "filestream.h", "graph.h", diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 6d4f94ed7598186a47d75359070eba401bea087f..109a98cde269e8b23dc2f5243c9aa522fd4c41f4 100644 GIT binary patch delta 14622 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 CaAYU| delta 14471 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 596e48415e9b89b3d78904c08a570cc77e4b95ca..7b5e32c5e289a00b0c3ec023fefa7f253c862c82 100644 GIT binary patch delta 15269 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 Date: Tue, 23 Jan 2018 12:27:18 +1100 Subject: [PATCH 707/839] Fix missing 'PhUnloadMappedImage' function export --- ProcessHacker/ProcessHacker.def | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 16e4545ff152..fcb0537c4d8a 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -534,6 +534,7 @@ EXPORTS PhGetMappedImageLoadConfig64 PhInitializeMappedImage PhLoadMappedImage + PhUnloadMappedImage ; settings PhAddSetting From 0aa25a2b07b4a7a3dfb07818d75db46831ca37c4 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 Jan 2018 12:27:48 +1100 Subject: [PATCH 708/839] Plugins: Improve CommonBitmapToIcon --- plugins/include/commonutil.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index 57d730baee46..18f4aea48cd2 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -37,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; @@ -47,7 +47,7 @@ CommonBitmapToIcon( icon = CreateIconIndirect(&iconInfo); DeleteObject(screenBitmap); - DeleteDC(screenDc); + ReleaseDC(NULL, screenDc); return icon; } From 3419fdd34ff82739e41611637adb24fb99631f3a Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 Jan 2018 13:55:19 +1100 Subject: [PATCH 709/839] Add MiniInfoWindowClassName setting --- ProcessHacker/miniinfo.c | 11 +++++++---- ProcessHacker/settings.c | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index f6b151608b98..adf3e23e627b 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -104,6 +104,8 @@ VOID PhPinMiniInformation( if (!PhMipContainerWindow) { WNDCLASSEX wcex; + RTL_ATOM windowAtom; + PPH_STRING className; memset(&wcex, 0, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); @@ -115,13 +117,14 @@ VOID PhPinMiniInformation( wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; + className = PhaGetStringSetting(L"MiniInfoWindowClassName"); + wcex.lpszClassName = PhGetStringOrDefault(className, L"MiniInfoWindowClassName"); wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - RegisterClassEx(&wcex); + windowAtom = RegisterClassEx(&wcex); PhMipContainerWindow = CreateWindow( - MIP_CONTAINER_CLASSNAME, - L"Process Hacker", + MAKEINTATOM(windowAtom), + PhGetIntegerSetting(L"EnableWindowText") ? L"Process Hacker" : NULL, WS_BORDER | WS_THICKFRAME | WS_POPUP, 0, 0, diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 3ab7aa1b0f65..e571b1a79fc1 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -109,6 +109,7 @@ VOID PhAddDefaultSettings( 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"); From 9c8cfc6512afde090ceda910f87d31c21bef41ad Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 23 Jan 2018 14:03:43 +1100 Subject: [PATCH 710/839] Update default TreeListBorderEnable setting --- ProcessHacker/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index e571b1a79fc1..1c79a5f768d5 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -164,7 +164,7 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"TokenSplitterEnable", L"0"); PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); - PhpAddIntegerSetting(L"TreeListBorderEnable", L"1"); + PhpAddIntegerSetting(L"TreeListBorderEnable", L"0"); PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms PhpAddIntegerSetting(L"WmiProviderEnableHiddenMenu", L"0"); PhpAddStringSetting(L"WmiProviderListViewColumns", L""); From 8d9acaad2896d7d8654dac062aec8e081115819a Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 09:24:22 +1100 Subject: [PATCH 711/839] Update treenew control; Add TreeListCustomColorsEnable setting --- ProcessHacker/mainwnd.c | 21 ++++-- phlib/include/treenew.h | 9 +++ phlib/include/treenewp.h | 7 +- phlib/treenew.c | 149 +++++++++++++++++++++++++++++++-------- 4 files changed, 149 insertions(+), 37 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 0d7ef374a445..b6f77f729c88 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -395,6 +395,8 @@ VOID PhMwpInitializeControls( { ULONG thinRows; ULONG treelistBorder; + ULONG treelistCustomColors; + PH_TREENEW_CREATEPARAMS treelistCreateParams; TabControlHandle = CreateWindow( WC_TABCONTROL, @@ -414,11 +416,18 @@ VOID PhMwpInitializeControls( thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; treelistBorder = PhGetIntegerSetting(L"TreeListBorderEnable") ? WS_BORDER : 0; + treelistCustomColors = PhGetIntegerSetting(L"TreeListCustomColorsEnable") ? TN_STYLE_CUSTOM_COLORS : 0; + + if (treelistCustomColors) + { + treelistCreateParams.FocusColor = PhGetIntegerSetting(L"TreeListCustomColorFocus"); + treelistCreateParams.SelectionColor = PhGetIntegerSetting(L"TreeListCustomColorSelection"); + } 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, + 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, @@ -426,14 +435,14 @@ VOID PhMwpInitializeControls( PhMainWndHandle, NULL, PhInstanceHandle, - NULL + &treelistCreateParams ); BringWindowToTop(PhMwpProcessTreeNewHandle); PhMwpServiceTreeNewHandle = CreateWindow( PH_TREENEW_CLASSNAME, NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder | treelistCustomColors, 0, 0, 3, @@ -441,14 +450,14 @@ VOID PhMwpInitializeControls( PhMainWndHandle, NULL, PhInstanceHandle, - NULL + &treelistCreateParams ); BringWindowToTop(PhMwpServiceTreeNewHandle); PhMwpNetworkTreeNewHandle = CreateWindow( PH_TREENEW_CLASSNAME, NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder | treelistCustomColors, 0, 0, 3, @@ -456,7 +465,7 @@ VOID PhMwpInitializeControls( PhMainWndHandle, NULL, PhInstanceHandle, - NULL + &treelistCreateParams ); BringWindowToTop(PhMwpNetworkTreeNewHandle); diff --git a/phlib/include/treenew.h b/phlib/include/treenew.h index 51e24253eaa3..3eac52a3b170 100644 --- a/phlib/include/treenew.h +++ b/phlib/include/treenew.h @@ -10,6 +10,13 @@ extern "C" { #define PH_TREENEW_SEARCH_TIMEOUT 1000 #define PH_TREENEW_SEARCH_MAXIMUM_LENGTH 1023 +typedef struct _PH_TREENEW_CREATEPARAMS +{ + COLORREF FocusColor; + COLORREF SelectionColor; + // Add new fields here. +} PH_TREENEW_CREATEPARAMS, *PPH_TREENEW_CREATEPARAMS; + typedef struct _PH_TREENEW_COLUMN { union @@ -101,6 +108,8 @@ typedef struct _PH_TREENEW_NODE #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 diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index 997bbf8980ca..a90c1695cdab 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -57,7 +57,9 @@ typedef struct _PH_TREENEW_CONTEXT ULONG DragSelectionActive : 1; ULONG SelectionRectangleAlpha : 1; // use alpha blending for the selection rectangle ULONG CustomRowHeight : 1; - ULONG Spare : 4; + ULONG CustomColors : 1; + ULONG ContextMenuActive : 1; + ULONG Spare : 2; }; ULONG Flags; }; @@ -134,6 +136,9 @@ typedef struct _PH_TREENEW_CONTEXT HTHEME ThemeData; COLORREF DefaultBackColor; COLORREF DefaultForeColor; + HBRUSH CustomFocusBrush; + HBRUSH CustomSelectedBrush; + LONG SystemBorderX; LONG SystemBorderY; LONG SystemEdgeX; diff --git a/phlib/treenew.c b/phlib/treenew.c index 635759affda9..b2bc4aa95545 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -183,7 +183,11 @@ LRESULT CALLBACK PhTnpWndProc( 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; @@ -208,6 +212,20 @@ LRESULT CALLBACK PhTnpWndProc( 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); } @@ -386,6 +404,14 @@ VOID PhTnpDestroyTreeNewContext( if (Context->SuspendUpdateRegion) DeleteObject(Context->SuspendUpdateRegion); + if (Context->CustomColors) + { + if (Context->CustomFocusBrush) + DeleteObject(Context->CustomFocusBrush); + if (Context->CustomSelectedBrush) + DeleteObject(Context->CustomSelectedBrush); + } + PhFree(Context); } @@ -396,11 +422,13 @@ BOOLEAN PhTnpOnCreate( ) { 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; @@ -414,6 +442,26 @@ BOOLEAN PhTnpOnCreate( if (!(Context->Style & TN_STYLE_NO_COLUMN_HEADER)) headerStyle |= WS_VISIBLE; + if (Context->Style & TN_STYLE_CUSTOM_COLORS) + { + Context->CustomColors = TRUE; + + if (createParamaters->FocusColor) + Context->CustomFocusBrush = CreateSolidBrush(createParamaters->FocusColor); + else + Context->CustomFocusBrush = CreateSolidBrush(RGB(0, 0, 0xff)); + + if (createParamaters->SelectionColor) + Context->CustomSelectedBrush = CreateSolidBrush(createParamaters->FocusColor); + else + Context->CustomSelectedBrush = CreateSolidBrush(RGB(0, 0, 0x80)); + } + else + { + Context->CustomFocusBrush = GetSysColorBrush(COLOR_HOTLIGHT); + Context->CustomSelectedBrush = GetSysColorBrush(COLOR_HIGHLIGHT); + } + if (!(Context->FixedHeaderHandle = CreateWindow( WC_HEADER, NULL, @@ -1295,7 +1343,9 @@ VOID PhTnpOnContextMenu( 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( @@ -3085,6 +3135,31 @@ VOID PhTnpAutoSizeColumnHeader( 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)) + { + SelectObject(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; @@ -5006,26 +5081,59 @@ VOID PhTnpPaint( for (i = firstRowToUpdate; i <= lastRowToUpdate; i++) { + INT stateId; + node = Context->FlatList->Items[i]; // Prepare the row for drawing. PhTnpPrepareRowForDraw(Context, hdc, node); - if (node->Selected && !Context->ThemeHasItemBackground) + + if (node->Selected) { - // Non-themed background - if (Context->HasFocus) - { - SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - backBrush = GetSysColorBrush(COLOR_HIGHLIGHT); - } + 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 (Context->CustomColors || !Context->ThemeHasItemBackground) + { + switch (stateId) { - SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); - backBrush = GetSysColorBrush(COLOR_BTNFACE); + case TREIS_SELECTED: + case TREIS_SELECTEDNOTFOCUS: + { + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + backBrush = Context->CustomSelectedBrush; + } + break; + case TREIS_HOT: + case TREIS_HOTSELECTED: + { + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + backBrush = Context->CustomFocusBrush; + } + break; + default: + { + SetTextColor(hdc, node->s.DrawForeColor); + SetDCBrushColor(hdc, node->s.DrawBackColor); + backBrush = GetStockObject(DC_BRUSH); + } + break; } - } + } else { SetTextColor(hdc, node->s.DrawForeColor); @@ -5035,29 +5143,10 @@ VOID PhTnpPaint( FillRect(hdc, &rowRect, backBrush); - if (Context->ThemeHasItemBackground) + 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 = -1; - } - if (stateId != -1) { if (!Context->FixedColumnVisible) From c15ef2018b0fac2814936eeebdc0a06f0ae0febf Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 09:26:47 +1100 Subject: [PATCH 712/839] Add missing settings from previous commit --- ProcessHacker/settings.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 1c79a5f768d5..380c07aa77fe 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -165,6 +165,9 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); PhpAddIntegerSetting(L"TreeListBorderEnable", L"0"); + PhpAddIntegerSetting(L"TreeListCustomColorsEnable", 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""); From 8fadaec3f88e5303188c9d43374b6e09cd5a30d7 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 09:27:41 +1100 Subject: [PATCH 713/839] Update ntrtl.h: Add offset macros --- phnt/include/ntrtl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 92ad843fd878..adbd3f164e50 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -1,6 +1,9 @@ #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( From 24f849fcbccc60ff6b209d62bee26e3732fe9977 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 09:41:49 +1100 Subject: [PATCH 714/839] Remove unused define --- ProcessHacker/include/miniinfop.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/ProcessHacker/include/miniinfop.h b/ProcessHacker/include/miniinfop.h index 64e5141ac26a..a9c36442e4dc 100644 --- a/ProcessHacker/include/miniinfop.h +++ b/ProcessHacker/include/miniinfop.h @@ -3,8 +3,6 @@ // Constants -#define MIP_CONTAINER_CLASSNAME L"ProcessHackerMiniInfo" - #define MIP_TIMER_PIN_FIRST 1 #define MIP_TIMER_PIN_LAST (MIP_TIMER_PIN_FIRST + MaxMiniInfoPinType - 1) From a3bcc28ebefa9539eb5936885c7e36350575dd7e Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 12:57:12 +1100 Subject: [PATCH 715/839] Update default confirmation dialog icon --- phlib/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/util.c b/phlib/util.c index 17f0186cc211..2cf209f74456 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -608,7 +608,7 @@ BOOLEAN PhShowConfirmMessage( config.hInstance = PhInstanceHandle; 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.pszMainIcon = Warning ? TD_WARNING_ICON : TD_INFORMATION_ICON; config.pszMainInstruction = PhaConcatStrings(3, L"Do you want to ", action->Buffer, L"?")->Buffer; if (Message) From 5b7d4cfa275be23e74836be3af5b9ef7de87e062 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 12:58:28 +1100 Subject: [PATCH 716/839] Update native API wrapper functions --- ProcessHacker/thrdprv.c | 40 +----- phlib/include/phnative.h | 8 ++ phlib/include/phnativeinl.h | 246 ++++++++++++++++++++++++++++-------- phlib/native.c | 46 +++++++ 4 files changed, 252 insertions(+), 88 deletions(-) diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 42c41e2f78fd..933999061a41 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -584,46 +584,12 @@ NTSTATUS PhpThreadQueryWorker( if (data->ThreadItem->ThreadHandle && WindowsVersion >= WINDOWS_10_RS1) { - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - ULONG returnLength; - PTHREAD_NAME_INFORMATION threadNameInfo; + PPH_STRING threadName; - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - status = NtQueryInformationThread( - data->ThreadItem->ThreadHandle, - ThreadNameInformation, - buffer, - bufferSize, - &returnLength - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - bufferSize = returnLength; - buffer = PhAllocate(bufferSize); - - status = NtQueryInformationThread( - data->ThreadItem->ThreadHandle, - ThreadNameInformation, - buffer, - bufferSize, - &returnLength - ); - } - - if (NT_SUCCESS(status)) + if (NT_SUCCESS(PhGetThreadName(data->ThreadItem->ThreadHandle, &threadName))) { - threadNameInfo = (PTHREAD_NAME_INFORMATION)buffer; - - data->ThreadName = PhCreateStringFromUnicodeString(&threadNameInfo->ThreadName); + data->ThreadName = threadName; } - - PhFree(buffer); } Done: diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 8dcb3aa2b9a9..75652c55fc2d 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -1087,6 +1087,14 @@ PhImpersonateClientOfNamedPipe( _In_ HANDLE PipeHandle ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetThreadName( + _In_ HANDLE ThreadHandle, + _Out_ PPH_STRING *ThreadName + ); + #ifdef __cplusplus } #endif diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index 572dfda47a1c..dc746e769f92 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -261,6 +261,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 +315,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 +369,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. * @@ -394,30 +465,33 @@ PhGetProcessProtection( FORCEINLINE NTSTATUS -PhGetProcessIsCFGuardEnabled( +PhGetProcessQuotaLimits( _In_ HANDLE ProcessHandle, - _Out_ PBOOLEAN IsControlFlowGuardEnabled + _Out_ PQUOTA_LIMITS QuotaLimits ) { - NTSTATUS status; - PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; - - policyInfo.Policy = ProcessControlFlowGuardPolicy; - - status = NtQueryInformationProcess( + return NtQueryInformationProcess( ProcessHandle, - ProcessMitigationPolicy, - &policyInfo, - sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), + ProcessQuotaLimits, + QuotaLimits, + sizeof(QUOTA_LIMITS), NULL ); +} - if (!NT_SUCCESS(status)) - return status; - - *IsControlFlowGuardEnabled = !!policyInfo.ControlFlowGuardPolicy.EnableControlFlowGuard; - - return status; +FORCEINLINE +NTSTATUS +PhSetProcessQuotaLimits( + _In_ HANDLE ProcessHandle, + _In_ QUOTA_LIMITS QuotaLimits + ) +{ + return NtSetInformationProcess( + ProcessHandle, + ProcessQuotaLimits, + &QuotaLimits, + sizeof(QUOTA_LIMITS) + ); } /** @@ -441,25 +515,32 @@ PhSetProcessAffinityMask( ); } -/** - * 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( +PhGetProcessIsCFGuardEnabled( _In_ HANDLE ProcessHandle, - _In_ IO_PRIORITY_HINT IoPriority + _Out_ PBOOLEAN IsControlFlowGuardEnabled ) { - return NtSetInformationProcess( + NTSTATUS status; + PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; + + policyInfo.Policy = ProcessControlFlowGuardPolicy; + + status = NtQueryInformationProcess( ProcessHandle, - ProcessIoPriority, - &IoPriority, - sizeof(IO_PRIORITY_HINT) + ProcessMitigationPolicy, + &policyInfo, + sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), + NULL ); + + if (!NT_SUCCESS(status)) + return status; + + *IsControlFlowGuardEnabled = !!policyInfo.ControlFlowGuardPolicy.EnableControlFlowGuard; + + return status; } /** @@ -485,6 +566,49 @@ PhGetThreadBasicInformation( ); } +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 +PhSetThreadBasePriority( + _In_ HANDLE ThreadHandle, + _In_ LONG Increment + ) +{ + return NtSetInformationThread( + ThreadHandle, + ThreadBasePriority, + &Increment, + sizeof(LONG) + ); +} + /** * Gets a thread's I/O priority. * @@ -508,6 +632,28 @@ PhGetThreadIoPriority( ); } +/** + * 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 +PhSetThreadIoPriority( + _In_ HANDLE ThreadHandle, + _In_ IO_PRIORITY_HINT IoPriority + ) +{ + return NtSetInformationThread( + ThreadHandle, + ThreadIoPriority, + &IoPriority, + sizeof(IO_PRIORITY_HINT) + ); +} + /** * Gets a thread's page priority. * @@ -541,6 +687,26 @@ PhGetThreadPagePriority( 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. * @@ -596,28 +762,6 @@ PhSetThreadAffinityMask( ); } -/** - * 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 -PhSetThreadIoPriority( - _In_ HANDLE ThreadHandle, - _In_ IO_PRIORITY_HINT IoPriority - ) -{ - return NtSetInformationThread( - ThreadHandle, - ThreadIoPriority, - &IoPriority, - sizeof(IO_PRIORITY_HINT) - ); -} - FORCEINLINE NTSTATUS PhGetJobBasicAndIoAccounting( diff --git a/phlib/native.c b/phlib/native.c index dadad9f0eaaf..ac6fa54a6025 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -6933,3 +6933,49 @@ NTSTATUS PhImpersonateClientOfNamedPipe( 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; +} From 491e9b1b14581ea935cfb5d14a1e7fc3621bf219 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 14:05:41 +1100 Subject: [PATCH 717/839] Improve TreeListCustomColorsEnable setting --- ProcessHacker/mainwnd.c | 1 + ProcessHacker/settings.c | 1 + phlib/include/treenew.h | 1 + phlib/include/treenewp.h | 7 +++++-- phlib/treenew.c | 35 +++++++++++------------------------ 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index b6f77f729c88..b574cb535fdf 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -420,6 +420,7 @@ VOID PhMwpInitializeControls( if (treelistCustomColors) { + treelistCreateParams.TextColor = PhGetIntegerSetting(L"TreeListCustomColorText"); treelistCreateParams.FocusColor = PhGetIntegerSetting(L"TreeListCustomColorFocus"); treelistCreateParams.SelectionColor = PhGetIntegerSetting(L"TreeListCustomColorSelection"); } diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 380c07aa77fe..319d88ebd3f1 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -166,6 +166,7 @@ VOID PhAddDefaultSettings( 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 diff --git a/phlib/include/treenew.h b/phlib/include/treenew.h index 3eac52a3b170..61d08081b1c2 100644 --- a/phlib/include/treenew.h +++ b/phlib/include/treenew.h @@ -12,6 +12,7 @@ extern "C" { typedef struct _PH_TREENEW_CREATEPARAMS { + COLORREF TextColor; COLORREF FocusColor; COLORREF SelectionColor; // Add new fields here. diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index a90c1695cdab..21fcfb9b95b1 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -136,8 +136,11 @@ typedef struct _PH_TREENEW_CONTEXT HTHEME ThemeData; COLORREF DefaultBackColor; COLORREF DefaultForeColor; - HBRUSH CustomFocusBrush; - HBRUSH CustomSelectedBrush; + + // User configurable colors. + COLORREF CustomTextColor; + COLORREF CustomFocusColor; + COLORREF CustomSelectedColor; LONG SystemBorderX; LONG SystemBorderY; diff --git a/phlib/treenew.c b/phlib/treenew.c index b2bc4aa95545..b89d582c2618 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -404,14 +404,6 @@ VOID PhTnpDestroyTreeNewContext( if (Context->SuspendUpdateRegion) DeleteObject(Context->SuspendUpdateRegion); - if (Context->CustomColors) - { - if (Context->CustomFocusBrush) - DeleteObject(Context->CustomFocusBrush); - if (Context->CustomSelectedBrush) - DeleteObject(Context->CustomSelectedBrush); - } - PhFree(Context); } @@ -444,22 +436,15 @@ BOOLEAN PhTnpOnCreate( 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; - - if (createParamaters->FocusColor) - Context->CustomFocusBrush = CreateSolidBrush(createParamaters->FocusColor); - else - Context->CustomFocusBrush = CreateSolidBrush(RGB(0, 0, 0xff)); - - if (createParamaters->SelectionColor) - Context->CustomSelectedBrush = CreateSolidBrush(createParamaters->FocusColor); - else - Context->CustomSelectedBrush = CreateSolidBrush(RGB(0, 0, 0x80)); } else { - Context->CustomFocusBrush = GetSysColorBrush(COLOR_HOTLIGHT); - Context->CustomSelectedBrush = GetSysColorBrush(COLOR_HIGHLIGHT); + Context->CustomFocusColor = GetSysColor(COLOR_HOTLIGHT); + Context->CustomSelectedColor = GetSysColor(COLOR_HIGHLIGHT); } if (!(Context->FixedHeaderHandle = CreateWindow( @@ -5114,15 +5099,17 @@ VOID PhTnpPaint( case TREIS_SELECTED: case TREIS_SELECTEDNOTFOCUS: { - SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); - backBrush = Context->CustomSelectedBrush; + SetTextColor(hdc, Context->CustomTextColor); + SetDCBrushColor(hdc, Context->CustomSelectedColor); + backBrush = GetStockObject(DC_BRUSH); } break; case TREIS_HOT: case TREIS_HOTSELECTED: { - SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); - backBrush = Context->CustomFocusBrush; + SetTextColor(hdc, Context->CustomTextColor); + SetDCBrushColor(hdc, Context->CustomFocusColor); + backBrush = GetStockObject(DC_BRUSH); } break; default: From 7f54bac9b7246f8a410d5ebe3807a899da3ed6d1 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 19:28:59 +1100 Subject: [PATCH 718/839] Remove unused code, Update copyright text --- phlib/treenew.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/phlib/treenew.c b/phlib/treenew.c index b89d582c2618..cd85bfff576d 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -3,7 +3,7 @@ * tree new (tree list control) * * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -4984,7 +4984,6 @@ VOID PhTnpPaint( LONG normalUpdateRightIndex; LONG normalTotalX; RECT cellRect; - HBRUSH backBrush; HRGN oldClipRegion; PhTnpInitializeThemeData(Context); @@ -5074,7 +5073,6 @@ VOID PhTnpPaint( PhTnpPrepareRowForDraw(Context, hdc, node); - if (node->Selected) { if (i == Context->HotNodeIndex) @@ -5101,7 +5099,6 @@ VOID PhTnpPaint( { SetTextColor(hdc, Context->CustomTextColor); SetDCBrushColor(hdc, Context->CustomSelectedColor); - backBrush = GetStockObject(DC_BRUSH); } break; case TREIS_HOT: @@ -5109,14 +5106,12 @@ VOID PhTnpPaint( { SetTextColor(hdc, Context->CustomTextColor); SetDCBrushColor(hdc, Context->CustomFocusColor); - backBrush = GetStockObject(DC_BRUSH); } break; default: { SetTextColor(hdc, node->s.DrawForeColor); SetDCBrushColor(hdc, node->s.DrawBackColor); - backBrush = GetStockObject(DC_BRUSH); } break; } @@ -5125,10 +5120,9 @@ VOID PhTnpPaint( { SetTextColor(hdc, node->s.DrawForeColor); SetDCBrushColor(hdc, node->s.DrawBackColor); - backBrush = GetStockObject(DC_BRUSH); } - FillRect(hdc, &rowRect, backBrush); + FillRect(hdc, &rowRect, GetStockObject(DC_BRUSH)); if (!Context->CustomColors && Context->ThemeHasItemBackground) { From bf863e4d6bbb6bc03742f3aa628ff3e05ad4f14f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 20:38:14 +1100 Subject: [PATCH 719/839] Update native API wrapper functions --- phlib/include/phnativeinl.h | 97 ++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index dc746e769f92..c3bc53598e63 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -543,6 +543,22 @@ PhGetProcessIsCFGuardEnabled( return status; } +FORCEINLINE +NTSTATUS +PhGetProcessHandleCount( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_HANDLE_INFORMATION HandleInfo + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessHandleCount, + HandleInfo, + sizeof(PROCESS_HANDLE_INFORMATION), + NULL + ); +} + /** * Gets basic information for a thread. * @@ -594,6 +610,22 @@ PhGetThreadBasePriority( // ); } +FORCEINLINE +NTSTATUS +PhGetThreadStartAddress( + _In_ HANDLE ThreadHandle, + _Out_ PVOID *StartAddress + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadQuerySetWin32StartAddress, + StartAddress, + sizeof(PVOID), + NULL + ); +} + FORCEINLINE NTSTATUS PhSetThreadBasePriority( @@ -706,7 +738,6 @@ PhSetThreadPagePriority( ); } - /** * Gets a thread's cycle count. * @@ -740,6 +771,70 @@ PhGetThreadCycleTime( 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 +PhGetThreadLastSystemCall( + _In_ HANDLE ThreadHandle, + _Out_ PTHREAD_LAST_SYSCALL_INFORMATION LastSystemCall + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadLastSystemCall, + LastSystemCall, + RTL_SIZEOF_THROUGH_FIELD(THREAD_LAST_SYSCALL_INFORMATION, Pad), // HACK: Win7 requires exact size. + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhGetThreadWow64Context( + _In_ HANDLE ThreadHandle, + _Out_ PWOW64_CONTEXT Context + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadWow64Context, + Context, + sizeof(WOW64_CONTEXT), + NULL + ); +} + /** * Sets a thread's affinity mask. * From 3ffb71ed1949ac56faf1be87fc53e5d41ef98903 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:17:21 +1100 Subject: [PATCH 720/839] Update native API wrapper functions --- ProcessHacker/actions.c | 30 ++----- ProcessHacker/anawait.c | 16 +--- ProcessHacker/cmdmode.c | 13 ++- ProcessHacker/hidnproc.c | 20 +---- ProcessHacker/main.c | 163 ++++++++++++++++++++++++++--------- ProcessHacker/mwpgproc.c | 8 +- ProcessHacker/phsvc/svcapi.c | 3 +- ProcessHacker/procprv.c | 8 +- ProcessHacker/proctree.c | 7 +- ProcessHacker/prpgstat.c | 8 +- ProcessHacker/prpgthrd.c | 11 +-- ProcessHacker/thrdprv.c | 8 +- phlib/include/phutil.h | 1 + phlib/symprv.c | 21 +---- phlib/util.c | 21 ++--- phlib/workqueue.c | 10 +-- phnt/include/ntpsapi.h | 8 +- 17 files changed, 177 insertions(+), 179 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 2e905a3cf5a3..4551bb8a50d9 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1528,12 +1528,7 @@ BOOLEAN PhUiReduceWorkingSetProcesses( quotaLimits.MinimumWorkingSetSize = -1; quotaLimits.MaximumWorkingSetSize = -1; - status = NtSetInformationProcess( - processHandle, - ProcessQuotaLimits, - "aLimits, - sizeof(QUOTA_LIMITS) - ); + status = PhSetProcessQuotaLimits(processHandle, quotaLimits); NtClose(processHandle); } @@ -1754,12 +1749,7 @@ BOOLEAN PhUiSetPagePriorityProcess( { if (Process->ProcessId != SYSTEM_PROCESS_ID) { - status = NtSetInformationProcess( - processHandle, - ProcessPagePriority, - &PagePriority, - sizeof(ULONG) - ); + status = PhSetProcessPagePriority(processHandle, PagePriority); } else { @@ -1794,7 +1784,6 @@ BOOLEAN PhUiSetPriorityProcesses( { NTSTATUS status; HANDLE processHandle; - PROCESS_PRIORITY_CLASS priorityClass; if (NT_SUCCESS(status = PhOpenProcess( &processHandle, @@ -1804,9 +1793,12 @@ BOOLEAN PhUiSetPriorityProcesses( { if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID) { + PROCESS_PRIORITY_CLASS priorityClass; + priorityClass.Foreground = FALSE; priorityClass.PriorityClass = (UCHAR)PriorityClass; - status = NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); + + status = PhSetProcessPriority(processHandle, priorityClass); } else { @@ -2519,7 +2511,7 @@ BOOLEAN PhUiSetPriorityThread( Thread->ThreadId ))) { - status = NtSetInformationThread(threadHandle, ThreadBasePriority, &Increment, sizeof(LONG)); + status = PhSetThreadBasePriority(threadHandle, Increment); NtClose(threadHandle); } @@ -2600,12 +2592,8 @@ BOOLEAN PhUiSetPagePriorityThread( Thread->ThreadId ))) { - status = NtSetInformationThread( - threadHandle, - ThreadPagePriority, - &PagePriority, - sizeof(ULONG) - ); + status = PhSetThreadPagePriority(threadHandle, PagePriority); + NtClose(threadHandle); } diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index 6e8d9f0f957b..93bb2d675458 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -213,13 +213,7 @@ VOID PhpAnalyzeWaitPassive( return; } - if (!NT_SUCCESS(status = NtQueryInformationThread( - threadHandle, - ThreadLastSystemCall, - &lastSystemCall, - sizeof(THREAD_LAST_SYSCALL_INFORMATION), - NULL - ))) + if (!NT_SUCCESS(status = PhGetThreadLastSystemCall(threadHandle, &lastSystemCall))) { NtClose(threadHandle); PhShowStatus(hWnd, L"Unable to determine whether the thread is waiting.", status, 0); @@ -715,13 +709,7 @@ static VOID PhpGetThreadLastSystemCallNumber( { THREAD_LAST_SYSCALL_INFORMATION lastSystemCall; - if (NT_SUCCESS(NtQueryInformationThread( - ThreadHandle, - ThreadLastSystemCall, - &lastSystemCall, - sizeof(THREAD_LAST_SYSCALL_INFORMATION), - NULL - ))) + if (NT_SUCCESS(PhGetThreadLastSystemCall(ThreadHandle, &lastSystemCall))) { *LastSystemCallNumber = lastSystemCall.SystemCallNumber; } diff --git a/ProcessHacker/cmdmode.c b/ProcessHacker/cmdmode.c index 9ea771073c32..1d7cb4f3761e 100644 --- a/ProcessHacker/cmdmode.c +++ b/ProcessHacker/cmdmode.c @@ -174,9 +174,12 @@ NTSTATUS PhCommandModeStart( 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)); + + status = PhSetProcessPriority(processHandle, priorityClass); + NtClose(processHandle); } } @@ -217,12 +220,8 @@ NTSTATUS PhCommandModeStart( if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) { - status = NtSetInformationProcess( - processHandle, - ProcessPagePriority, - &pagePriority, - sizeof(ULONG) - ); + status = PhSetProcessPagePriority(processHandle, pagePriority); + NtClose(processHandle); } } diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index 7744af80173f..e2398510b385 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -551,7 +551,7 @@ static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( PROCESS_BASIC_INFORMATION basicInfo; KERNEL_USER_TIMES times; PROCESS_PRIORITY_CLASS priorityClass; - ULONG handleCount; + PROCESS_HANDLE_INFORMATION handleInfo; HANDLE processHandle2; if (Entry->Type == NormalProcess) @@ -636,26 +636,14 @@ static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( // TODO: Token information? - if (NT_SUCCESS(NtQueryInformationProcess( - processHandle, - ProcessPriorityClass, - &priorityClass, - sizeof(PROCESS_PRIORITY_CLASS), - NULL - ))) + if (NT_SUCCESS(PhGetProcessPriority(processHandle, &priorityClass))) { processItem->PriorityClass = priorityClass.PriorityClass; } - if (NT_SUCCESS(NtQueryInformationProcess( - processHandle, - ProcessHandleCount, - &handleCount, - sizeof(ULONG), - NULL - ))) + if (NT_SUCCESS(PhGetProcessHandleCount(processHandle, &handleInfo))) { - processItem->NumberOfHandles = handleCount; + processItem->NumberOfHandles = handleInfo.HandleCount; } } diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 5386cd7ae90c..0acc622d12f4 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -75,6 +75,14 @@ VOID PhpEnablePrivileges( VOID ); +BOOLEAN PhInitializeMitigationPolicy( + VOID + ); + +VOID PhInitializeRestartPolicy( + VOID + ); + PPH_STRING PhApplicationDirectory = NULL; PPH_STRING PhApplicationFileName = NULL; PHAPPAPI HFONT PhApplicationFont = NULL; @@ -113,6 +121,8 @@ INT WINAPI wWinMain( if (!NT_SUCCESS(PhInitializePhLibEx(ULONG_MAX, Instance, 0, 0))) return 1; + if (PhInitializeMitigationPolicy()) + return 0; if (!PhInitializeAppSystem()) return 1; @@ -234,6 +244,7 @@ INT WINAPI wWinMain( PhLoadPlugins(); } +#ifndef DEBUG if (WindowsVersion >= WINDOWS_10) { PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; @@ -246,6 +257,7 @@ INT WINAPI wWinMain( NtSetInformationProcess(NtCurrentProcess(), ProcessMitigationPolicy, &policyInfo, sizeof(PROCESS_MITIGATION_POLICY_INFORMATION)); } +#endif if (PhStartupParameters.PhSvc) { @@ -285,7 +297,7 @@ INT WINAPI wWinMain( PhDereferenceObject(objectName); } - // Set priority. + // Set the default priority. { PROCESS_PRIORITY_CLASS priorityClass; @@ -295,9 +307,12 @@ INT WINAPI wWinMain( if (PhStartupParameters.PriorityClass != 0) priorityClass.PriorityClass = (UCHAR)PhStartupParameters.PriorityClass; - NtSetInformationProcess(NtCurrentProcess(), ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); + PhSetProcessPriority(NtCurrentProcess(), priorityClass); } + // Create the restart policy. + PhInitializeRestartPolicy(); + if (!PhMainWndInitialization(CmdShow)) { PhShowError(NULL, L"Unable to initialize the main window."); @@ -517,19 +532,13 @@ VOID PhInitializeFont( } } -VOID PhInitializeMitigationPolicy( +BOOLEAN PhInitializeMitigationPolicy( VOID ) { - static PH_STRINGREF policyKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); - static UNICODE_STRING policyKeyValue = RTL_CONSTANT_STRING(L"MitigationOptions"); - BOOLEAN policyKeyValid = FALSE; - HANDLE keyReadHandle; - HANDLE keyWriteHandle; - - if (WindowsVersion < WINDOWS_10 || !PhGetOwnTokenAttributes().Elevated) - return; - +#ifdef DEBUG + return FALSE; +#else #define DEFAULT_MITIGATION_POLICY_FLAGS \ (PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | \ PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | \ @@ -540,43 +549,113 @@ VOID PhInitializeMitigationPolicy( PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | \ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON) - if (NT_SUCCESS(PhOpenKey( - &keyReadHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &policyKeyName, - 0 - ))) - { - if (PhQueryRegistryUlong64(keyReadHandle, L"MitigationOptions") == DEFAULT_MITIGATION_POLICY_FLAGS) - policyKeyValid = TRUE; + BOOLEAN success = FALSE; + PS_SYSTEM_DLL_INIT_BLOCK (*LdrSystemDllInitBlock_I); + HANDLE jobObjectHandle; + SIZE_T attributeListLength = 0; + PROCESS_INFORMATION processInfo = { 0 }; + STARTUPINFOEX startupInfo = { sizeof(STARTUPINFOEX) }; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = { 0 }; - NtClose(keyReadHandle); - } + if (WindowsVersion < WINDOWS_10_RS2) + return FALSE; - if (policyKeyValid) - return; + LdrSystemDllInitBlock_I = PhGetModuleProcAddress(L"ntdll.dll", "LdrSystemDllInitBlock"); + + if (!LdrSystemDllInitBlock_I) + return FALSE; + + if ((LdrSystemDllInitBlock_I->MitigationOptionsMap.Map[0] & DEFAULT_MITIGATION_POLICY_FLAGS) == DEFAULT_MITIGATION_POLICY_FLAGS) + return FALSE; + + if (!InitializeProcThreadAttributeList(NULL, 2, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return FALSE; + + startupInfo.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 2, 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; - if (NT_SUCCESS(PhCreateKey( - &keyWriteHandle, - KEY_WRITE | DELETE, - PH_KEY_LOCAL_MACHINE, - &policyKeyName, - OBJ_OPENIF, - 0, + if (!NT_SUCCESS(NtCreateJobObject(&jobObjectHandle, JOB_OBJECT_ALL_ACCESS, NULL))) + goto CleanupExit; + + extendedInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_ACTIVE_PROCESS; + extendedInfo.BasicLimitInformation.ActiveProcessLimit = 1; + + if (!NT_SUCCESS(NtSetInformationJobObject( + jobObjectHandle, + JobObjectExtendedLimitInformation, + &extendedInfo, + sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION) + ))) + goto CleanupExit; + + if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &(HANDLE){ jobObjectHandle }, sizeof(HANDLE), NULL, NULL)) + goto CleanupExit; + + if (NT_SUCCESS(PhCreateProcessWin32Ex( + NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, + NtCurrentPeb()->ProcessParameters->CommandLine.Buffer, + NULL, + NULL, + &startupInfo.StartupInfo, + PH_CREATE_PROCESS_EXTENDED_STARTUPINFO | PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB, + NULL, + NULL, + NULL, NULL ))) { - NtSetValueKey( - keyWriteHandle, - &policyKeyValue, - 0, - REG_QWORD, - &(ULONG64) { DEFAULT_MITIGATION_POLICY_FLAGS }, - sizeof(ULONG64) - ); - NtClose(keyWriteHandle); + success = TRUE; } + +CleanupExit: + + if (processInfo.hProcess) + NtClose(processInfo.hProcess); + + if (processInfo.hThread) + NtClose(processInfo.hThread); + + if (jobObjectHandle) + NtClose(jobObjectHandle); + + if (startupInfo.lpAttributeList) + { + DeleteProcThreadAttributeList(startupInfo.lpAttributeList); + PhFree(startupInfo.lpAttributeList); + } + + return success; +#endif +} + +VOID PhInitializeRestartPolicy( + VOID + ) +{ + PH_STRINGREF commandLine; + PH_STRINGREF fileName; + PH_STRINGREF arguments; + PPH_STRING argumentsString = NULL; + + // dmex: Restart process after a crash, hang, patch installation or + // after Windows 10 auto-restarts the machine due to an update. + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + PhParseCommandLineFuzzy(&commandLine, &fileName, &arguments, NULL); + + if (arguments.Length > 0) + argumentsString = PhCreateString2(&arguments); + + // Note: Do not include the file name in the command line. + RegisterApplicationRestart(PhGetString(argumentsString), 0); + + if (argumentsString) + PhDereferenceObject(argumentsString); } NTSTATUS PhpReadSignature( diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index 0a5edbf21902..c437a25c22e4 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -399,13 +399,7 @@ VOID PhMwpSetProcessMenuPriorityChecks( { if (SetPriority) { - NtQueryInformationProcess( - processHandle, - ProcessPriorityClass, - &priorityClass, - sizeof(PROCESS_PRIORITY_CLASS), - NULL - ); + PhGetProcessPriority(processHandle, &priorityClass); } if (SetIoPriority) diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index 9aba65bd33e3..9f8ded087e2e 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -521,7 +521,8 @@ NTSTATUS PhSvcApiControlProcess( priorityClass.Foreground = FALSE; priorityClass.PriorityClass = (UCHAR)Payload->u.ControlProcess.i.Argument; - status = NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); + + status = PhSetProcessPriority(processHandle, priorityClass); NtClose(processHandle); } diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index a7973d3403d1..29b54475c6dd 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1499,13 +1499,7 @@ FORCEINLINE VOID PhpUpdateDynamicInfoProcessItem( { PROCESS_PRIORITY_CLASS priorityClass; - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessPriorityClass, - &priorityClass, - sizeof(PROCESS_PRIORITY_CLASS), - NULL - ))) + if (NT_SUCCESS(PhGetProcessPriority(ProcessItem->QueryHandle, &priorityClass))) { ProcessItem->PriorityClass = priorityClass.PriorityClass; } diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 641dd4c0836e..6bb97961da4b 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1040,12 +1040,9 @@ static VOID PhpUpdateProcessNodeQuotaLimits( { QUOTA_LIMITS quotaLimits; - if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(NtQueryInformationProcess( + if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(PhGetProcessQuotaLimits( ProcessNode->ProcessItem->QueryHandle, - ProcessQuotaLimits, - "aLimits, - sizeof(QUOTA_LIMITS), - NULL + "aLimits ))) { ProcessNode->MinimumWorkingSetSize = quotaLimits.MinimumWorkingSetSize; diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 3f0f2f862649..18eecfc8b870 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -105,13 +105,7 @@ VOID PhpUpdateProcessStatistics( ULONG64 cycleTime; PROCESS_HANDLE_INFORMATION handleInfo; - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessHandleCount, - &handleInfo, - sizeof(PROCESS_HANDLE_INFORMATION), - NULL - ))) + if (NT_SUCCESS(PhGetProcessHandleCount(ProcessItem->QueryHandle, &handleInfo))) { peakHandles = PhaFormatUInt64(handleInfo.HandleCountHighWatermark, TRUE); } diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 68565ddee871..93bfd1381110 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -148,14 +148,9 @@ VOID PhpInitializeThreadMenu( Threads[0]->ThreadId ))) { - THREAD_BASIC_INFORMATION basicInfo; HANDLE tokenHandle; - if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo))) - { - threadPriority = basicInfo.BasePriority; - } - + PhGetThreadBasePriority(threadHandle, &threadPriority); PhGetThreadIoPriority(threadHandle, &ioPriority); PhGetThreadPagePriority(threadHandle, &pagePriority); @@ -376,7 +371,7 @@ VOID PhpUpdateThreadDetails( pagePriority = PhPagePriorityNames[pagePriorityInteger]; } - if (NT_SUCCESS(NtQueryInformationThread(threadHandle, ThreadIdealProcessorEx, &idealProcessorNumber, sizeof(PROCESSOR_NUMBER), NULL))) + if (NT_SUCCESS(PhGetThreadIdealProcessor(threadHandle, &idealProcessorNumber))) { PH_FORMAT format[3]; @@ -386,7 +381,7 @@ VOID PhpUpdateThreadDetails( PhFormatToBuffer(format, 3, idealProcessor, sizeof(idealProcessor), NULL); } - if (threadItem->WaitReason == Suspended && NT_SUCCESS(NtQueryInformationThread(threadHandle, ThreadSuspendCount, &suspendCount, sizeof(ULONG), NULL))) + if (threadItem->WaitReason == Suspended && NT_SUCCESS(PhGetThreadSuspendCount(threadHandle, &suspendCount))) { PH_FORMAT format[4]; diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 933999061a41..4e34dd964f5b 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -912,13 +912,7 @@ VOID PhpThreadProviderUpdate( if (threadItem->ThreadHandle) { - NtQueryInformationThread( - threadItem->ThreadHandle, - ThreadQuerySetWin32StartAddress, - &startAddress, - sizeof(PVOID), - NULL - ); + PhGetThreadStartAddress(threadItem->ThreadHandle, &startAddress); } if (!startAddress) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 38027644c8e1..a135ac89f21b 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -660,6 +660,7 @@ 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_EXTENDED_STARTUPINFO 0x20 PHLIBAPI NTSTATUS diff --git a/phlib/symprv.c b/phlib/symprv.c index ac3a4da3045e..04bb84099eec 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -1579,8 +1579,7 @@ NTSTATUS PhWalkThreadStack( { PVOID startAddress; - if (NT_SUCCESS(NtQueryInformationThread(ThreadHandle, ThreadQuerySetWin32StartAddress, - &startAddress, sizeof(PVOID), NULL))) + if (NT_SUCCESS(PhGetThreadStartAddress(ThreadHandle, &startAddress))) { if ((ULONG_PTR)startAddress > PhSystemBasicInformation.MaximumUserModeAddress) isSystemThread = TRUE; @@ -1637,10 +1636,7 @@ NTSTATUS PhWalkThreadStack( context.ContextFlags = CONTEXT_ALL; - if (!NT_SUCCESS(status = NtGetContextThread( - ThreadHandle, - &context - ))) + if (!NT_SUCCESS(status = NtGetContextThread(ThreadHandle, &context))) goto SkipAmd64Stack; memset(&stackFrame, 0, sizeof(STACKFRAME64)); @@ -1693,23 +1689,14 @@ NTSTATUS PhWalkThreadStack( context.ContextFlags = CONTEXT_ALL; - if (!NT_SUCCESS(status = NtGetContextThread( - ThreadHandle, - &context - ))) + 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 - ))) + if (!NT_SUCCESS(status = PhGetThreadWow64Context(ThreadHandle, &context))) goto SkipI386Stack; #endif diff --git a/phlib/util.c b/phlib/util.c index 2cf209f74456..42dd323c6288 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2512,7 +2512,8 @@ 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_EXTENDED_STARTUPINFO, EXTENDED_STARTUPINFO_PRESENT } }; FORCEINLINE VOID PhpConvertProcessInformation( @@ -2607,19 +2608,16 @@ NTSTATUS PhCreateProcessWin32Ex( newFlags = 0; PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING)); - if (StartupInfo) - { - startupInfo = *StartupInfo; - } - else + if (!StartupInfo) { memset(&startupInfo, 0, sizeof(STARTUPINFO)); startupInfo.cb = sizeof(STARTUPINFO); } - if (!TokenHandle) + if (TokenHandle) { - if (CreateProcess( + if (CreateProcessAsUser( + TokenHandle, PhGetString(fileName), PhGetString(commandLine), NULL, @@ -2628,7 +2626,7 @@ NTSTATUS PhCreateProcessWin32Ex( newFlags, Environment, CurrentDirectory, - &startupInfo, + StartupInfo ? StartupInfo : &startupInfo, &processInfo )) status = STATUS_SUCCESS; @@ -2637,8 +2635,7 @@ NTSTATUS PhCreateProcessWin32Ex( } else { - if (CreateProcessAsUser( - TokenHandle, + if (CreateProcess( PhGetString(fileName), PhGetString(commandLine), NULL, @@ -2647,7 +2644,7 @@ NTSTATUS PhCreateProcessWin32Ex( newFlags, Environment, CurrentDirectory, - &startupInfo, + StartupInfo ? StartupInfo : &startupInfo, &processInfo )) status = STATUS_SUCCESS; diff --git a/phlib/workqueue.c b/phlib/workqueue.c index 2ccc3495e626..f0e364a9960d 100644 --- a/phlib/workqueue.c +++ b/phlib/workqueue.c @@ -24,6 +24,7 @@ #include #include +#include #include @@ -260,8 +261,7 @@ VOID PhpUpdateWorkQueueEnvironment( increment = NewEnvironment->BasePriority; - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, - &increment, sizeof(LONG)))) + if (NT_SUCCESS(PhSetThreadBasePriority(NtCurrentThread(), increment))) { CurrentEnvironment->BasePriority = NewEnvironment->BasePriority; } @@ -273,8 +273,7 @@ VOID PhpUpdateWorkQueueEnvironment( ioPriority = NewEnvironment->IoPriority; - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, - &ioPriority, sizeof(IO_PRIORITY_HINT)))) + if (NT_SUCCESS(PhSetThreadIoPriority(NtCurrentThread(), ioPriority))) { CurrentEnvironment->IoPriority = NewEnvironment->IoPriority; } @@ -286,8 +285,7 @@ VOID PhpUpdateWorkQueueEnvironment( pagePriority = NewEnvironment->PagePriority; - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadPagePriority, - &pagePriority, sizeof(ULONG)))) + if (NT_SUCCESS(PhSetThreadPagePriority(NtCurrentThread(), pagePriority))) { CurrentEnvironment->PagePriority = NewEnvironment->PagePriority; } diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 513505172b0b..e134eb368b0c 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -809,8 +809,12 @@ typedef struct _THREAD_LAST_SYSCALL_INFORMATION { PVOID FirstArgument; USHORT SystemCallNumber; - //USHORT Reserved; // since REDSTONE2 - //ULONG64 WaitTime; +#ifdef WIN64 + USHORT Pad[0x3]; // since REDSTONE2 +#else + USHORT Pad[0x1]; // since REDSTONE2 +#endif + ULONG64 WaitTime; } THREAD_LAST_SYSCALL_INFORMATION, *PTHREAD_LAST_SYSCALL_INFORMATION; // private From e65de2e434bff8072d12e1d3d6c1f166b222c5bc Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:17:45 +1100 Subject: [PATCH 721/839] HardwareDevices: Fix tabspace --- plugins/HardwareDevices/diskdetails.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/HardwareDevices/diskdetails.c b/plugins/HardwareDevices/diskdetails.c index a6912b73bb20..1165e41be5ec 100644 --- a/plugins/HardwareDevices/diskdetails.c +++ b/plugins/HardwareDevices/diskdetails.c @@ -169,20 +169,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,7 +191,7 @@ VOID DiskDriveQuerySmart( lvItemIndex, 3, PhaFormatString(L"%lu", attribute->RawValue)->Buffer - ); + ); } PhFree(attribute); From 8904c575121aec15336cd8e5543ddb1d72b6441d Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:29:01 +1100 Subject: [PATCH 722/839] Show the 'Create this task with administrative privileges' checkbox on the RunFileDlg --- ProcessHacker/mainwnd.c | 100 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index b574cb535fdf..0d0167014eb4 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -568,7 +569,7 @@ VOID PhMwpOnCommand( if (RunFileDlg) { SelectedRunAsMode = 0; - RunFileDlg(PhMainWndHandle, NULL, NULL, NULL, NULL, 0); + RunFileDlg(PhMainWndHandle, NULL, NULL, NULL, NULL, RFF_OPTRUNAS); } } break; @@ -1795,6 +1796,103 @@ BOOLEAN PhMwpOnNotify( return TRUE; } } + else if (Header->code == RFN_LIMITEDRUNAS) + { + LPNMRUNFILEDLG runFileDlg = (LPNMRUNFILEDLG)Header; + PVOID WdcLibraryHandle; + ULONG (WINAPI* WdcRunTaskAsInteractiveUser_I)( + _In_ PCWSTR CommandLine, + _In_ PCWSTR CurrentDirectory, + _In_ ULONG Reserved + ); + + // dmex: Task Manager uses RFF_OPTRUNAS and RFN_LIMITEDRUNAS to show the 'Create this task with administrative privileges' checkbox + // on the RunFileDlg when the current process is elevated. Task Manager also uses the WdcRunTaskAsInteractiveUser function to launch processes + // as the interactive user from an elevated token. The WdcRunTaskAsInteractiveUser function + // invokes the "\Microsoft\Windows\Task Manager\Interactive" Task Scheduler task for launching the process but + // doesn't return error information and we need to perform some sanity checks before invoking the task. + // Ideally, we should use our own task but for now just re-use the existing task and do what Task Manager does... + + if (WdcLibraryHandle = LoadLibrary(L"wdc.dll")) + { + if (WdcRunTaskAsInteractiveUser_I = PhGetProcedureAddress(WdcLibraryHandle, "WdcRunTaskAsInteractiveUser", 0)) + { + PH_STRINGREF string; + PPH_STRING commandlineString; + PPH_STRING executeString = NULL; + INT cmdlineArgCount; + PWSTR* cmdlineArgList; + + PhInitializeStringRefLongHint(&string, (PWSTR)runFileDlg->lpszFile); + commandlineString = PhCreateString2(&string); + + // Extract the filename. + if (cmdlineArgList = CommandLineToArgvW(commandlineString->Buffer, &cmdlineArgCount)) + { + PPH_STRING fileName = PhCreateString(cmdlineArgList[0]); + + if (fileName && !RtlDoesFileExists_U(fileName->Buffer)) + { + WCHAR buffer[MAX_PATH]; + + // The user typed a name without a path so attempt to locate the executable. + if (PhSearchFilePath(fileName->Buffer, L".exe", buffer)) + PhMoveReference(&fileName, PhCreateString(buffer)); + 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. + PhMoveReference(&executeString, fileName); + } + + PhDereferenceObject(fileName); + } + + LocalFree(cmdlineArgList); + } + + if (!PhIsNullOrEmptyString(executeString)) + { + if (WdcRunTaskAsInteractiveUser_I(executeString->Buffer, NULL, 0) == 0) + *Result = RF_CANCEL; + else + *Result = RF_RETRY; + } + else + { + *Result = RF_RETRY; + } + + if (executeString) + PhDereferenceObject(executeString); + PhDereferenceObject(commandlineString); + } + + FreeLibrary(WdcLibraryHandle); + } + return TRUE; + } return FALSE; } From ba5be2bc43b6556361ec924edfc3187d06dcbd37 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:30:31 +1100 Subject: [PATCH 723/839] OnlineChecks: Add hybrid-analysis.com upload support --- plugins/OnlineChecks/db.c | 2 + plugins/OnlineChecks/db.h | 2 + plugins/OnlineChecks/main.c | 17 ++- plugins/OnlineChecks/onlnchk.h | 29 ++-- plugins/OnlineChecks/upload.c | 220 ++++++++++++++++++++++++------ plugins/OnlineChecks/virustotal.c | 14 +- 6 files changed, 218 insertions(+), 66 deletions(-) diff --git a/plugins/OnlineChecks/db.c b/plugins/OnlineChecks/db.c index e1082a5bb5ff..4029c9091e60 100644 --- a/plugins/OnlineChecks/db.c +++ b/plugins/OnlineChecks/db.c @@ -25,6 +25,8 @@ 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"6b396f3b3f6e693b3a6e696c386c393d3c3b3b386f383e3e3e3e343c3e6c3d386f6b3f3f6e69396f3c3a34686b38353a"); +PH_STRINGREF NetworkObjectDbHash = PH_STRINGREF_INIT(L"6e61653c676065676b6b7a393d6a66357a396a6e6e66397a35"); BOOLEAN NTAPI ProcessObjectDbEqualFunction( _In_ PVOID Entry1, diff --git a/plugins/OnlineChecks/db.h b/plugins/OnlineChecks/db.h index 8753c31115f4..2232c1d9e6cd 100644 --- a/plugins/OnlineChecks/db.h +++ b/plugins/OnlineChecks/db.h @@ -24,6 +24,8 @@ #define DB_H extern PH_STRINGREF ProcessObjectDbHash; +extern PH_STRINGREF ServiceObjectDbHash; +extern PH_STRINGREF NetworkObjectDbHash; typedef struct _PROCESS_DB_OBJECT { diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 09caed0e07a7..286e5ff6a8dd 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -3,7 +3,7 @@ * Main Program * * Copyright (C) 2010-2013 wj32 - * Copyright (C) 2012-2017 dmex + * Copyright (C) 2012-2018 dmex * * This file is part of Process Hacker. * @@ -214,15 +214,21 @@ VOID NTAPI MenuItemCallback( 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_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_FALCON_UPLOAD: + UploadToOnlineService(menuItem->Context, MENUITEM_FALCON_UPLOAD); + break; + case MENUITEM_FALCON_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_FALCON_UPLOAD_SERVICE); + break; case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: { static PH_FILETYPE_FILTER filters[] = @@ -263,6 +269,7 @@ VOID NTAPI MainMenuInitializingCallback( 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, PhCreateEMenuSeparator(), -1); + PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD, L"Upload file to &hybrid-analysis", 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); @@ -282,6 +289,7 @@ PPH_EMENU_ITEM CreateSendToMenu( ULONG insertIndex; sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD, L"&hybrid-analysis.com", FileName), -1); 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); @@ -360,6 +368,7 @@ VOID NTAPI ServiceMenuInitializingCallback( serviceItem = NULL; sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD_SERVICE, L"&hybrid-analysis.com", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"&virustotal.com", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.&jotti.org", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), -1); diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 1940fc053220..24a3ac6ce508 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -57,8 +58,6 @@ typedef struct _SERVICE_INFO { ULONG Id; PWSTR HostName; - USHORT HostPort; - ULONG HostFlags; PWSTR UploadObjectName; PWSTR FileNameFieldName; } SERVICE_INFO, *PSERVICE_INFO; @@ -186,13 +185,20 @@ VOID VirusTotalShowErrorDialog( ); // upload -#define ENABLE_SERVICE_VIRUSTOTAL 100 -#define MENUITEM_VIRUSTOTAL_QUEUE 101 -#define MENUITEM_VIRUSTOTAL_UPLOAD 102 -#define MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE 103 -#define MENUITEM_VIRUSTOTAL_UPLOAD_FILE 104 -#define MENUITEM_JOTTI_UPLOAD 105 -#define MENUITEM_JOTTI_UPLOAD_SERVICE 106 +#define ENABLE_SERVICE_FALCON 100 +#define MENUITEM_FALCON_QUEUE 101 +#define MENUITEM_FALCON_UPLOAD 102 +#define MENUITEM_FALCON_UPLOAD_SERVICE 103 +#define MENUITEM_FALCON_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, @@ -239,7 +245,7 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResult( ); PPH_BYTES VirusTotalGetCachedDbHash( - VOID + _In_ PPH_STRINGREF CachedHash ); typedef struct _VT_SYSINT_FILE_REPORT_RESULT @@ -262,7 +268,7 @@ typedef struct _VT_SYSINT_FILE_REPORT_RESULT PPH_STRING VirusTotalStringToTime( _In_ PPH_STRING Time -); + ); typedef struct _VIRUSTOTAL_API_RESPONSE { @@ -303,7 +309,6 @@ PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( _In_ PPH_STRING FileHash ); - VOID InitializeVirusTotalProcessMonitor( VOID ); diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 4c6309239c69..3048e570cab7 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Online Checks Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2018 dmex * * This file is part of Process Hacker. * @@ -21,16 +21,17 @@ */ #include "onlnchk.h" -#include PPH_OBJECT_TYPE UploadContextType = NULL; PH_INITONCE UploadContextTypeInitOnce = PH_INITONCE_INIT; SERVICE_INFO UploadServiceInfo[] = -{ - { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"???", L"file" }, - { MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"???", L"file" }, - { MENUITEM_JOTTI_UPLOAD, L"virusscan.jotti.org", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, - { MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", PH_HTTP_DEFAULT_HTTPS_PORT, PH_HTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, +{ + { MENUITEM_FALCON_UPLOAD, L"www.hybrid-analysis.com", L"/api/submit", L"file" }, + { MENUITEM_FALCON_UPLOAD_SERVICE, L"www.hybrid-analysis.com", L"/api/submit", 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( @@ -254,34 +255,24 @@ NTSTATUS HashFileAndResetPosition( PPH_BYTES PerformSubRequest( _In_ PUPLOAD_CONTEXT Context, _In_ PWSTR HostName, - _In_ USHORT HostPort, - _In_ ULONG HostFlags, _In_ PWSTR ObjectName ) { PPH_BYTES result = NULL; PPH_HTTP_CONTEXT httpContext = NULL; - PPH_STRING phVersion = NULL; - PPH_STRING userAgent = NULL; + PPH_STRING phVersion; + PPH_STRING userAgent; - // Create a user agent string. phVersion = PhGetPhVersion(); userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - if (!PhHttpSocketCreate( - &httpContext, - PhGetString(userAgent) - )) + if (!PhHttpSocketCreate(&httpContext, userAgent->Buffer)) { RaiseUploadError(Context, L"Unable to create the http socket.", GetLastError()); goto CleanupExit; } - if (!PhHttpSocketConnect( - httpContext, - HostName, - HostPort - )) + if (!PhHttpSocketConnect(httpContext, HostName, PH_HTTP_DEFAULT_HTTPS_PORT)) { RaiseUploadError(Context, L"Unable to connect to the service.", GetLastError()); goto CleanupExit; @@ -291,7 +282,7 @@ PPH_BYTES PerformSubRequest( httpContext, NULL, ObjectName, - PH_HTTP_FLAG_REFRESH | HostFlags + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE )) { RaiseUploadError(Context, L"Unable to create the request.", GetLastError()); @@ -309,7 +300,7 @@ PPH_BYTES PerformSubRequest( 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()); @@ -327,6 +318,24 @@ PPH_BYTES PerformSubRequest( return result; } +BOOLEAN UploadGetFileArchitecture( + _In_ HANDLE FileHandle, + _Out_ PUSHORT FileArchitecture + ) +{ + NTSTATUS status; + PH_MAPPED_IMAGE mappedImage; + + if (!NT_SUCCESS(status = PhLoadMappedImage(NULL, FileHandle, TRUE, &mappedImage))) + return FALSE; + + *FileArchitecture = mappedImage.NtHeaders->FileHeader.Machine; + + PhUnloadMappedImage(&mappedImage); + + return TRUE; +} + NTSTATUS UploadFileThreadStart( _In_ PVOID Parameter ) @@ -426,22 +435,92 @@ NTSTATUS UploadFileThreadStart( PhInitializeStringBuilder(&httpPostHeader, DOS_MAX_PATH_LENGTH); PhInitializeStringBuilder(&httpPostFooter, DOS_MAX_PATH_LENGTH); - // build request boundary string + // HTTP request boundary string. postBoundary = PhFormatString( - L"------------------------%I64u", + L"--%I64u", (ULONG64)RtlRandomEx(&httpPostSeed) | ((ULONG64)RtlRandomEx(&httpPostSeed) << 31) ); - // build request header string + // HTTP request header string. PhAppendFormatStringBuilder( &httpRequestHeaders, L"Content-Type: multipart/form-data; boundary=%s\r\n", postBoundary->Buffer ); - if (context->Service == MENUITEM_JOTTI_UPLOAD) + if (context->Service == MENUITEM_FALCON_UPLOAD || context->Service == MENUITEM_FALCON_UPLOAD_SERVICE) { - // POST boundary header + USHORT machineType; + USHORT environmentId; + + if (!UploadGetFileArchitecture(fileHandle, &machineType)) + { + RaiseUploadError(context, L"Unable to create the request.", GetLastError()); + goto CleanupExit; + } + + switch (machineType) + { + case IMAGE_FILE_MACHINE_I386: + environmentId = 100; + break; + case IMAGE_FILE_MACHINE_AMD64: + environmentId = 120; + break; + default: + { + RaiseUploadError(context, L"File architecture not supported.", GetLastError()); + goto CleanupExit; + } + } + + { + PPH_BYTES serviceHash; + PPH_BYTES networkHash; + PPH_STRING resourceNameString; + PPH_STRING resourceHashString; + + serviceHash = VirusTotalGetCachedDbHash(&ServiceObjectDbHash); + networkHash = VirusTotalGetCachedDbHash(&NetworkObjectDbHash); + + resourceNameString = PhZeroExtendToUtf16(serviceHash->Buffer); + resourceHashString = PhZeroExtendToUtf16(networkHash->Buffer); + PhHttpSocketSetCredentials(httpContext, PhGetString(resourceHashString), PhGetString(resourceNameString)); + PhClearReference(&resourceHashString); + PhClearReference(&resourceNameString); + PhClearReference(&networkHash); + PhClearReference(&serviceHash); + } + + // POST boundary header. + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\nContent-Disposition: form-data; name=\"environmentId\"\r\n\r\n%hu\r\n", + postBoundary->Buffer, + environmentId + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\nContent-Disposition: form-data; name=\"nosharevt\"\r\n\r\n1\r\n", + postBoundary->Buffer + ); + 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) + { + // POST boundary header. PhAppendFormatStringBuilder( &httpPostHeader, L"\r\n--%s\r\n", @@ -467,7 +546,7 @@ NTSTATUS UploadFileThreadStart( L"Content-Type: application/x-msdownload\r\n\r\n" ); - // POST boundary footer + // POST boundary footer. PhAppendFormatStringBuilder( &httpPostFooter, L"\r\n--%s--\r\n", @@ -667,6 +746,58 @@ NTSTATUS UploadFileThreadStart( { switch (context->Service) { + case MENUITEM_FALCON_UPLOAD: + case MENUITEM_FALCON_UPLOAD_SERVICE: + { + PPH_BYTES jsonString; + PVOID jsonRootObject; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + RaiseUploadError(context, L"Unable to download the response.", GetLastError()); + goto CleanupExit; + } + + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) + { + INT64 errorCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + + if (errorCode == 0) + { + PVOID jsonResponse; + PPH_STRING jsonHashString = NULL; + + if (jsonResponse = PhGetJsonObject(jsonRootObject, "response")) + jsonHashString = PhGetJsonValueAsString(jsonResponse, "sha256"); + + if (jsonHashString) + { + PhMoveReference(&context->LaunchCommand, PhFormatString( + L"https://www.hybrid-analysis.com/sample/%s", + context->FileHash ? PhGetString(context->FileHash) : PhGetString(jsonHashString) + )); + + PhDereferenceObject(jsonHashString); + } + } + 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: { @@ -757,6 +888,9 @@ NTSTATUS UploadFileThreadStart( CleanupExit: + if (httpContext) + PhHttpSocketDestroy(httpContext); + // Reset Taskbar progress state(s) if (context->TaskbarListClass) { @@ -876,8 +1010,6 @@ NTSTATUS UploadCheckThreadStart( if (!(subRequestBuffer = PerformSubRequest( context, serviceInfo->HostName, - serviceInfo->HostPort, - serviceInfo->HostFlags, subObjectName->Buffer ))) { @@ -921,7 +1053,7 @@ NTSTATUS UploadCheckThreadStart( if (context->VtApiUpload) { - PPH_BYTES resource = VirusTotalGetCachedDbHash(); + PPH_BYTES resource = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); PhMoveReference(&context->UploadUrl, PhFormatString( L"%s%s?\x0061\x0070\x0069\x006B\x0065\x0079=%S&resource=%s", @@ -969,20 +1101,21 @@ NTSTATUS UploadCheckThreadStart( case MENUITEM_JOTTI_UPLOAD: case MENUITEM_JOTTI_UPLOAD_SERVICE: { - // Create the default upload URL - context->UploadUrl = PhFormatString(L"https://virusscan.jotti.org%s", serviceInfo->UploadObjectName); + // Create the default upload URL. + context->UploadUrl = PhFormatString(L"https://%s%s", serviceInfo->HostName, serviceInfo->UploadObjectName); - // No file found... Start the upload. - if (!PhIsNullOrEmptyString(context->UploadUrl)) - { - PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); - } - else - { - RaiseUploadError(context, L"Unable to parse the response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); - } + // Start the upload. + PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); } break; + case MENUITEM_FALCON_UPLOAD: + case MENUITEM_FALCON_UPLOAD_SERVICE: + { + // Create the default upload URL. + context->UploadUrl = PhFormatString(L"https://%s%s", serviceInfo->HostName, serviceInfo->UploadObjectName); + + PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); + } } CleanupExit: @@ -1155,6 +1288,7 @@ NTSTATUS ShowUpdateDialogThread( 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; diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 630575214429..4e9eb77f064f 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -180,19 +180,19 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResultFromHash( } PPH_BYTES VirusTotalGetCachedDbHash( - VOID + _In_ PPH_STRINGREF CachedHash ) { ULONG length; PUCHAR buffer; PPH_BYTES string; - length = (ULONG)ProcessObjectDbHash.Length / sizeof(WCHAR) / 2; + length = (ULONG)CachedHash->Length / sizeof(WCHAR) / 2; buffer = PhAllocate(length + 1); memset(buffer, 0, length + 1); - PhHexStringToBuffer(&ProcessObjectDbHash, buffer); + PhHexStringToBuffer(CachedHash, buffer); string = PhCreateBytes(buffer); @@ -310,7 +310,7 @@ PPH_BYTES VirusTotalSendHttpRequest( goto CleanupExit; { - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); urlPathString = PhFormatString( L"%s%s%s%s%S", @@ -395,7 +395,7 @@ PVIRUSTOTAL_FILE_REPORT VirusTotalRequestFileReport( } { - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); urlPathString = PhFormatString( L"%s%s%s%s%s%S%s%s", @@ -529,7 +529,7 @@ PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( } { - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); urlPathString = PhFormatString( L"%s%s%s%s%s%S%s%s", @@ -627,7 +627,7 @@ PVIRUSTOTAL_API_RESPONSE VirusTotalRequestIpAddressReport( } { - PPH_BYTES resourceString = VirusTotalGetCachedDbHash(); + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); urlPathString = PhFormatString( L"%s%s%s%s%s%S%s%s", From 978b25c54aa98002d73606874d084a17324bb6c1 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:32:55 +1100 Subject: [PATCH 724/839] Fix typo --- ProcessHacker/main.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 0acc622d12f4..e9c2bda26895 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -553,7 +553,6 @@ BOOLEAN PhInitializeMitigationPolicy( PS_SYSTEM_DLL_INIT_BLOCK (*LdrSystemDllInitBlock_I); HANDLE jobObjectHandle; SIZE_T attributeListLength = 0; - PROCESS_INFORMATION processInfo = { 0 }; STARTUPINFOEX startupInfo = { sizeof(STARTUPINFOEX) }; JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = { 0 }; @@ -614,12 +613,6 @@ BOOLEAN PhInitializeMitigationPolicy( CleanupExit: - if (processInfo.hProcess) - NtClose(processInfo.hProcess); - - if (processInfo.hThread) - NtClose(processInfo.hThread); - if (jobObjectHandle) NtClose(jobObjectHandle); From 43cc8ba1a13e6686ddf3723c4fca71c98265d694 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 25 Jan 2018 23:36:28 +1100 Subject: [PATCH 725/839] OnlineChecks: Fix hybrid-analysis.com upload support --- plugins/OnlineChecks/main.c | 14 +++++++------- plugins/OnlineChecks/onlnchk.h | 10 +++++----- plugins/OnlineChecks/upload.c | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 286e5ff6a8dd..209c3d6d8d04 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -223,11 +223,11 @@ VOID NTAPI MenuItemCallback( case MENUITEM_JOTTI_UPLOAD_SERVICE: UploadServiceToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD_SERVICE); break; - case MENUITEM_FALCON_UPLOAD: - UploadToOnlineService(menuItem->Context, MENUITEM_FALCON_UPLOAD); + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + UploadToOnlineService(menuItem->Context, MENUITEM_HYBRIDANALYSIS_UPLOAD); break; - case MENUITEM_FALCON_UPLOAD_SERVICE: - UploadServiceToOnlineService(menuItem->Context, MENUITEM_FALCON_UPLOAD_SERVICE); + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE); break; case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: { @@ -269,7 +269,7 @@ VOID NTAPI MainMenuInitializingCallback( 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, PhCreateEMenuSeparator(), -1); - PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD, L"Upload file to &hybrid-analysis", NULL), -1); + PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD, L"Upload file to &hybrid-analysis", 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); @@ -289,7 +289,7 @@ PPH_EMENU_ITEM CreateSendToMenu( ULONG insertIndex; sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD, L"&hybrid-analysis.com", FileName), -1); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD, L"&hybrid-analysis.com", FileName), -1); 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); @@ -368,7 +368,7 @@ VOID NTAPI ServiceMenuInitializingCallback( serviceItem = NULL; sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_FALCON_UPLOAD_SERVICE, L"&hybrid-analysis.com", serviceItem ? serviceItem : NULL), -1); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE, L"&hybrid-analysis.com", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"&virustotal.com", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.&jotti.org", serviceItem ? serviceItem : NULL), -1); PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), -1); diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index 24a3ac6ce508..e07b3245d69c 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -185,11 +185,11 @@ VOID VirusTotalShowErrorDialog( ); // upload -#define ENABLE_SERVICE_FALCON 100 -#define MENUITEM_FALCON_QUEUE 101 -#define MENUITEM_FALCON_UPLOAD 102 -#define MENUITEM_FALCON_UPLOAD_SERVICE 103 -#define MENUITEM_FALCON_UPLOAD_FILE 104 +#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 diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 3048e570cab7..023a7e38b54c 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -26,8 +26,8 @@ PPH_OBJECT_TYPE UploadContextType = NULL; PH_INITONCE UploadContextTypeInitOnce = PH_INITONCE_INIT; SERVICE_INFO UploadServiceInfo[] = { - { MENUITEM_FALCON_UPLOAD, L"www.hybrid-analysis.com", L"/api/submit", L"file" }, - { MENUITEM_FALCON_UPLOAD_SERVICE, L"www.hybrid-analysis.com", L"/api/submit", L"file" }, + { MENUITEM_HYBRIDANALYSIS_UPLOAD, L"www.hybrid-analysis.com", L"/api/submit", L"file" }, + { MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE, L"www.hybrid-analysis.com", L"/api/submit", 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[]" }, @@ -448,7 +448,7 @@ NTSTATUS UploadFileThreadStart( postBoundary->Buffer ); - if (context->Service == MENUITEM_FALCON_UPLOAD || context->Service == MENUITEM_FALCON_UPLOAD_SERVICE) + if (context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD || context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE) { USHORT machineType; USHORT environmentId; @@ -746,8 +746,8 @@ NTSTATUS UploadFileThreadStart( { switch (context->Service) { - case MENUITEM_FALCON_UPLOAD: - case MENUITEM_FALCON_UPLOAD_SERVICE: + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: { PPH_BYTES jsonString; PVOID jsonRootObject; @@ -1108,8 +1108,8 @@ NTSTATUS UploadCheckThreadStart( PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); } break; - case MENUITEM_FALCON_UPLOAD: - case MENUITEM_FALCON_UPLOAD_SERVICE: + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: { // Create the default upload URL. context->UploadUrl = PhFormatString(L"https://%s%s", serviceInfo->HostName, serviceInfo->UploadObjectName); From 5d72b081f38fd3b2cdb4484c394802aab7513886 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 26 Jan 2018 02:33:47 +1100 Subject: [PATCH 726/839] ExtendedTools: Fix network usage text icon --- plugins/ExtendedTools/iconext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ExtendedTools/iconext.c b/plugins/ExtendedTools/iconext.c index 3bdec4ac9749..921e86d94f71 100644 --- a/plugins/ExtendedTools/iconext.c +++ b/plugins/ExtendedTools/iconext.c @@ -161,7 +161,7 @@ VOID EtRegisterNotifyIcons( &data ); - data.UpdateCallback = EtpNetworkIconUpdateCallback; + data.UpdateCallback = EtpNetworkTextIconUpdateCallback; data.MessageCallback = EtpNetworkIconMessageCallback; Pointers->RegisterTrayIcon( PluginInstance, From a50831329f2ffb1c48143b4a9e1b8f189409b6dc Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 26 Jan 2018 02:37:22 +1100 Subject: [PATCH 727/839] Fix notify icons settings --- ProcessHacker/notifico.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index 29b0f66b87c8..b5584a4c3ab2 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -107,9 +107,15 @@ VOID PhNfLoadSettings( { PPH_NF_ICON icon; - if (icon = PhNfFindIcon(&pluginNamePart, (ULONG)idInteger)) + if (pluginNamePart.Length) { - icon->Flags |= PH_NF_ICON_ENABLED; + if (icon = PhNfFindIcon(&pluginNamePart, (ULONG)idInteger)) + icon->Flags |= PH_NF_ICON_ENABLED; + } + else + { + if (icon = PhNfGetIconById((ULONG)idInteger)) + icon->Flags |= PH_NF_ICON_ENABLED; } } } @@ -142,6 +148,9 @@ VOID PhNfSaveSettings( ); } + if (iconListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&iconListBuilder, 1); + settingsString = PhFinalStringBuilderString(&iconListBuilder); PhSetStringSetting2(L"IconSettings", &settingsString->sr); PhDereferenceObject(settingsString); @@ -491,7 +500,7 @@ PPH_NF_ICON PhNfGetIconById( } PPH_NF_ICON PhNfFindIcon( - _In_opt_ PPH_STRINGREF PluginName, + _In_ PPH_STRINGREF PluginName, _In_ ULONG SubId ) { @@ -499,21 +508,14 @@ PPH_NF_ICON PhNfFindIcon( { PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; - if (PluginName) + if (icon->Plugin) { - if ( - icon->SubId == SubId && - (icon->Plugin ? PhEqualStringRef(PluginName, &icon->Plugin->AppContext.AppName, TRUE) : TRUE) - ) + if (icon->SubId == SubId && + PhEqualStringRef(PluginName, &icon->Plugin->Name, TRUE)) { return icon; } } - else - { - if (icon->SubId == SubId) - return icon; - } } return NULL; From 8ac3afb7d3858cf54958076b48c55ac76b6c33c6 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 26 Jan 2018 04:00:00 +1100 Subject: [PATCH 728/839] Fix treelist regression (show_always) --- phlib/treenew.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/phlib/treenew.c b/phlib/treenew.c index cd85bfff576d..492739319042 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -183,8 +183,8 @@ LRESULT CALLBACK PhTnpWndProc( return 0; case WM_KILLFOCUS: { - if (!context->ContextMenuActive && !(context->Style & TN_STYLE_ALWAYS_SHOW_SELECTION)) - PhTnpSelectRange(context, -1, -1, TN_SELECT_RESET, NULL, NULL); + //if (!context->ContextMenuActive && !(context->Style & TN_STYLE_ALWAYS_SHOW_SELECTION)) + // PhTnpSelectRange(context, -1, -1, TN_SELECT_RESET, NULL, NULL); context->HasFocus = FALSE; @@ -212,19 +212,19 @@ LRESULT CALLBACK PhTnpWndProc( 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->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); From 40ed7196aa123b6c4367a9f009fba062686c0ff7 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 26 Jan 2018 06:35:02 +1100 Subject: [PATCH 729/839] Fix version check #229 --- ProcessHacker/main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index e9c2bda26895..d855053ed18e 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -564,6 +564,9 @@ BOOLEAN PhInitializeMitigationPolicy( if (!LdrSystemDllInitBlock_I) return FALSE; + if (!RTL_CONTAINS_FIELD(LdrSystemDllInitBlock_I, LdrSystemDllInitBlock_I->Size, MitigationOptionsMap)) + return FALSE; + if ((LdrSystemDllInitBlock_I->MitigationOptionsMap.Map[0] & DEFAULT_MITIGATION_POLICY_FLAGS) == DEFAULT_MITIGATION_POLICY_FLAGS) return FALSE; @@ -595,8 +598,8 @@ BOOLEAN PhInitializeMitigationPolicy( if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &(HANDLE){ jobObjectHandle }, sizeof(HANDLE), NULL, NULL)) goto CleanupExit; - if (NT_SUCCESS(PhCreateProcessWin32Ex( - NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, + if (NT_SUCCESS(status = PhCreateProcessWin32Ex( + NULL, NtCurrentPeb()->ProcessParameters->CommandLine.Buffer, NULL, NULL, From c0f2c535c640582b3c871cf0a63e3e58f71ae71b Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 26 Jan 2018 06:42:14 +1100 Subject: [PATCH 730/839] Fix build warning --- ProcessHacker/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index d855053ed18e..f2f3f92b1323 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -598,7 +598,7 @@ BOOLEAN PhInitializeMitigationPolicy( if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &(HANDLE){ jobObjectHandle }, sizeof(HANDLE), NULL, NULL)) goto CleanupExit; - if (NT_SUCCESS(status = PhCreateProcessWin32Ex( + if (NT_SUCCESS(PhCreateProcessWin32Ex( NULL, NtCurrentPeb()->ProcessParameters->CommandLine.Buffer, NULL, From d31507dbe27e65b9d85c0c172306cb8267fc2de6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 27 Jan 2018 11:57:27 +1100 Subject: [PATCH 731/839] Fix #229 restart issue --- ProcessHacker/main.c | 117 +++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 65 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index f2f3f92b1323..517b191ec328 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -532,6 +532,36 @@ VOID PhInitializeFont( } } +VOID PhInitializeRestartPolicy( + VOID + ) +{ + 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; + + 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); + + PhClearReference(&argumentsString); +} + BOOLEAN PhInitializeMitigationPolicy( VOID ) @@ -549,75 +579,57 @@ BOOLEAN PhInitializeMitigationPolicy( PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | \ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON) + static PH_STRINGREF commandlinePart = PH_STRINGREF_INIT(L" -nomp"); BOOLEAN success = FALSE; - PS_SYSTEM_DLL_INIT_BLOCK (*LdrSystemDllInitBlock_I); - HANDLE jobObjectHandle; - SIZE_T attributeListLength = 0; + PH_STRINGREF commandlineSr; + PPH_STRING commandline = NULL; + PS_SYSTEM_DLL_INIT_BLOCK (*LdrSystemDllInitBlock_I) = NULL; STARTUPINFOEX startupInfo = { sizeof(STARTUPINFOEX) }; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = { 0 }; + SIZE_T attributeListLength; - if (WindowsVersion < WINDOWS_10_RS2) - return FALSE; + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandlineSr); - LdrSystemDllInitBlock_I = PhGetModuleProcAddress(L"ntdll.dll", "LdrSystemDllInitBlock"); + if (PhEndsWithStringRef(&commandlineSr, &commandlinePart, FALSE)) + goto CleanupExit; - if (!LdrSystemDllInitBlock_I) - return FALSE; + if (!(LdrSystemDllInitBlock_I = PhGetModuleProcAddress(L"ntdll.dll", "LdrSystemDllInitBlock"))) + goto CleanupExit; if (!RTL_CONTAINS_FIELD(LdrSystemDllInitBlock_I, LdrSystemDllInitBlock_I->Size, MitigationOptionsMap)) - return FALSE; - - if ((LdrSystemDllInitBlock_I->MitigationOptionsMap.Map[0] & DEFAULT_MITIGATION_POLICY_FLAGS) == DEFAULT_MITIGATION_POLICY_FLAGS) - return FALSE; - - if (!InitializeProcThreadAttributeList(NULL, 2, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) - return FALSE; - - startupInfo.lpAttributeList = PhAllocate(attributeListLength); - - if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 2, 0, &attributeListLength)) goto CleanupExit; - if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &(ULONG64){ DEFAULT_MITIGATION_POLICY_FLAGS }, sizeof(ULONG64), NULL, NULL)) + if ((LdrSystemDllInitBlock_I->MitigationOptionsMap.Map[0] & DEFAULT_MITIGATION_POLICY_FLAGS) == DEFAULT_MITIGATION_POLICY_FLAGS) goto CleanupExit; - if (!NT_SUCCESS(NtCreateJobObject(&jobObjectHandle, JOB_OBJECT_ALL_ACCESS, NULL))) + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto CleanupExit; - extendedInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_ACTIVE_PROCESS; - extendedInfo.BasicLimitInformation.ActiveProcessLimit = 1; + startupInfo.lpAttributeList = PhAllocate(attributeListLength); - if (!NT_SUCCESS(NtSetInformationJobObject( - jobObjectHandle, - JobObjectExtendedLimitInformation, - &extendedInfo, - sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION) - ))) + if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, &attributeListLength)) goto CleanupExit; - if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &(HANDLE){ jobObjectHandle }, sizeof(HANDLE), NULL, NULL)) + if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &(ULONG64){ DEFAULT_MITIGATION_POLICY_FLAGS }, sizeof(ULONG64), NULL, NULL)) goto CleanupExit; - if (NT_SUCCESS(PhCreateProcessWin32Ex( + commandline = PhConcatStringRef2(&commandlineSr, &commandlinePart); + success = NT_SUCCESS(PhCreateProcessWin32Ex( NULL, - NtCurrentPeb()->ProcessParameters->CommandLine.Buffer, + PhGetString(commandline), NULL, NULL, &startupInfo.StartupInfo, PH_CREATE_PROCESS_EXTENDED_STARTUPINFO | PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB, NULL, - NULL, + NULL, NULL, NULL - ))) - { - success = TRUE; - } + )); CleanupExit: - if (jobObjectHandle) - NtClose(jobObjectHandle); + if (commandline) + PhDereferenceObject(commandline); if (startupInfo.lpAttributeList) { @@ -629,31 +641,6 @@ BOOLEAN PhInitializeMitigationPolicy( #endif } -VOID PhInitializeRestartPolicy( - VOID - ) -{ - PH_STRINGREF commandLine; - PH_STRINGREF fileName; - PH_STRINGREF arguments; - PPH_STRING argumentsString = NULL; - - // dmex: Restart process after a crash, hang, patch installation or - // after Windows 10 auto-restarts the machine due to an update. - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - PhParseCommandLineFuzzy(&commandLine, &fileName, &arguments, NULL); - - if (arguments.Length > 0) - argumentsString = PhCreateString2(&arguments); - - // Note: Do not include the file name in the command line. - RegisterApplicationRestart(PhGetString(argumentsString), 0); - - if (argumentsString) - PhDereferenceObject(argumentsString); -} - NTSTATUS PhpReadSignature( _In_ PWSTR FileName, _Out_ PUCHAR *Signature, From 593e93888968e9b5324f49fb16133eb9cfa71a3d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 03:49:54 +1100 Subject: [PATCH 732/839] OnlineChecks: Fix crash #231 --- plugins/OnlineChecks/main.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index 209c3d6d8d04..1aef27e43015 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -228,8 +228,9 @@ VOID NTAPI MenuItemCallback( break; case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: UploadServiceToOnlineService(menuItem->Context, MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE); - break; + break; case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE: { static PH_FILETYPE_FILTER filters[] = { @@ -245,7 +246,15 @@ VOID NTAPI MenuItemCallback( { fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - UploadToOnlineService(fileName, MENUITEM_VIRUSTOTAL_UPLOAD); + 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); @@ -269,7 +278,7 @@ VOID NTAPI MainMenuInitializingCallback( 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, PhCreateEMenuSeparator(), -1); - PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD, L"Upload file to &hybrid-analysis", NULL), -1); + PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE, L"Upload file to &Hybrid-Analysis...", 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); From a2afd6364004e3457818a61dac30ed70d49ae1c9 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Sun, 28 Jan 2018 19:04:50 +0100 Subject: [PATCH 733/839] Get proper information for kernel modules (#226) * PhLoadRemoteMappedImageEx Similar to PhLoadRemoteMappedImage. Caller needs to provide own virtual memory reading function. * Get proper information for kernel modules Fixes "CF Guard", "ASLR" and "Entry point" columns for kernel modules. As well as relocated modules highlighting. --- ProcessHacker/modprv.c | 38 +++++++++++++++++++++++++++++--------- phlib/include/mapimg.h | 17 +++++++++++++++++ phlib/mapimg.c | 16 +++++++++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index 9a7589f415d6..dd0d901009c5 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -486,7 +487,6 @@ VOID PhModuleProviderUpdate( moduleItem->FileName = module->FileName; PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); - PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->EntryPoint); PhInitializeImageVersionInfo(&moduleItem->VersionInfo, moduleItem->FileName->Buffer); @@ -502,9 +502,16 @@ VOID PhModuleProviderUpdate( 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_MAPPED_IMAGE || + moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) { PH_REMOTE_MAPPED_IMAGE remoteMappedImage; + PPH_READ_VIRTUAL_MEMORY readVirtualMemory; + + if (moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) + readVirtualMemory = KphReadVirtualMemoryUnsafe; + else + readVirtualMemory = NtReadVirtualMemory; // Note: // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used @@ -516,30 +523,43 @@ VOID PhModuleProviderUpdate( moduleItem->Flags &= ~LDRP_IMAGE_NOT_AT_BASE; - if (NT_SUCCESS(PhLoadRemoteMappedImage(moduleProvider->ProcessHandle, moduleItem->BaseAddress, &remoteMappedImage))) + if (NT_SUCCESS(PhLoadRemoteMappedImageEx(moduleProvider->ProcessHandle, moduleItem->BaseAddress, readVirtualMemory, &remoteMappedImage))) { + ULONG_PTR imageBase = 0; + DWORD entryPoint = 0; + 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; + PIMAGE_OPTIONAL_HEADER32 optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader; - moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; + imageBase = (ULONG_PTR)optionalHeader->ImageBase; + entryPoint = optionalHeader->AddressOfEntryPoint; + moduleItem->ImageDllCharacteristics = 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; + PIMAGE_OPTIONAL_HEADER64 optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader; - moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; + 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); } } + PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->EntryPoint); + // remove CF Guard flag if CFG mitigation is not enabled for the process if (!moduleProvider->ControlFlowGuardEnabled) moduleItem->ImageDllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_GUARD_CF; diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index 95f9a90faf45..0d41efbb4926 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -156,6 +156,23 @@ PhLoadRemoteMappedImage( _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ); +typedef NTSTATUS (NTAPI *PPH_READ_VIRTUAL_MEMORY)( + _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 ReadVirtualMemory, + _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ); + NTSTATUS NTAPI PhUnloadRemoteMappedImage( diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 8e8da7003868..12e2203372e9 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -501,6 +501,16 @@ NTSTATUS PhLoadRemoteMappedImage( _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 ReadVirtualMemory, + _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ) { NTSTATUS status; IMAGE_DOS_HEADER dosHeader; @@ -510,7 +520,7 @@ NTSTATUS PhLoadRemoteMappedImage( RemoteMappedImage->ViewBase = ViewBase; - status = NtReadVirtualMemory( + status = ReadVirtualMemory( ProcessHandle, ViewBase, &dosHeader, @@ -533,7 +543,7 @@ NTSTATUS PhLoadRemoteMappedImage( if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) return STATUS_INVALID_IMAGE_FORMAT; - status = NtReadVirtualMemory( + status = ReadVirtualMemory( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), &ntHeaders, @@ -569,7 +579,7 @@ NTSTATUS PhLoadRemoteMappedImage( RemoteMappedImage->NtHeaders = PhAllocate(ntHeadersSize); - status = NtReadVirtualMemory( + status = ReadVirtualMemory( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), RemoteMappedImage->NtHeaders, From 9679e5d151f099c837c41836172bcb6a1e4c9a01 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 17:55:54 +1100 Subject: [PATCH 734/839] Add window context functions --- phlib/guisup.c | 101 +++++++++++++++++++++++++++++++++++++++++ phlib/include/guisup.h | 24 ++++++++++ 2 files changed, 125 insertions(+) diff --git a/phlib/guisup.c b/phlib/guisup.c index 1753b9fd85da..0512215a6bb1 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -31,6 +31,22 @@ #define SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) +BOOLEAN NTAPI PhpWindowContextHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpWindowContextHashtableHashFunction( + _In_ PVOID Entry + ); + +typedef struct _PH_WINDOW_PROPERTY_CONTEXT +{ + ULONG PropertyHash; + HWND WindowHandle; + PVOID Context; +} PH_WINDOW_PROPERTY_CONTEXT, *PPH_WINDOW_PROPERTY_CONTEXT; + _IsImmersiveProcess IsImmersiveProcess_I; _RunFileDlg RunFileDlg; _SHAutoComplete SHAutoComplete_I; @@ -39,6 +55,9 @@ static PH_INITONCE SharedIconCacheInitOnce = PH_INITONCE_INIT; static PPH_HASHTABLE SharedIconCacheHashtable; static PH_QUEUED_LOCK SharedIconCacheLock = PH_QUEUED_LOCK_INIT; +static PPH_HASHTABLE WindowContextHashTable = NULL; +static PH_QUEUED_LOCK WindowContextListLock = PH_QUEUED_LOCK_INIT; + VOID PhGuiSupportInitialization( VOID ) @@ -47,6 +66,13 @@ VOID PhGuiSupportInitialization( PVOID shell32Handle; PVOID shlwapiHandle; + WindowContextHashTable = PhCreateHashtable( + sizeof(PH_WINDOW_PROPERTY_CONTEXT), + PhpWindowContextHashtableEqualFunction, + PhpWindowContextHashtableHashFunction, + 10 + ); + if (hdc = GetDC(NULL)) { PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); @@ -1208,3 +1234,78 @@ VOID PhLayoutManagerLayout( 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); +} diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index f7d7abb6d655..b999a4e38953 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -457,6 +457,30 @@ 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 + ); + FORCEINLINE VOID PhResizingMinimumSize( _Inout_ PRECT Rect, _In_ WPARAM Edge, From 019e1a1d835cf416cc783ab473c7d81b3bb12164 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:35:29 +1100 Subject: [PATCH 735/839] Improve window property callbacks --- ProcessHacker/affinity.c | 6 ++-- ProcessHacker/appsup.c | 7 ---- ProcessHacker/chcol.c | 6 ++-- ProcessHacker/chdlg.c | 6 ++-- ProcessHacker/chproc.c | 6 ++-- ProcessHacker/colsetmgr.c | 6 ++-- ProcessHacker/findobj.c | 6 ++-- ProcessHacker/gdihndl.c | 23 +++++++++--- ProcessHacker/hndlprp.c | 23 +++++++++--- ProcessHacker/include/appsup.h | 16 +++------ ProcessHacker/include/procprpp.h | 7 ++-- ProcessHacker/infodlg.c | 58 ++++++++++++++---------------- ProcessHacker/jobprp.c | 3 +- ProcessHacker/mdump.c | 30 +++++++++------- ProcessHacker/memedit.c | 6 ++-- ProcessHacker/memprot.c | 6 ++-- ProcessHacker/memrslt.c | 6 ++-- ProcessHacker/memsrch.c | 20 +++++++++-- ProcessHacker/miniinfo.c | 20 ++++++++--- ProcessHacker/mtgndlg.c | 7 ++-- ProcessHacker/netstk.c | 62 ++++++++++++++++---------------- ProcessHacker/ntobjprp.c | 3 +- ProcessHacker/options.c | 6 ++-- ProcessHacker/plugman.c | 18 +++++----- ProcessHacker/procprp.c | 6 ++-- ProcessHacker/procrec.c | 6 ++-- ProcessHacker/prpgenv.c | 6 ++-- ProcessHacker/prpgjob.c | 6 ++-- ProcessHacker/prpgtok.c | 6 ++-- ProcessHacker/runas.c | 6 ++-- ProcessHacker/sessmsg.c | 7 ++-- ProcessHacker/sessprp.c | 6 ---- ProcessHacker/sessshad.c | 7 ++-- ProcessHacker/splitter.c | 9 +++-- ProcessHacker/srvctl.c | 6 ++-- ProcessHacker/srvprp.c | 28 +++++++++------ ProcessHacker/thrdstk.c | 12 +++---- ProcessHacker/tokprp.c | 3 +- 38 files changed, 257 insertions(+), 214 deletions(-) diff --git a/ProcessHacker/affinity.c b/ProcessHacker/affinity.c index 48c3a19f96c6..a46695abf7b9 100644 --- a/ProcessHacker/affinity.c +++ b/ProcessHacker/affinity.c @@ -118,7 +118,7 @@ static INT_PTR CALLBACK PhpProcessAffinityDlgProc( ULONG_PTR affinityMask; ULONG i; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); systemAffinityMask = 0; @@ -227,7 +227,7 @@ static INT_PTR CALLBACK PhpProcessAffinityDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -240,7 +240,7 @@ static INT_PTR CALLBACK PhpProcessAffinityDlgProc( case IDOK: { NTSTATUS status; - PAFFINITY_DIALOG_CONTEXT context = (PAFFINITY_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + PAFFINITY_DIALOG_CONTEXT context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); ULONG i; ULONG_PTR affinityMask; diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 9b389494655f..b4d9388062fc 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1109,13 +1109,6 @@ VOID PhLoadSymbolProviderOptions( PhDereferenceObject(searchPath); } -PWSTR PhMakeContextAtom( - VOID - ) -{ - PH_DEFINE_MAKE_ATOM(L"PH2_Context"); -} - /** * Copies a string into a NMLVGETINFOTIP structure. * diff --git a/ProcessHacker/chcol.c b/ProcessHacker/chcol.c index 75e453219617..96b422507398 100644 --- a/ProcessHacker/chcol.c +++ b/ProcessHacker/chcol.c @@ -110,11 +110,11 @@ INT_PTR CALLBACK PhpColumnsDlgProc( if (uMsg == WM_INITDIALOG) { context = (PCOLUMNS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PCOLUMNS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -202,7 +202,7 @@ INT_PTR CALLBACK PhpColumnsDlgProc( for (i = 0; i < context->Columns->Count; i++) PhFree(context->Columns->Items[i]); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: diff --git a/ProcessHacker/chdlg.c b/ProcessHacker/chdlg.c index b7acd39a9ed4..e9d8ddc22863 100644 --- a/ProcessHacker/chdlg.c +++ b/ProcessHacker/chdlg.c @@ -108,7 +108,7 @@ INT_PTR CALLBACK PhpChoiceDlgProc( RECT rect; ULONG diff; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); SetWindowText(hwndDlg, context->Title); @@ -267,7 +267,7 @@ INT_PTR CALLBACK PhpChoiceDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -279,7 +279,7 @@ INT_PTR CALLBACK PhpChoiceDlgProc( break; case IDOK: { - PCHOICE_DIALOG_CONTEXT context = (PCHOICE_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + 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) diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index e9f3c532d482..05d45796d3cf 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -184,15 +184,15 @@ INT_PTR CALLBACK PhpChooseProcessDlgProc( if (uMsg == WM_INITDIALOG) { context = (PCHOOSE_PROCESS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PCHOOSE_PROCESS_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); } } diff --git a/ProcessHacker/colsetmgr.c b/ProcessHacker/colsetmgr.c index 901dd3628c4c..26516aa53d30 100644 --- a/ProcessHacker/colsetmgr.c +++ b/ProcessHacker/colsetmgr.c @@ -377,11 +377,11 @@ INT_PTR CALLBACK PhpColumnSetEditorDlgProc( context->SettingName = PhCreateString((PWSTR)lParam); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PCOLUMNSET_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -424,7 +424,7 @@ INT_PTR CALLBACK PhpColumnSetEditorDlgProc( { PhDeleteColumnSetList(context->ColumnSetList); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } break; diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 9b531b0fb34e..ff8692d61265 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -1100,11 +1100,11 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( context = PhCreateAlloc(sizeof(PH_HANDLE_SEARCH_CONTEXT)); memset(context, 0, sizeof(PH_HANDLE_SEARCH_CONTEXT)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPH_HANDLE_SEARCH_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -1217,6 +1217,8 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( PhClearList(context->SearchResults); } + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhDereferenceObject(context); PostQuitMessage(0); diff --git a/ProcessHacker/gdihndl.c b/ProcessHacker/gdihndl.c index e5d62f7018ae..762c404c1e4c 100644 --- a/ProcessHacker/gdihndl.c +++ b/ProcessHacker/gdihndl.c @@ -320,17 +320,30 @@ INT_PTR CALLBACK PhpGdiHandlesDlgProc( _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: { - 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); @@ -351,7 +364,7 @@ INT_PTR CALLBACK PhpGdiHandlesDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -364,7 +377,7 @@ INT_PTR CALLBACK PhpGdiHandlesDlgProc( break; case IDC_REFRESH: { - PhpRefreshGdiHandles(hwndDlg, (PGDI_HANDLES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom())); + PhpRefreshGdiHandles(hwndDlg, context); } break; } diff --git a/ProcessHacker/hndlprp.c b/ProcessHacker/hndlprp.c index e98ce8c949ad..f9de4930cf80 100644 --- a/ProcessHacker/hndlprp.c +++ b/ProcessHacker/hndlprp.c @@ -220,12 +220,27 @@ INT_PTR CALLBACK PhpHandleGeneralDlgProc( _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: { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PHANDLE_PROPERTIES_CONTEXT context = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam; PPH_ACCESS_ENTRY accessEntries; ULONG numberOfAccessEntries; HANDLE processHandle; @@ -235,8 +250,6 @@ INT_PTR CALLBACK PhpHandleGeneralDlgProc( // 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); @@ -315,7 +328,7 @@ INT_PTR CALLBACK PhpHandleGeneralDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_NOTIFY: diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index fd79e97e2bd3..335339db4ce7 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -179,13 +179,7 @@ NTAPI PhLoadSymbolProviderOptions( _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider ); -// end_phapppub -PWSTR PhMakeContextAtom( - VOID - ); - -// begin_phapppub PHAPPAPI VOID NTAPI @@ -458,7 +452,7 @@ FORCEINLINE PVOID PhpGenericPropertyPageHeader( _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ PWSTR ContextName + _In_ ULONG ContextHash ) { PVOID context; @@ -470,18 +464,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/procprpp.h b/ProcessHacker/include/procprpp.h index b38f340a7a58..f1878dead5a4 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -63,11 +63,10 @@ FORCEINLINE BOOLEAN PhpPropPageDlgProcHeader( if (uMsg == WM_INITDIALOG) { - // Save the context. - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, (PVOID)lParam); } - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, PhMakeContextAtom()); + propSheetPage = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (!propSheetPage) return FALSE; @@ -83,7 +82,7 @@ FORCEINLINE VOID PhpPropPageDlgProcDestroy( _In_ HWND hwndDlg ) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } #define SET_BUTTON_ICON(Id, Icon) \ diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 73b8fa1916d3..c2faefa86b87 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -28,6 +28,7 @@ typedef struct _INFORMATION_CONTEXT { PWSTR String; ULONG Flags; + PH_LAYOUT_MANAGER LayoutManager; } INFORMATION_CONTEXT, *PINFORMATION_CONTEXT; static RECT MinimumSize = { -1, -1, -1, -1 }; @@ -39,13 +40,25 @@ static INT_PTR CALLBACK PhpInformationDlgProc( _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: { - PINFORMATION_CONTEXT context = (PINFORMATION_CONTEXT)lParam; - PPH_LAYOUT_MANAGER layoutManager; - 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))); @@ -53,16 +66,11 @@ static INT_PTR CALLBACK PhpInformationDlgProc( 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); + 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) { @@ -77,21 +85,14 @@ static INT_PTR CALLBACK PhpInformationDlgProc( 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; + PhDeleteLayoutManager(&context->LayoutManager); - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); - RemoveProp(hwndDlg, L"String"); - RemoveProp(hwndDlg, L"LayoutManager"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -107,22 +108,20 @@ static INT_PTR CALLBACK PhpInformationDlgProc( 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); + PhInitializeStringRefLongHint(&string, context->String); Edit_SetSel(editControl, 0, -1); } else { - string.Buffer = buffer + selStart; + string.Buffer = context->String + selStart; string.Length = (selEnd - selStart) * 2; } @@ -164,7 +163,7 @@ static INT_PTR CALLBACK PhpInformationDlgProc( PH_STRINGREF string; PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhInitializeStringRef(&string, (PWSTR)GetProp(hwndDlg, L"String")); + PhInitializeStringRef(&string, context->String); PhWriteStringAsUtf8FileStream(fileStream, &string); PhDereferenceObject(fileStream); } @@ -181,10 +180,7 @@ static INT_PTR CALLBACK PhpInformationDlgProc( break; case WM_SIZE: { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); + PhLayoutManagerLayout(&context->LayoutManager); } break; case WM_SIZING: diff --git a/ProcessHacker/jobprp.c b/ProcessHacker/jobprp.c index a8151338c781..0ccbf6be37d7 100644 --- a/ProcessHacker/jobprp.c +++ b/ProcessHacker/jobprp.c @@ -147,8 +147,7 @@ FORCEINLINE PJOB_PAGE_CONTEXT PhpJobPageHeader( _In_ LPARAM lParam ) { - return (PJOB_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"JobPageContext"); + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 1); } static VOID PhpAddLimit( diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index e4bf2a239f32..f923a0f01ee4 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -362,14 +362,27 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( _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: { - 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..."); @@ -385,7 +398,7 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -394,9 +407,6 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( { case IDCANCEL: { - PPROCESS_MINIDUMP_CONTEXT context = - (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); context->Stop = TRUE; } @@ -408,8 +418,6 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( { if (wParam == 1) { - PPROCESS_MINIDUMP_CONTEXT context = - (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); ULONG64 currentTickCount; currentTickCount = NtGetTickCount64(); @@ -429,10 +437,6 @@ INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( 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: diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index 1d78c2caa17b..5690bf817dea 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -172,12 +172,12 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( if (uMsg != WM_INITDIALOG) { - context = GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } else { context = (PMEMORY_EDITOR_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } if (!context) @@ -328,7 +328,7 @@ INT_PTR CALLBACK PhpMemoryEditorDlgProc( PhUnregisterDialog(hwndDlg); } - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhDeleteLayoutManager(&context->LayoutManager); diff --git a/ProcessHacker/memprot.c b/ProcessHacker/memprot.c index 17b6d09f09e3..15b2bf0e563d 100644 --- a/ProcessHacker/memprot.c +++ b/ProcessHacker/memprot.c @@ -71,7 +71,7 @@ static INT_PTR CALLBACK PhpMemoryProtectDlgProc( { case WM_INITDIALOG: { - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, (PVOID)lParam); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); @@ -97,7 +97,7 @@ static INT_PTR CALLBACK PhpMemoryProtectDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -110,7 +110,7 @@ static INT_PTR CALLBACK PhpMemoryProtectDlgProc( case IDOK: { NTSTATUS status; - PMEMORY_PROTECT_CONTEXT context = (PMEMORY_PROTECT_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + PMEMORY_PROTECT_CONTEXT context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); HANDLE processHandle; ULONG64 protect; diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index f4b3140a86d9..8d36878e31b9 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -263,12 +263,12 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( if (uMsg != WM_INITDIALOG) { - context = GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } else { context = (PMEMORY_RESULTS_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } if (!context) @@ -362,7 +362,7 @@ INT_PTR CALLBACK PhpMemoryResultsDlgProc( PhDeleteLayoutManager(&context->LayoutManager); PhUnregisterDialog(hwndDlg); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)context->Results->Items, context->Results->Count); PhDereferenceObject(context->Results); diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index 1d38f573cf66..fe66dd890010 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -540,12 +540,27 @@ INT_PTR CALLBACK PhpMemoryStringDlgProc( _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)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); SetDlgItemText(hwndDlg, IDC_MINIMUMLENGTH, L"10"); Button_SetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE), BST_CHECKED); @@ -554,7 +569,7 @@ INT_PTR CALLBACK PhpMemoryStringDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -566,7 +581,6 @@ INT_PTR CALLBACK PhpMemoryStringDlgProc( break; case IDOK: { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); ULONG64 minimumLength = 10; PhStringToInteger64(&PhaGetDlgItemText(hwndDlg, IDC_MINIMUMLENGTH)->sr, 0, &minimumLength); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index adf3e23e627b..599135aba062 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1278,7 +1278,20 @@ INT_PTR CALLBACK PhMipListSectionDialogProc( _In_ LPARAM lParam ) { - PPH_MINIINFO_LIST_SECTION listSection = (PPH_MINIINFO_LIST_SECTION)GetProp(hwndDlg, PhMakeContextAtom()); + 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) { @@ -1286,9 +1299,6 @@ INT_PTR CALLBACK PhMipListSectionDialogProc( { PPH_LAYOUT_ITEM layoutItem; - listSection = (PPH_MINIINFO_LIST_SECTION)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)listSection); - listSection->DialogHandle = hwndDlg; listSection->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); @@ -1313,7 +1323,7 @@ INT_PTR CALLBACK PhMipListSectionDialogProc( case WM_DESTROY: { PhDeleteLayoutManager(&listSection->LayoutManager); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_SIZE: diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index a9c5c8ef7ed6..2d5630e7d6a7 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -182,22 +182,21 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( if (uMsg == WM_INITDIALOG) { context = (PMITIGATION_POLICY_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PMITIGATION_POLICY_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 == NULL) return FALSE; - switch (uMsg) { case WM_INITDIALOG: diff --git a/ProcessHacker/netstk.c b/ProcessHacker/netstk.c index 8ae55638bfed..5ab9e2c76ce8 100644 --- a/ProcessHacker/netstk.c +++ b/ProcessHacker/netstk.c @@ -28,6 +28,7 @@ typedef struct NETWORK_STACK_CONTEXT { HWND ListViewHandle; + PH_LAYOUT_MANAGER LayoutManager; PPH_NETWORK_ITEM NetworkItem; PPH_SYMBOL_PROVIDER SymbolProvider; HANDLE LoadingProcessId; @@ -126,36 +127,44 @@ static INT_PTR CALLBACK PhpNetworkStackDlgProc( _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: { - 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); + 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"); - 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); + 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) { @@ -174,13 +183,13 @@ static INT_PTR CALLBACK PhpNetworkStackDlgProc( { PPH_STRING name; - address = *(PVOID *)&networkStackContext->NetworkItem->OwnerInfo[i]; + address = *(PVOID *)&context->NetworkItem->OwnerInfo[i]; if ((ULONG_PTR)address < PAGE_SIZE) // stop at an invalid address break; name = PhGetSymbolFromAddress( - networkStackContext->SymbolProvider, + context->SymbolProvider, (ULONG64)address, NULL, NULL, @@ -194,17 +203,9 @@ static INT_PTR CALLBACK PhpNetworkStackDlgProc( break; case WM_DESTROY: { - PPH_LAYOUT_MANAGER layoutManager; - PNETWORK_STACK_CONTEXT networkStackContext; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); + PhDeleteLayoutManager(&context->LayoutManager); - networkStackContext = (PNETWORK_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - RemoveProp(hwndDlg, L"LayoutManager"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -220,10 +221,7 @@ static INT_PTR CALLBACK PhpNetworkStackDlgProc( break; case WM_SIZE: { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); + PhLayoutManagerLayout(&context->LayoutManager); } break; case WM_SIZING: diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index e7aa709fa6e8..4196733c8e6e 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -150,8 +150,7 @@ FORCEINLINE PCOMMON_PAGE_CONTEXT PhpCommonPageHeader( _In_ LPARAM lParam ) { - return (PCOMMON_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"PageContext"); + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 2); } HPROPSHEETPAGE PhCreateEventPage( diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 15e6f4e6d531..0992214bbd1d 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1472,7 +1472,7 @@ static INT_PTR CALLBACK PhpOptionsAdvancedEditDlgProc( SetWindowText(hwndDlg, L"Setting Editor"); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetProp(hwndDlg, PhMakeContextAtom(), setting); + 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); @@ -1490,7 +1490,7 @@ static INT_PTR CALLBACK PhpOptionsAdvancedEditDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhDeleteLayoutManager(&LayoutManager); } @@ -1509,7 +1509,7 @@ static INT_PTR CALLBACK PhpOptionsAdvancedEditDlgProc( break; case IDOK: { - PPH_SETTING setting = (PPH_SETTING)GetProp(hwndDlg, PhMakeContextAtom()); + PPH_SETTING setting = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PPH_STRING settingValue = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUE))); if (!PhSettingFromString( diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index 6d5462c518ea..b105bbd93b21 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -628,11 +628,11 @@ INT_PTR CALLBACK PhpPluginsDlgProc( context = PhAllocate(sizeof(PH_PLUGMAN_CONTEXT)); memset(context, 0, sizeof(PH_PLUGMAN_CONTEXT)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPH_PLUGMAN_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -679,6 +679,8 @@ INT_PTR CALLBACK PhpPluginsDlgProc( DeletePluginsTree(context); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); PostQuitMessage(0); @@ -955,15 +957,15 @@ INT_PTR CALLBACK PhpPluginPropertiesDlgProc( if (uMsg == WM_INITDIALOG) { selectedPlugin = (PPH_PLUGIN)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)selectedPlugin); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, selectedPlugin); } else { - selectedPlugin = (PPH_PLUGIN)GetProp(hwndDlg, PhMakeContextAtom()); + selectedPlugin = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -1097,15 +1099,15 @@ INT_PTR CALLBACK PhpPluginsDisabledDlgProc( context = PhAllocate(sizeof(PLUGIN_DISABLED_CONTEXT)); memset(context, 0, sizeof(PLUGIN_DISABLED_CONTEXT)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPLUGIN_DISABLED_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index a9909f48b557..593cb33b8114 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -167,7 +167,7 @@ INT CALLBACK PhpPropSheetProc( PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)propSheetContext); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, propSheetContext); SetWindowSubclass(hwndDlg, PhpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); if (MinimumSize.left == -1) @@ -193,7 +193,7 @@ PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( _In_ HWND hwnd ) { - return (PPH_PROCESS_PROPSHEETCONTEXT)GetProp(hwnd, PhMakeContextAtom()); + return PhGetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); } LRESULT CALLBACK PhpPropSheetWndProc( @@ -236,7 +236,7 @@ LRESULT CALLBACK PhpPropSheetWndProc( case WM_NCDESTROY: { RemoveWindowSubclass(hwnd, PhpPropSheetWndProc, uIdSubclass); - RemoveProp(hwnd, PhMakeContextAtom()); + PhRemoveWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index fb654e1aa669..8237b18fabc9 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -98,15 +98,15 @@ INT_PTR CALLBACK PhpProcessRecordDlgProc( if (uMsg == WM_INITDIALOG) { context = (PPROCESS_RECORD_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPROCESS_RECORD_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index 02d007c0610d..fa220acf37bb 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -442,15 +442,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); } } diff --git a/ProcessHacker/prpgjob.c b/ProcessHacker/prpgjob.c index a6858385e81e..fcbe8ae1e249 100644 --- a/ProcessHacker/prpgjob.c +++ b/ProcessHacker/prpgjob.c @@ -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/prpgtok.c b/ProcessHacker/prpgtok.c index d19a654673e9..44a1e9a287d9 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -59,12 +59,12 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( { 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 PhpProcessTokenHookProc( PhDoPropPageLayout(hwndDlg); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)TRUE); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr(TRUE)); } } break; diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index bc53241c45c5..1c93b78e143a 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -247,12 +247,12 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (uMsg != WM_INITDIALOG) { - context = (PRUNAS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } else { context = (PRUNAS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } if (!context) @@ -356,7 +356,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (context->DesktopList) PhDereferenceObject(context->DesktopList); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: diff --git a/ProcessHacker/sessmsg.c b/ProcessHacker/sessmsg.c index 2e48174e87e2..390ffca4d547 100644 --- a/ProcessHacker/sessmsg.c +++ b/ProcessHacker/sessmsg.c @@ -70,7 +70,8 @@ INT_PTR CALLBACK PhpSessionSendMessageDlgProc( { HWND iconComboBox; - SetProp(hwndDlg, L"SessionId", UlongToHandle((ULONG)lParam)); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr((ULONG)lParam)); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); iconComboBox = GetDlgItem(hwndDlg, IDC_TYPE); @@ -96,7 +97,7 @@ INT_PTR CALLBACK PhpSessionSendMessageDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, L"SessionId"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -108,7 +109,7 @@ INT_PTR CALLBACK PhpSessionSendMessageDlgProc( break; case IDOK: { - ULONG sessionId = HandleToUlong(GetProp(hwndDlg, L"SessionId")); + ULONG sessionId = PtrToUlong(PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)); PPH_STRING title; PPH_STRING text; ULONG icon = 0; diff --git a/ProcessHacker/sessprp.c b/ProcessHacker/sessprp.c index 076696e7b71a..91e06a80cdee 100644 --- a/ProcessHacker/sessprp.c +++ b/ProcessHacker/sessprp.c @@ -81,7 +81,6 @@ INT_PTR CALLBACK PhpSessionPropertiesDlgProc( ULONG returnLength; PWSTR stateString; - SetProp(hwndDlg, L"SessionId", UlongToHandle(sessionId)); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); // Query basic session information @@ -215,11 +214,6 @@ INT_PTR CALLBACK PhpSessionPropertiesDlgProc( SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); } break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, L"SessionId"); - } - break; case WM_COMMAND: { switch (LOWORD(wParam)) diff --git a/ProcessHacker/sessshad.c b/ProcessHacker/sessshad.c index 62bd01a8317a..23b156373480 100644 --- a/ProcessHacker/sessshad.c +++ b/ProcessHacker/sessshad.c @@ -140,7 +140,8 @@ INT_PTR CALLBACK PhpSessionShadowDlgProc( ULONG i; PWSTR stringToSelect; - SetProp(hwndDlg, L"SessionId", UlongToHandle((ULONG)lParam)); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr((ULONG)lParam)); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); hotkey = PhGetIntegerPairSetting(L"SessionShadowHotkey"); @@ -171,7 +172,7 @@ INT_PTR CALLBACK PhpSessionShadowDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, L"SessionId"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -183,7 +184,7 @@ INT_PTR CALLBACK PhpSessionShadowDlgProc( break; case IDOK: { - ULONG sessionId = HandleToUlong(GetProp(hwndDlg, L"SessionId")); + ULONG sessionId = PtrToUlong(PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)); ULONG virtualKey; ULONG modifiers; WCHAR computerName[64]; diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 208e3c22d8e9..df6aa2baed33 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -110,11 +110,16 @@ LRESULT CALLBACK HSplitterWindowProc( { LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam; context = cs->lpCreateParams; - SetProp(hwnd, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPH_HSPLITTER_CONTEXT)GetProp(hwnd, PhMakeContextAtom()); + context = PhGetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); + } } if (!context) diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 4992df84a96e..a3aab1c31363 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -207,15 +207,15 @@ INT_PTR CALLBACK PhpServicesPageProc( if (uMsg == WM_INITDIALOG) { servicesContext = (PPH_SERVICES_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)servicesContext); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, servicesContext); } else { - servicesContext = (PPH_SERVICES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + servicesContext = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index 2cdb40209fe3..e093b457f993 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -209,12 +209,27 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( _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: { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PSERVICE_PROPERTIES_CONTEXT context = (PSERVICE_PROPERTIES_CONTEXT)propSheetPage->lParam; PPH_SERVICE_ITEM serviceItem = context->ServiceItem; SC_HANDLE serviceHandle; ULONG startType; @@ -224,8 +239,6 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( // 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, @@ -305,14 +318,11 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: { - PSERVICE_PROPERTIES_CONTEXT context = - (PSERVICE_PROPERTIES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - switch (LOWORD(wParam)) { case IDCANCEL: @@ -415,8 +425,6 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( 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; diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index b63a2f5c2619..8ba355c9777b 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -738,11 +738,11 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( if (uMsg == WM_INITDIALOG) { context = (PPH_THREAD_STACK_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPH_THREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -835,7 +835,7 @@ static INT_PTR CALLBACK PhpThreadStackDlgProc( PhSaveWindowPlacementToSetting(NULL, L"ThreadStackWindowSize", hwndDlg); - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: @@ -1147,11 +1147,11 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( if (uMsg == WM_INITDIALOG) { context = (PPH_THREAD_STACK_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PPH_THREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -1185,7 +1185,7 @@ static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( break; case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_COMMAND: diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 906bd4dc7222..85de914d2542 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -474,8 +474,7 @@ FORCEINLINE PTOKEN_PAGE_CONTEXT PhpTokenPageHeader( _In_ LPARAM lParam ) { - return (PTOKEN_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"TokenPageContext"); + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 3); } INT_PTR CALLBACK PhpTokenPageProc( From e404712aceb9a9f7cb0e8a93314de90bf74e770e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:36:00 +1100 Subject: [PATCH 736/839] Update export definitions --- ProcessHacker/ProcessHacker.def | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index fcb0537c4d8a..45755ab3a0bd 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -509,6 +509,9 @@ EXPORTS PhSetListViewItemImageIndex PhSetListViewSubItem PhSetStateAllListViewItems + PhGetWindowContext + PhSetWindowContext + PhRemoveWindowContext ; hndlinfo PhEnumObjectTypes From 57e2436861222719543e53719e2028bee85037e9 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:39:30 +1100 Subject: [PATCH 737/839] Plugins: Improve window property callbacks --- plugins/ExtendedServices/depend.c | 12 ++++++------ plugins/ExtendedServices/other.c | 6 +++--- plugins/ExtendedServices/recovery.c | 12 ++++++------ plugins/ExtendedServices/srvprgrs.c | 6 +++--- plugins/ExtendedServices/trigger.c | 12 ++++++------ plugins/ExtendedServices/triggpg.c | 6 +++--- plugins/ExtendedTools/gpuprprp.c | 12 ++++++------ plugins/ExtendedTools/unldll.c | 6 +++--- plugins/ExtendedTools/wswatch.c | 6 +++--- plugins/HardwareDevices/diskdetails.c | 12 ++++++------ plugins/HardwareDevices/diskgraph.c | 12 ++++++------ plugins/HardwareDevices/diskoptions.c | 6 +++--- plugins/HardwareDevices/netdetails.c | 6 +++--- plugins/HardwareDevices/netgraph.c | 12 ++++++------ plugins/HardwareDevices/netoptions.c | 6 +++--- plugins/HardwareDevices/prpsh.c | 4 ++-- plugins/HardwareDevices/prpsh.h | 6 +++--- plugins/NetworkTools/ping.c | 6 +++--- plugins/NetworkTools/tracert.c | 6 +++--- plugins/NetworkTools/whois.c | 6 +++--- plugins/ToolStatus/customizesb.c | 6 +++--- plugins/ToolStatus/customizetb.c | 6 +++--- plugins/UserNotes/main.c | 6 +++--- plugins/WindowExplorer/wnddlg.c | 6 +++--- plugins/WindowExplorer/wndprp.c | 20 ++++++++++---------- 25 files changed, 102 insertions(+), 102 deletions(-) diff --git a/plugins/ExtendedServices/depend.c b/plugins/ExtendedServices/depend.c index dc15aa52f2ad..99b6aaeecff2 100644 --- a/plugins/ExtendedServices/depend.c +++ b/plugins/ExtendedServices/depend.c @@ -117,14 +117,14 @@ INT_PTR CALLBACK EspServiceDependenciesDlgProc( context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_LIST_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) @@ -245,14 +245,14 @@ INT_PTR CALLBACK EspServiceDependentsDlgProc( context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_LIST_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) diff --git a/plugins/ExtendedServices/other.c b/plugins/ExtendedServices/other.c index 28d98146d6a9..e93bfb620a1b 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -319,14 +319,14 @@ INT_PTR CALLBACK EspServiceOtherDlgProc( context = PhAllocate(sizeof(SERVICE_OTHER_CONTEXT)); memset(context, 0, sizeof(SERVICE_OTHER_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_OTHER_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) diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index 47a55520c2f0..1949ff3a7ffc 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -274,14 +274,14 @@ INT_PTR CALLBACK EspServiceRecoveryDlgProc( context = PhAllocate(sizeof(SERVICE_RECOVERY_CONTEXT)); memset(context, 0, sizeof(SERVICE_RECOVERY_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_RECOVERY_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) @@ -594,14 +594,14 @@ INT_PTR CALLBACK RestartComputerDlgProc( if (uMsg == WM_INITDIALOG) { context = (PSERVICE_RECOVERY_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_RECOVERY_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) diff --git a/plugins/ExtendedServices/srvprgrs.c b/plugins/ExtendedServices/srvprgrs.c index 3b19031596ef..059b95b36a93 100644 --- a/plugins/ExtendedServices/srvprgrs.c +++ b/plugins/ExtendedServices/srvprgrs.c @@ -42,14 +42,14 @@ INT_PTR CALLBACK EspRestartServiceDlgProc( if (uMsg == WM_INITDIALOG) { context = (PRESTART_SERVICE_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PRESTART_SERVICE_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) diff --git a/plugins/ExtendedServices/trigger.c b/plugins/ExtendedServices/trigger.c index fb2c2803837e..a423158167a4 100644 --- a/plugins/ExtendedServices/trigger.c +++ b/plugins/ExtendedServices/trigger.c @@ -1264,14 +1264,14 @@ INT_PTR CALLBACK EspServiceTriggerDlgProc( if (uMsg == WM_INITDIALOG) { context = (PES_TRIGGER_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PES_TRIGGER_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) @@ -1678,14 +1678,14 @@ INT_PTR CALLBACK ValueDlgProc( if (uMsg == WM_INITDIALOG) { context = (PES_TRIGGER_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PES_TRIGGER_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) diff --git a/plugins/ExtendedServices/triggpg.c b/plugins/ExtendedServices/triggpg.c index 514dd38a0d5b..103ee6672e7f 100644 --- a/plugins/ExtendedServices/triggpg.c +++ b/plugins/ExtendedServices/triggpg.c @@ -60,14 +60,14 @@ INT_PTR CALLBACK EspServiceTriggersDlgProc( context = PhAllocate(sizeof(SERVICE_TRIGGERS_CONTEXT)); memset(context, 0, sizeof(SERVICE_TRIGGERS_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_TRIGGERS_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) diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index fc9138bf0915..bd89018f544e 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -73,16 +73,16 @@ INT_PTR CALLBACK GpuDetailsDialogProc( if (uMsg == WM_INITDIALOG) { context = (PET_GPU_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PET_GPU_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { context->DetailsHandle = NULL; - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -126,15 +126,15 @@ INT_PTR CALLBACK GpuPanelDialogProc( if (uMsg == WM_INITDIALOG) { context = (PET_GPU_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PET_GPU_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); } } diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 7640363288c0..02cd7cff34cf 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -287,14 +287,14 @@ INT_PTR CALLBACK EtpUnloadedDllsDlgProc( if (uMsg == WM_INITDIALOG) { context = (PUNLOADED_DLLS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PUNLOADED_DLLS_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) diff --git a/plugins/ExtendedTools/wswatch.c b/plugins/ExtendedTools/wswatch.c index 9e7ca403c5b5..e8aa9d969a13 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -411,14 +411,14 @@ INT_PTR CALLBACK EtpWsWatchDlgProc( if (uMsg == WM_INITDIALOG) { context = (PWS_WATCH_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PWS_WATCH_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) diff --git a/plugins/HardwareDevices/diskdetails.c b/plugins/HardwareDevices/diskdetails.c index 1165e41be5ec..79986e5bbf1f 100644 --- a/plugins/HardwareDevices/diskdetails.c +++ b/plugins/HardwareDevices/diskdetails.c @@ -568,15 +568,15 @@ INT_PTR CALLBACK DiskDriveFileSystemDetailsDlgProc( 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_DISK_COUNTERS_COLUMNS, context->ListViewHandle); PhFree(context); @@ -648,15 +648,15 @@ INT_PTR CALLBACK DiskDriveSmartDetailsDlgProc( 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); diff --git a/plugins/HardwareDevices/diskgraph.c b/plugins/HardwareDevices/diskgraph.c index 422f031bee74..bc25e20236aa 100644 --- a/plugins/HardwareDevices/diskgraph.c +++ b/plugins/HardwareDevices/diskgraph.c @@ -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); } } diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index 968ec97569f0..b3648a3db728 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -595,11 +595,11 @@ 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) { @@ -610,7 +610,7 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( FreeListViewDiskDriveEntries(context); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } diff --git a/plugins/HardwareDevices/netdetails.c b/plugins/HardwareDevices/netdetails.c index 03cd4e92cf0a..491deee744cb 100644 --- a/plugins/HardwareDevices/netdetails.c +++ b/plugins/HardwareDevices/netdetails.c @@ -497,14 +497,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) diff --git a/plugins/HardwareDevices/netgraph.c b/plugins/HardwareDevices/netgraph.c index 83890c9e8fa7..12b53cba95b2 100644 --- a/plugins/HardwareDevices/netgraph.c +++ b/plugins/HardwareDevices/netgraph.c @@ -166,15 +166,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); } } @@ -211,11 +211,11 @@ INT_PTR CALLBACK NetAdapterDialogProc( { 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) { @@ -228,7 +228,7 @@ INT_PTR CALLBACK NetAdapterDialogProc( if (context->PanelWindowHandle) DestroyWindow(context->PanelWindowHandle); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index e6a24711514f..c6c82890e62b 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -694,11 +694,11 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( context = (PDV_NETADAPTER_CONTEXT)PhAllocate(sizeof(DV_NETADAPTER_CONTEXT)); memset(context, 0, 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) { @@ -709,7 +709,7 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( FreeListViewAdapterEntries(context); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index 546ec8f469db..4b0aedf56512 100644 --- a/plugins/HardwareDevices/prpsh.c +++ b/plugins/HardwareDevices/prpsh.c @@ -144,8 +144,8 @@ INT CALLBACK PvpPropSheetProc( PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); - SetProp(hwndDlg, L"HdContext", (HANDLE)propSheetContext); SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); + PhSetWindowContext(hwndDlg, ULONG_MAX, propSheetContext); if (MinimumSize.left == -1) { @@ -170,7 +170,7 @@ PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( _In_ HWND hwnd ) { - return (PPV_PROPSHEETCONTEXT)GetProp(hwnd, L"HdContext"); + return PhGetWindowContext(hwnd, ULONG_MAX); } LRESULT CALLBACK PvpPropSheetWndProc( diff --git a/plugins/HardwareDevices/prpsh.h b/plugins/HardwareDevices/prpsh.h index d6417da4f404..dd2411ab700c 100644 --- a/plugins/HardwareDevices/prpsh.h +++ b/plugins/HardwareDevices/prpsh.h @@ -29,9 +29,9 @@ typedef struct _PV_PROPSHEETCONTEXT { + BOOLEAN LayoutInitialized; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM TabPageItem; - BOOLEAN LayoutInitialized; } PV_PROPSHEETCONTEXT, *PPV_PROPSHEETCONTEXT; typedef struct _PV_PROPCONTEXT @@ -114,10 +114,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/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index 4fd646f6e450..2dab53790826 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -269,11 +269,11 @@ INT_PTR CALLBACK NetworkPingWndProc( if (uMsg == WM_INITDIALOG) { context = (PNETWORK_PING_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PNETWORK_PING_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (context == NULL) @@ -385,7 +385,7 @@ INT_PTR CALLBACK NetworkPingWndProc( PhDeleteGraphState(&context->PingGraphState); PhDeleteLayoutManager(&context->LayoutManager); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); PostQuitMessage(0); diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index a0683dc89945..3ed9273c75b6 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -585,11 +585,11 @@ INT_PTR CALLBACK TracertDlgProc( if (uMsg == WM_INITDIALOG) { context = (PNETWORK_TRACERT_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PNETWORK_TRACERT_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { @@ -604,7 +604,7 @@ INT_PTR CALLBACK TracertDlgProc( PhDeleteWorkQueue(&context->WorkQueue); PhDeleteLayoutManager(&context->LayoutManager); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhDereferenceObject(context); PostQuitMessage(0); diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index 0f8e0bf73469..686dc934ccb5 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -370,17 +370,17 @@ INT_PTR CALLBACK NetworkOutputDlgProc( if (uMsg == WM_INITDIALOG) { context = (PNETWORK_WHOIS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PNETWORK_WHOIS_CONTEXT)GetProp(hwndDlg, L"Context"); + 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); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhDereferenceObject(context); PostQuitMessage(0); diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index d823a35a3339..680eb6b368cb 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -279,15 +279,15 @@ INT_PTR CALLBACK CustomizeStatusBarDialogProc( context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PCUSTOMIZE_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); PhFree(context); } } diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index c103c01b4d18..1b7c48c04547 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -490,15 +490,15 @@ INT_PTR CALLBACK CustomizeToolbarDialogProc( context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PCUSTOMIZE_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); PhFree(context); } } diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index 69c0ca077f65..05cfa44a0138 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -1803,14 +1803,14 @@ INT_PTR CALLBACK ServiceCommentPageDlgProc( context = PhAllocate(sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); memset(context, 0, sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PSERVICE_COMMENT_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); } if (!context) diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index 9aa47dd23c1f..4ab23fe9a491 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -329,14 +329,14 @@ INT_PTR CALLBACK WepWindowsDlgProc( if (uMsg == WM_INITDIALOG) { context = (PWINDOWS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PWINDOWS_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) diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index c853353a0b11..98360506b553 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -362,7 +362,7 @@ static INT CALLBACK WepPropSheetProc( oldWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc); - SetProp(hwndDlg, L"OldWndProc", (HANDLE)oldWndProc); + PhSetWindowContext(hwndDlg, 1, (HANDLE)oldWndProc); // Hide the Cancel button. ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); @@ -386,20 +386,20 @@ LRESULT CALLBACK WepPropSheetWndProc( _In_ LPARAM lParam ) { - WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, L"OldWndProc"); + WNDPROC oldWndProc = PhGetWindowContext(hwnd, 1); switch (uMsg) { case WM_DESTROY: { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); - RemoveProp(hwnd, L"OldWndProc"); - RemoveProp(hwnd, L"Moved"); + PhRemoveWindowContext(hwnd, 1); + PhRemoveWindowContext(hwnd, 2); } break; case WM_SHOWWINDOW: { - if (!GetProp(hwnd, L"Moved")) + if (!PhGetWindowContext(hwnd, 2)) { // Move the Refresh button to where the OK button is, and move the OK button to // where the Cancel button is. @@ -407,7 +407,7 @@ LRESULT CALLBACK WepPropSheetWndProc( // 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); + PhSetWindowContext(hwnd, 2, UlongToPtr(1)); } } break; @@ -568,15 +568,15 @@ FORCEINLINE BOOLEAN WepPropPageDlgProcHeader( if (uMsg == WM_INITDIALOG) { propSheetPage = (LPPROPSHEETPAGE)lParam; - // Save the context. - SetProp(hwndDlg, L"PropSheetPage", (HANDLE)lParam); + + PhSetWindowContext(hwndDlg, ULONG_MAX, (HANDLE)lParam); } else { - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"PropSheetPage"); + propSheetPage = PhGetWindowContext(hwndDlg, ULONG_MAX); if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"PropSheetPage"); + PhRemoveWindowContext(hwndDlg, ULONG_MAX); } if (!propSheetPage) From 6b67c9e5efba29d5904bc1f68258d3a790081479 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:40:23 +1100 Subject: [PATCH 738/839] Fix type name --- phlib/include/mapimg.h | 4 ++-- phlib/mapimg.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index 0d41efbb4926..1293fa34ac3a 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -156,7 +156,7 @@ PhLoadRemoteMappedImage( _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ); -typedef NTSTATUS (NTAPI *PPH_READ_VIRTUAL_MEMORY)( +typedef NTSTATUS (NTAPI *PPH_READ_VIRTUAL_MEMORY_CALLBACK)( _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress, _Out_writes_bytes_(BufferSize) PVOID Buffer, @@ -169,7 +169,7 @@ NTAPI PhLoadRemoteMappedImageEx( _In_ HANDLE ProcessHandle, _In_ PVOID ViewBase, - _In_ PPH_READ_VIRTUAL_MEMORY ReadVirtualMemory, + _In_ PPH_READ_VIRTUAL_MEMORY_CALLBACK ReadVirtualMemoryCallback, _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ); diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 12e2203372e9..18f226724789 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -508,7 +508,7 @@ NTSTATUS PhLoadRemoteMappedImage( NTSTATUS PhLoadRemoteMappedImageEx( _In_ HANDLE ProcessHandle, _In_ PVOID ViewBase, - _In_ PPH_READ_VIRTUAL_MEMORY ReadVirtualMemory, + _In_ PPH_READ_VIRTUAL_MEMORY_CALLBACK ReadVirtualMemoryCallback, _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ) { @@ -520,7 +520,7 @@ NTSTATUS PhLoadRemoteMappedImageEx( RemoteMappedImage->ViewBase = ViewBase; - status = ReadVirtualMemory( + status = ReadVirtualMemoryCallback( ProcessHandle, ViewBase, &dosHeader, @@ -543,7 +543,7 @@ NTSTATUS PhLoadRemoteMappedImageEx( if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) return STATUS_INVALID_IMAGE_FORMAT; - status = ReadVirtualMemory( + status = ReadVirtualMemoryCallback( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), &ntHeaders, @@ -579,7 +579,7 @@ NTSTATUS PhLoadRemoteMappedImageEx( RemoteMappedImage->NtHeaders = PhAllocate(ntHeadersSize); - status = ReadVirtualMemory( + status = ReadVirtualMemoryCallback( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), RemoteMappedImage->NtHeaders, From d2d3043ca703c38deb31b00f894e92ba90bacf28 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:42:02 +1100 Subject: [PATCH 739/839] OnlineChecks: Fix hybrid-anaysis maximum upload size --- plugins/OnlineChecks/upload.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 023a7e38b54c..2e925bac3bfe 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -246,8 +246,8 @@ NTSTATUS HashFileAndResetPosition( ); } - NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &priority, sizeof(LONG)); - NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, &ioPriority, sizeof(IO_PRIORITY_HINT)); + PhSetThreadBasePriority(NtCurrentThread(), priority); + PhSetThreadIoPriority(NtCurrentThread(), ioPriority); return status; } @@ -970,6 +970,17 @@ NTSTATUS UploadCheckThreadStart( 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 From 4141c87897fae6236d524eb05ea71ee53d0d8e48 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 29 Jan 2018 18:46:48 +1100 Subject: [PATCH 740/839] Add missing file from commit 6b67c9e --- ProcessHacker/modprv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index dd0d901009c5..ec632a63569c 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -506,12 +506,12 @@ VOID PhModuleProviderUpdate( moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) { PH_REMOTE_MAPPED_IMAGE remoteMappedImage; - PPH_READ_VIRTUAL_MEMORY readVirtualMemory; + PPH_READ_VIRTUAL_MEMORY_CALLBACK readVirtualMemoryCallback; if (moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) - readVirtualMemory = KphReadVirtualMemoryUnsafe; + readVirtualMemoryCallback = KphReadVirtualMemoryUnsafe; else - readVirtualMemory = NtReadVirtualMemory; + readVirtualMemoryCallback = NtReadVirtualMemory; // Note: // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used @@ -523,7 +523,7 @@ VOID PhModuleProviderUpdate( moduleItem->Flags &= ~LDRP_IMAGE_NOT_AT_BASE; - if (NT_SUCCESS(PhLoadRemoteMappedImageEx(moduleProvider->ProcessHandle, moduleItem->BaseAddress, readVirtualMemory, &remoteMappedImage))) + if (NT_SUCCESS(PhLoadRemoteMappedImageEx(moduleProvider->ProcessHandle, moduleItem->BaseAddress, readVirtualMemoryCallback, &remoteMappedImage))) { ULONG_PTR imageBase = 0; DWORD entryPoint = 0; From a685bac6ac2c8a058cb114ba81650933691cd124 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 31 Jan 2018 07:20:58 +1100 Subject: [PATCH 741/839] BuildTools: Fix build flag issue --- tools/CustomBuildTool/Source Files/Build.cs | 5 +++-- .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 8dac9e821fc5..05ea621f630e 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -546,7 +546,7 @@ public static bool CopyKProcessHacker(BuildFlags Flags) if (!File.Exists("build\\kph.key")) return true; - if (Flags.HasFlag(BuildFlags.BuildDebug)) + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { if (File.Exists("bin\\Debug32\\ProcessHacker.exe")) { @@ -572,7 +572,8 @@ public static bool CopyKProcessHacker(BuildFlags Flags) } } } - else + + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) { if (File.Exists("bin\\Release32\\ProcessHacker.exe")) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 109a98cde269e8b23dc2f5243c9aa522fd4c41f4..66b2a6015e56716d1ae01195b8cd4b1ef15531b9 100644 GIT binary patch delta 2007 zcmY+_ZERCj7zgnG={C5%?aFl#g=_X;VT|%KP+}?S4zqTz$-bi)E%$pPKHpT3jJl zc^}P4%uZK?Dm3~EpYZKYEOeZn8}$?!?#KE(zg0o~OsV<|Lzh^srh1EIcHu@N8)c1} z+F2sAbEekbDRtV^IhNIK%nlHPKSZSmqEEY-Kq!FQzKUx<_W&W^3BLQom$} zX=42xsbRI$qWay+X)0KQ1c%>K!5R#+j#0r{9AkY+1#9sstB4BL;VkPuDp-dfSf5cr zAAV(hM+JTOlXa2m)#Iw6D3!lP1?eSjkz2ZxwzL5X>kw^e18!jbOwIU_&ALj>_~B-G zshLL1U^P)QjhMx{K+Oa&hn1ve0`QoMb}PTB5j0^jhgVdM)WABD8NpICvrc7dq&C(} z`VE(16>9wqOTqGEHp3+pISz z3t@H?16*Xst8pajYm3*$0}lK$k=>cIbSe@<>MAz zjvO=VL|z}jG1~7z*4YlRRc*x~<|VOR zZ5y{}JC1VZ$1b%UTG(i`6R(IpY6p5)`C^y)2zHo~t*sol%1XSGxZR)LskOel`pnp= zb=6%5k{<-RY+DlEZ%SN6bkUk;l!cbgp{uituGy~3bUyn$KG#57=M#6wtKa*g**&9X z)3(IU%ZkuWYQ_yKKR?m;7xxugTyzuL6F-g=i+G8>(=mF`F)FV0SVl_@e&`ls;(r|l Bn+gB` delta 2030 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 delta 7442 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 From 41e6ac72d87bc694c24516307115ad3e25f83822 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 2 Feb 2018 00:20:40 +1100 Subject: [PATCH 742/839] Fix disabling notifyicons regression --- ProcessHacker/notifico.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index b5584a4c3ab2..a767031bc1ee 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -75,16 +75,7 @@ VOID PhNfLoadSettings( remaining = settingsString->sr; if (remaining.Length == 0) - { - PPH_NF_ICON icon; - - // Load default settings. - if (icon = PhNfGetIconById(PH_TRAY_ICON_ID_CPU_USAGE)) - icon->Flags |= PH_NF_ICON_ENABLED; - - PhDereferenceObject(settingsString); return; - } while (remaining.Length != 0) { From 3b7f3baf96ca28ff39f0665615476f0933da8230 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 3 Feb 2018 04:59:34 +1100 Subject: [PATCH 743/839] OnlineChecks: Fix hybrid-analysis error uploading 32bit binaries --- plugins/OnlineChecks/upload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 2e925bac3bfe..fabb0a4353bd 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -462,7 +462,7 @@ NTSTATUS UploadFileThreadStart( switch (machineType) { case IMAGE_FILE_MACHINE_I386: - environmentId = 100; + environmentId = 110; break; case IMAGE_FILE_MACHINE_AMD64: environmentId = 120; From d7912f701ff8916ce8e19103da5bc478c236ad72 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 02:42:54 +1100 Subject: [PATCH 744/839] Add protected handle highlighting --- ProcessHacker/include/phsettings.h | 2 ++ ProcessHacker/include/procprv.h | 2 +- ProcessHacker/procprv.c | 45 ++++++++++++++++++++++++++++-- ProcessHacker/proctree.c | 2 ++ ProcessHacker/settings.c | 4 +++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h index c74d741f692b..1485bfe86aa7 100644 --- a/ProcessHacker/include/phsettings.h +++ b/ProcessHacker/include/phsettings.h @@ -37,6 +37,8 @@ EXT ULONG PhCsUseColorWow64Processes; EXT ULONG PhCsColorWow64Processes; EXT ULONG PhCsUseColorDebuggedProcesses; EXT ULONG PhCsColorDebuggedProcesses; +EXT ULONG PhCsUseColorHandleProtected; +EXT ULONG PhCsColorHandleProtected; EXT ULONG PhCsUseColorElevatedProcesses; EXT ULONG PhCsColorElevatedProcesses; EXT ULONG PhCsUseColorPicoProcesses; diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 7ee4e9f3711a..2749bdf1d365 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -164,7 +164,7 @@ typedef struct _PH_PROCESS_ITEM ULONG IsImmersive : 1; ULONG IsWow64Valid : 1; ULONG IsPartiallySuspended : 1; - ULONG Unused : 1; + ULONG IsProtectedHandle : 1; ULONG IsProtectedProcess : 1; ULONG IsSecureProcess : 1; ULONG IsSubsystemProcess : 1; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 29b54475c6dd..4a4be997cad6 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1344,11 +1344,52 @@ VOID PhpFillProcessItem( PhPrintUInt32(ProcessItem->SessionIdString, ProcessItem->SessionId); // Open a handle to the process for later usage. + + if ( + ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && + ProcessItem->ProcessId != DPCS_PROCESS_ID && + ProcessItem->ProcessId != INTERRUPTS_PROCESS_ID + ) { - PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId); + PhOpenProcess( + &ProcessItem->QueryHandle, + PROCESS_QUERY_INFORMATION, + ProcessItem->ProcessId + ); if (!ProcessItem->QueryHandle) - PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessItem->ProcessId); + { + PhOpenProcess( + &ProcessItem->QueryHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + ProcessItem->ProcessId + ); + } + else + { + OBJECT_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetHandleInformationEx( + NtCurrentProcess(), + ProcessItem->QueryHandle, + -1, + 0, + NULL, + &basicInfo, + NULL, + NULL, + NULL, + NULL + ))) + { + if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) + ProcessItem->IsProtectedHandle = TRUE; + } + else + { + ProcessItem->IsProtectedHandle = TRUE; + } + } } // Process flags diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 6bb97961da4b..895fa96758c4 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2844,6 +2844,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( getNodeColor->BackColor = PhCsColorDebuggedProcesses; else if (PhCsUseColorSuspended && processItem->IsSuspended) getNodeColor->BackColor = PhCsColorSuspended; + else if (PhCsUseColorHandleProtected && processItem->IsProtectedHandle) + getNodeColor->BackColor = PhCsColorHandleProtected; else if (PhCsUseColorElevatedProcesses && processItem->IsElevated) getNodeColor->BackColor = PhCsColorElevatedProcesses; else if (PhCsUseColorPicoProcesses && processItem->IsSubsystemProcess) diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index 319d88ebd3f1..e4bba431e458 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -188,6 +188,8 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"ColorWow64Processes", L"8f8fbc"); // Rosy Brown PhpAddIntegerSetting(L"UseColorDebuggedProcesses", L"1"); PhpAddIntegerSetting(L"ColorDebuggedProcesses", L"ffbbcc"); + PhpAddIntegerSetting(L"UseColorHandleProtected", L"1"); + PhpAddIntegerSetting(L"ColorHandleProtected", L"000000"); // Black PhpAddIntegerSetting(L"UseColorElevatedProcesses", L"1"); PhpAddIntegerSetting(L"ColorElevatedProcesses", L"00aaff"); PhpAddIntegerSetting(L"UseColorPicoProcesses", L"1"); @@ -250,6 +252,8 @@ VOID PhUpdateCachedSettings( PH_UPDATE_SETTING(ColorWow64Processes); PH_UPDATE_SETTING(UseColorDebuggedProcesses); PH_UPDATE_SETTING(ColorDebuggedProcesses); + PH_UPDATE_SETTING(UseColorHandleProtected); + PH_UPDATE_SETTING(ColorHandleProtected); PH_UPDATE_SETTING(UseColorElevatedProcesses); PH_UPDATE_SETTING(ColorElevatedProcesses); PH_UPDATE_SETTING(UseColorPicoProcesses); From bc4b87c67f4ac0a3fd1ec7ba96b17503f9fdb8f3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 02:52:45 +1100 Subject: [PATCH 745/839] revert 6a02c5b0 Update window procedure callbacks --- phlib/extlv.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/phlib/extlv.c b/phlib/extlv.c index 4ab2b593390c..0e210ed59863 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -38,6 +38,7 @@ typedef struct _PH_EXTLV_CONTEXT { HWND Handle; + WNDPROC OldWndProc; PVOID Context; // Sorting @@ -66,9 +67,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ); INT PhpExtendedListViewCompareFunc( @@ -114,6 +113,7 @@ VOID PhSetExtendedListView( context = PhAllocate(sizeof(PH_EXTLV_CONTEXT)); context->Handle = hWnd; + context->OldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); context->Context = NULL; context->TriState = FALSE; context->SortColumn = 0; @@ -122,14 +122,13 @@ VOID PhSetExtendedListView( 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); + PhSetWindowContext(hWnd, 5, context); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PhpExtendedListViewWndProc); ExtendedListView_Init(hWnd); } @@ -138,19 +137,17 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { - PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)dwRefData; + PPH_EXTLV_CONTEXT context = PhGetWindowContext(hwnd, 5); switch (uMsg) { case WM_DESTROY: { - RemoveWindowSubclass(hwnd, PhpExtendedListViewWndProc, uIdSubclass); - + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)context->OldWndProc); + PhRemoveWindowContext(hwnd, 5); PhFree(context); } break; @@ -164,7 +161,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( { HWND headerHandle; - headerHandle = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0); + headerHandle = (HWND)CallWindowProc(context->OldWndProc, hwnd, LVM_GETHEADER, 0, 0); if (header->hwndFrom == headerHandle) { @@ -352,7 +349,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( { if (i != column) { - if (SendMessage(hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) + if (CallWindowProc(context->OldWndProc, hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) { availableWidth -= lvColumn.cx; } @@ -366,10 +363,10 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( } if (availableWidth >= 40) - return SendMessage(hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); + return CallWindowProc(context->OldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); } - return SendMessage(hwnd, LVM_SETCOLUMNWIDTH, column, width); + return CallWindowProc(context->OldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, width); } break; case ELVM_SETCOMPAREFUNCTION: @@ -471,7 +468,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( return TRUE; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return CallWindowProc(context->OldWndProc, hwnd, uMsg, wParam, lParam); } /** @@ -549,9 +546,9 @@ static INT PhpExtendedListViewCompareFunc( yItem.iItem = y; yItem.iSubItem = 0; - if (!SendMessage(context->Handle, LVM_GETITEM, 0, (LPARAM)&xItem)) + if (!CallWindowProc(context->OldWndProc, context->Handle, LVM_GETITEM, 0, (LPARAM)&xItem)) return 0; - if (!SendMessage(context->Handle, LVM_GETITEM, 0, (LPARAM)&yItem)) + if (!CallWindowProc(context->OldWndProc, context->Handle, LVM_GETITEM, 0, (LPARAM)&yItem)) return 0; // First, do tri-state sorting. @@ -713,7 +710,7 @@ static INT PhpDefaultCompareListViewItems( item.cchTextMax = 260; xText[0] = 0; - SendMessage(Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); + CallWindowProc(Context->OldWndProc, Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); // Get the Y item text. @@ -722,7 +719,7 @@ static INT PhpDefaultCompareListViewItems( item.cchTextMax = 260; yText[0] = 0; - SendMessage(Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); + CallWindowProc(Context->OldWndProc, Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); // Compare them. From bef059f4dd1795ee804f1594722424b94e698dea Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 02:56:29 +1100 Subject: [PATCH 746/839] Add CallWindowProc wrappers --- phlib/guisup.c | 90 ++++++++++++++++++++++++++++++++++++++++++ phlib/include/guisup.h | 39 ++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/phlib/guisup.c b/phlib/guisup.c index 0512215a6bb1..b5e0001a1d57 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1309,3 +1309,93 @@ VOID PhRemoveWindowContext( PhRemoveEntryHashtable(WindowContextHashTable, &lookupEntry); PhReleaseQueuedLockExclusive(&WindowContextListLock); } + +static LRESULT CALLBACK PhWindowSubclassCallback( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_WINDOW_SUBCLASS context; + PPH_WINDOW_SUBCLASS_ENTRY entry; + ULONG i; + + if (!(context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT))) + return 0; + + for (i = 0; i < context->WindowCallbackArray.Count; i++) + { + entry = PhItemArray(&context->WindowCallbackArray, i); + + if (entry->SubclassCallback(WindowHandle, uMsg, wParam, lParam, entry->Context)) + { + return DefWindowProc(WindowHandle, uMsg, wParam, lParam); + } + } + + return CallWindowProc(context->DefaultWindowProc, WindowHandle, uMsg, wParam, lParam); +} + +VOID PhRegisterWindowSubclass( + _In_ HWND WindowHandle, + _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback, + _In_opt_ PVOID Context + ) +{ + PPH_WINDOW_SUBCLASS context; + PH_WINDOW_SUBCLASS_ENTRY entry; + + entry.SubclassCallback = SubclassCallback; + entry.Context = Context; + + if (context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT)) + { + PhAddItemArray(&context->WindowCallbackArray, &entry); + } + else + { + context = PhAllocate(sizeof(PH_WINDOW_SUBCLASS)); + memset(context, 0, sizeof(PH_WINDOW_SUBCLASS)); + + PhInitializeArray(&context->WindowCallbackArray, sizeof(PH_WINDOW_SUBCLASS_ENTRY), 1); + PhAddItemArray(&context->WindowCallbackArray, &entry); + + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); + + PhSetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT, context); + + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhWindowSubclassCallback); + } +} + +VOID PhUnregisterWindowSubclass( + _In_ HWND WindowHandle, + _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback + ) +{ + PPH_WINDOW_SUBCLASS context; + PPH_WINDOW_SUBCLASS_ENTRY entry; + ULONG i; + + if (!(context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT))) + return; + + for (i = 0; i < context->WindowCallbackArray.Count; i++) + { + entry = PhItemArray(&context->WindowCallbackArray, i); + + if (entry->SubclassCallback == SubclassCallback) + PhRemoveItemArray(&context->WindowCallbackArray, i); + } + + if (context->WindowCallbackArray.Count == 0) + { + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + + PhRemoveWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT); + + PhDeleteArray(&context->WindowCallbackArray); + PhFree(context); + } +} diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index b999a4e38953..832df97fc2b4 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -481,6 +481,45 @@ PhRemoveWindowContext( _In_ ULONG PropertyHash ); +typedef BOOLEAN (NTAPI *PPH_WINDOW_SUBCLASS_CALLBACK)( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ PVOID Context + ); + +typedef struct _PH_WINDOW_SUBCLASS_ENTRY +{ + PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback; + PVOID Context; +} PH_WINDOW_SUBCLASS_ENTRY, *PPH_WINDOW_SUBCLASS_ENTRY; + +typedef struct _PH_WINDOW_SUBCLASS +{ + WNDPROC DefaultWindowProc; + PH_ARRAY WindowCallbackArray; +} PH_WINDOW_SUBCLASS, *PPH_WINDOW_SUBCLASS; + +#define PH_WINDOW_SUBCLASS_CONTEXT 0xFFFFFF + +PHLIBAPI +VOID +NTAPI +PhRegisterWindowSubclass( + _In_ HWND WindowHandle, + _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassWindowProc, + _In_opt_ PVOID Context + ); + +PHLIBAPI +VOID +NTAPI +PhUnregisterWindowSubclass( + _In_ HWND WindowHandle, + _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassWindowProc + ); + FORCEINLINE VOID PhResizingMinimumSize( _Inout_ PRECT Rect, _In_ WPARAM Edge, From 08917fa0ac46ccc2ac877abd88924102c4ba8ede Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 02:57:16 +1100 Subject: [PATCH 747/839] Fix PhStringRefToUnicodeString MaximumLength --- phlib/include/phbasesup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index 9b2c871b6ae2..d487e6c56e0c 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -826,7 +826,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; From d5b4ad224df525ab57e3d3adc92262897e7e892e Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 03:02:34 +1100 Subject: [PATCH 748/839] Remove PhEnableServiceQueryStage2 --- ProcessHacker/include/phplug.h | 1 + ProcessHacker/include/phsettings.h | 1 - ProcessHacker/mainwnd.c | 1 - ProcessHacker/proctree.c | 9 ++++++--- ProcessHacker/settings.c | 1 - ProcessHacker/srvlist.c | 5 +++-- ProcessHacker/srvprv.c | 2 +- phlib/native.c | 10 ++++------ 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index a4a4c36d44b9..02cd6cfb99b8 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -68,6 +68,7 @@ typedef struct _PH_PLUGIN_GET_HIGHLIGHTING_COLOR PVOID Parameter; COLORREF BackColor; + COLORREF ForeColor; BOOLEAN Handled; BOOLEAN Cache; } PH_PLUGIN_GET_HIGHLIGHTING_COLOR, *PPH_PLUGIN_GET_HIGHLIGHTING_COLOR; diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h index 1485bfe86aa7..1081d65bfac0 100644 --- a/ProcessHacker/include/phsettings.h +++ b/ProcessHacker/include/phsettings.h @@ -12,7 +12,6 @@ #endif EXT BOOLEAN PhEnableProcessQueryStage2; -EXT BOOLEAN PhEnableServiceQueryStage2; EXT ULONG PhCsCollapseServicesOnStart; EXT ULONG PhCsForceNoParent; diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 0d0167014eb4..4dda48ddfb67 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -2172,7 +2172,6 @@ VOID PhMwpLoadSettings( PhEnableNetworkProviderResolve = !!PhGetIntegerSetting(L"EnableNetworkResolve"); PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); - PhEnableServiceQueryStage2 = !!PhGetIntegerSetting(L"EnableServiceStage2"); PhNfLoadStage1(); PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 895fa96758c4..f0c32570aff7 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -2829,7 +2829,10 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( if (getHighlightingColor.Handled) { getNodeColor->BackColor = getHighlightingColor.BackColor; - getNodeColor->Flags = TN_AUTO_FORECOLOR; + getNodeColor->ForeColor = getHighlightingColor.ForeColor; + + if (!getNodeColor->ForeColor) + getNodeColor->Flags |= TN_AUTO_FORECOLOR; if (getHighlightingColor.Cache) getNodeColor->Flags |= TN_CACHE; @@ -2838,6 +2841,8 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( } } + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + if (!processItem) ; // Dummy else if (PhCsUseColorDebuggedProcesses && processItem->IsBeingDebugged) @@ -2875,8 +2880,6 @@ BOOLEAN NTAPI PhpProcessTreeNewCallback( PhEqualString(processItem->UserName, PhCurrentUserName, TRUE) ) getNodeColor->BackColor = PhCsColorOwnProcesses; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; } return TRUE; case TreeNewGetNodeIcon: diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index e4bba431e458..eb4d2ab41396 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -48,7 +48,6 @@ VOID PhAddDefaultSettings( PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); PhpAddIntegerSetting(L"EnablePlugins", L"1"); PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); - PhpAddIntegerSetting(L"EnableServiceStage2", L"0"); PhpAddIntegerSetting(L"EnableStage2", L"1"); PhpAddIntegerSetting(L"EnableWarnings", L"1"); PhpAddIntegerSetting(L"EnableWindowText", L"1"); diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index ee2cdc4ef3e6..1359c062d164 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -820,15 +820,16 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( if (!serviceItem) ; // Dummy - else if (PhEnableServiceQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) + else if (PhEnableProcessQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) { - getNodeColor->Flags = TN_AUTO_FORECOLOR; getNodeColor->BackColor = PhCsColorUnknown; } else if (PhCsUseColorServiceStop && serviceItem->StartType == SERVICE_DISABLED) { getNodeColor->ForeColor = PhCsColorServiceStop; } + + getNodeColor->Flags = TN_AUTO_FORECOLOR; } return TRUE; case TreeNewGetCellTooltip: diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index b6738b57cdc7..69912965dd0e 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -616,7 +616,7 @@ VOID PhpQueueServiceQueryStage2( { PH_WORK_QUEUE_ENVIRONMENT environment; - if (!PhEnableServiceQueryStage2) + if (!PhEnableProcessQueryStage2) return; PhReferenceObject(ServiceItem); diff --git a/phlib/native.c b/phlib/native.c index ac6fa54a6025..e5d80619004c 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -5070,7 +5070,6 @@ static BOOLEAN EnumGenericProcessModulesCallback( { PENUM_GENERIC_PROCESS_MODULES_CONTEXT context; PH_MODULE_INFO moduleInfo; - PPH_STRING fileName; BOOLEAN cont; context = (PENUM_GENERIC_PROCESS_MODULES_CONTEXT)Context; @@ -5085,19 +5084,18 @@ static BOOLEAN EnumGenericProcessModulesCallback( 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.OriginalFileName = fileName; 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.LoadReason = (USHORT)Module->LoadReason; From 62ff94b057b44b822ce9ddd481a829ee9011fe11 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 03:35:04 +1100 Subject: [PATCH 749/839] Fix provider handle check --- ProcessHacker/include/procprv.h | 2 +- ProcessHacker/procprv.c | 72 +++++++++++++++------------------ 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 2749bdf1d365..0405382525f6 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -158,7 +158,7 @@ typedef struct _PH_PROCESS_ITEM ULONG IsInJob : 1; ULONG IsInSignificantJob : 1; ULONG IsPacked : 1; - ULONG Reserved : 1; + ULONG IsValidHandle : 1; ULONG IsSuspended : 1; ULONG IsWow64 : 1; ULONG IsImmersive : 1; diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 4a4be997cad6..638e8edc68dd 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -107,8 +107,8 @@ typedef struct _PH_PROCESS_QUERY_S1_DATA ULONG IsInSignificantJob : 1; ULONG IsBeingDebugged : 1; ULONG IsImmersive : 1; - - ULONG Spare : 26; + ULONG IsProtectedHandle : 1; + ULONG Spare : 25; }; }; } PH_PROCESS_QUERY_S1_DATA, *PPH_PROCESS_QUERY_S1_DATA; @@ -1155,6 +1155,32 @@ VOID PhpProcessQueryStage1( { Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } + + if (processHandleLimited && processItem->IsValidHandle) + { + OBJECT_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetHandleInformationEx( + NtCurrentProcess(), + processHandleLimited, + -1, + 0, + NULL, + &basicInfo, + NULL, + NULL, + NULL, + NULL + ))) + { + if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) + Data->IsProtectedHandle = TRUE; + } + else + { + Data->IsProtectedHandle = TRUE; + } + } } VOID PhpProcessQueryStage2( @@ -1290,6 +1316,7 @@ VOID PhpFillProcessItemStage1( processItem->IsInSignificantJob = Data->IsInSignificantJob; processItem->IsBeingDebugged = Data->IsBeingDebugged; processItem->IsImmersive = Data->IsImmersive; + processItem->IsProtectedHandle = Data->IsProtectedHandle; PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); @@ -1344,51 +1371,16 @@ VOID PhpFillProcessItem( PhPrintUInt32(ProcessItem->SessionIdString, ProcessItem->SessionId); // Open a handle to the process for later usage. - - if ( - ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && - ProcessItem->ProcessId != DPCS_PROCESS_ID && - ProcessItem->ProcessId != INTERRUPTS_PROCESS_ID - ) + if (PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId)) { - PhOpenProcess( &ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId - ); + )); if (!ProcessItem->QueryHandle) { - PhOpenProcess( - &ProcessItem->QueryHandle, - PROCESS_QUERY_LIMITED_INFORMATION, - ProcessItem->ProcessId - ); - } - else - { - OBJECT_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetHandleInformationEx( - NtCurrentProcess(), - ProcessItem->QueryHandle, - -1, - 0, - NULL, - &basicInfo, - NULL, - NULL, - NULL, - NULL - ))) - { - if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) - ProcessItem->IsProtectedHandle = TRUE; - } - else - { - ProcessItem->IsProtectedHandle = TRUE; - } + PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessItem->ProcessId); } } From 2cc6d3d773f27a7f37c06d66c7a2559b703b9ea4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 03:36:52 +1100 Subject: [PATCH 750/839] Add missing file from pevious commit --- ProcessHacker/procprv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 638e8edc68dd..adae0dbd01f5 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1373,6 +1373,7 @@ VOID PhpFillProcessItem( // Open a handle to the process for later usage. if (PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId)) { + ProcessItem->IsValidHandle = NT_SUCCESS(PhOpenProcess( &ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId From 704c25606421796d4e54f4430fe7b185b14b1e57 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 03:55:41 +1100 Subject: [PATCH 751/839] Update LDR_DATA_TABLE_ENTRY types --- phnt/include/ntldr.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index d03cd284e4e0..8f8fea39570d 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -5,6 +5,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 { @@ -98,6 +104,7 @@ typedef enum _LDR_DLL_LOAD_REASON #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 sizeof(LDR_DATA_TABLE_ENTRY) // symbols typedef struct _LDR_DATA_TABLE_ENTRY @@ -110,7 +117,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; @@ -172,12 +179,6 @@ 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 - ); - NTSYSAPI NTSTATUS NTAPI From ef67afdcf439c1fa47b8e6889524d49a7e46b3bb Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 03:56:24 +1100 Subject: [PATCH 752/839] Update PEB types --- phnt/include/ntpebteb.h | 42 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 7114a1bb7bb9..e6f0a82e372e 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -14,6 +14,46 @@ typedef struct _ACTIVATION_CONTEXT_STACK 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 { @@ -68,7 +108,7 @@ typedef struct _PEB }; ULONG SystemReserved[1]; ULONG AtlThunkSListPtr32; - PVOID ApiSetMap; + PAPI_SET_NAMESPACE ApiSetMap; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[2]; From 70d8be3244f0884e844dd3dd5a192df8b01258a7 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 04:31:41 +1100 Subject: [PATCH 753/839] Update window subclass callbacks --- ProcessHacker/ProcessHacker.def | 2 ++ ProcessHacker/include/miniinfop.h | 4 +--- ProcessHacker/include/procprpp.h | 5 ++-- ProcessHacker/include/sysinfop.h | 10 ++++---- ProcessHacker/memsrch.c | 13 +++++----- ProcessHacker/miniinfo.c | 36 ++++++++++++++++------------ ProcessHacker/procprp.c | 15 ++++++------ ProcessHacker/searchbox.c | 27 ++++++++++++--------- ProcessHacker/splitter.c | 14 +++++------ ProcessHacker/sysinfo.c | 28 ++++++++++------------ phlib/include/treenewp.h | 6 ++--- phlib/treenew.c | 18 +++++++------- plugins/HardwareDevices/disknotify.c | 11 ++++----- plugins/OnlineChecks/upload.c | 18 ++++++-------- plugins/ToolStatus/main.c | 11 ++++----- plugins/Updater/updater.c | 15 ++++++------ plugins/WindowExplorer/wndprp.c | 26 +++++++++----------- 17 files changed, 123 insertions(+), 136 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 45755ab3a0bd..eafb53d70c24 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -512,6 +512,8 @@ EXPORTS PhGetWindowContext PhSetWindowContext PhRemoveWindowContext + PhRegisterWindowSubclass + PhUnregisterWindowSubclass ; hndlinfo PhEnumObjectTypes diff --git a/ProcessHacker/include/miniinfop.h b/ProcessHacker/include/miniinfop.h index a9c36442e4dc..00e81b5eca46 100644 --- a/ProcessHacker/include/miniinfop.h +++ b/ProcessHacker/include/miniinfop.h @@ -215,9 +215,7 @@ LRESULT CALLBACK PhMipSectionControlHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ); // List-based section diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index f1878dead5a4..061aa3892692 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -29,13 +29,12 @@ PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( _In_ HWND hwnd ); -LRESULT CALLBACK PhpPropSheetWndProc( +BOOLEAN CALLBACK PhpPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ); VOID NTAPI PhpProcessPropPageContextDeleteProcedure( diff --git a/ProcessHacker/include/sysinfop.h b/ProcessHacker/include/sysinfop.h index 2428889df949..87ed3b71b470 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -189,22 +189,20 @@ VOID PhSipCreateSectionDialog( _In_ PPH_SYSINFO_SECTION Section ); -LRESULT CALLBACK PhSipGraphHookWndProc( +BOOLEAN CALLBACK PhSipGraphHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ); -LRESULT CALLBACK PhSipPanelHookWndProc( +BOOLEAN CALLBACK PhSipPanelHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ); // Misc. diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index fe66dd890010..78e4e0e62b91 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -650,21 +650,20 @@ NTSTATUS PhpMemoryStringThreadStart( return STATUS_SUCCESS; } -LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( +BOOLEAN CALLBACK PhpMemoryStringTaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)dwRefData; + PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)Context; switch (uMsg) { case WM_NCDESTROY: - RemoveWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, uIdSubclass); + PhUnregisterWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc); break; case WM_PH_MEMORY_STATUS_UPDATE: { @@ -681,7 +680,7 @@ LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return FALSE; } HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( @@ -715,7 +714,7 @@ HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); // Subclass the Taskdialog. - SetWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, 0, (ULONG_PTR)context); + PhRegisterWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, context); // Create the search thread. PhCreateThread2(PhpMemoryStringThreadStart, context); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 599135aba062..a3be5d07940e 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -452,6 +452,7 @@ VOID PhMipOnInitDialog( { HICON cog; HICON pin; + WNDPROC oldWndProc; cog = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); @@ -460,18 +461,17 @@ VOID PhMipOnInitDialog( 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); - - SetWindowSubclass(GetDlgItem(PhMipWindow, IDC_SECTION), PhMipSectionControlHookWndProc, 0, 0); + 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), 10, oldWndProc); + SetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC, (LONG_PTR)PhMipSectionControlHookWndProc); } VOID PhMipOnShowWindow( @@ -1154,15 +1154,21 @@ LRESULT CALLBACK PhMipSectionControlHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { + WNDPROC oldWndProc; + + if (!(oldWndProc = PhGetWindowContext(hwnd, 10))) + return 0; + switch (uMsg) { case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhMipSectionControlHookWndProc, uIdSubclass); + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 10); + } break; case WM_SETCURSOR: { @@ -1171,7 +1177,7 @@ LRESULT CALLBACK PhMipSectionControlHookWndProc( return TRUE; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } PPH_MINIINFO_LIST_SECTION PhMipCreateListSection( diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 593cb33b8114..fec4120ed7fb 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -168,7 +168,7 @@ INT CALLBACK PhpPropSheetProc( PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, propSheetContext); - SetWindowSubclass(hwndDlg, PhpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); + PhRegisterWindowSubclass(hwndDlg, PhpPropSheetWndProc, propSheetContext); if (MinimumSize.left == -1) { @@ -196,16 +196,15 @@ PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( return PhGetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); } -LRESULT CALLBACK PhpPropSheetWndProc( +BOOLEAN CALLBACK PhpPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam, + _In_ PVOID Context ) { - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext = (PPH_PROCESS_PROPSHEETCONTEXT)dwRefData; + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext = (PPH_PROCESS_PROPSHEETCONTEXT)Context; switch (uMsg) { @@ -235,7 +234,7 @@ LRESULT CALLBACK PhpPropSheetWndProc( break; case WM_NCDESTROY: { - RemoveWindowSubclass(hwnd, PhpPropSheetWndProc, uIdSubclass); + PhUnregisterWindowSubclass(hwnd, PhpPropSheetWndProc); PhRemoveWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); PhDeleteLayoutManager(&propSheetContext->LayoutManager); @@ -269,7 +268,7 @@ LRESULT CALLBACK PhpPropSheetWndProc( break; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return FALSE; } BOOLEAN PhpInitializePropSheetLayoutStage1( diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index b6ec86880b23..080bd02e5484 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -48,6 +48,7 @@ typedef struct _EDIT_CONTEXT INT ImageWidth; INT ImageHeight; HWND WindowHandle; + WNDPROC DefaultWindowProc; HFONT WindowFont; HICON BitmapActive; HICON BitmapInactive; @@ -249,12 +250,13 @@ 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 = (PEDIT_CONTEXT)dwRefData; + PEDIT_CONTEXT context; + + if (!(context = PhGetWindowContext(hWnd, 10))) + return 0; switch (uMsg) { @@ -265,7 +267,8 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( if (context->WindowFont) DeleteObject(context->WindowFont); - RemoveWindowSubclass(hWnd, PhpSearchWndSubclassProc, uIdSubclass); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + PhRemoveWindowContext(hWnd, 10); PhFree(context); } break; @@ -276,7 +279,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( LPNCCALCSIZE_PARAMS ncCalcSize = (NCCALCSIZE_PARAMS*)lParam; // Let Windows handle the non-client defaults. - DefSubclassProc(hWnd, uMsg, wParam, lParam); + CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); // Deflate the client area to accommodate the custom button. ncCalcSize->rgrc[0].right -= context->CXWidth; @@ -287,7 +290,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( RECT windowRect; // Let Windows handle the non-client defaults. - DefSubclassProc(hWnd, uMsg, wParam, lParam); + CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); // Get the screen coordinates of the window. GetWindowRect(hWnd, &windowRect); @@ -479,7 +482,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); } HICON PhpSearchBitmapToIcon( @@ -520,19 +523,21 @@ VOID PhCreateSearchControl( memset(context, 0, sizeof(EDIT_CONTEXT)); context->WindowHandle = WindowHandle; + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); //PhpSearchInitializeTheme(context); PhpSearchInitializeImages(context); // Set initial text if (BannerText) - Edit_SetCueBannerText(context->WindowHandle, BannerText); + Edit_SetCueBannerText(WindowHandle, BannerText); // Subclass the Edit control window procedure. - SetWindowSubclass(context->WindowHandle, PhpSearchWndSubclassProc, 0, (ULONG_PTR)context); + PhSetWindowContext(WindowHandle, 10, context); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpSearchWndSubclassProc); // Initialize the theme parameters. - SendMessage(context->WindowHandle, WM_THEMECHANGED, 0, 0); + SendMessage(WindowHandle, WM_THEMECHANGED, 0, 0); } HBITMAP PhLoadPngImageFromResource( diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index df6aa2baed33..1dcb8dba9783 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -264,23 +264,21 @@ LRESULT CALLBACK HSplitterWindowProc( return DefWindowProc(hwnd, uMsg, wParam, lParam); } -LRESULT CALLBACK HSplitterParentWindowProc( +BOOLEAN CALLBACK HSplitterParentWindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PPH_HSPLITTER_CONTEXT context = (PPH_HSPLITTER_CONTEXT)dwRefData; + PPH_HSPLITTER_CONTEXT context = (PPH_HSPLITTER_CONTEXT)Context; switch (uMsg) { case WM_DESTROY: { - RemoveWindowSubclass(hwnd, HSplitterParentWindowProc, uIdSubclass); - + PhUnregisterWindowSubclass(hwnd, HSplitterParentWindowProc); PhDeleteHSplitter(context); } break; @@ -291,7 +289,7 @@ LRESULT CALLBACK HSplitterParentWindowProc( break; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return FALSE; } VOID PhInitializeHSplitter( @@ -355,7 +353,7 @@ VOID PhInitializeHSplitter( ShowWindow(context->Window, SW_SHOW); UpdateWindow(context->Window); - SetWindowSubclass(ParentWindow, HSplitterParentWindowProc, 0, (ULONG_PTR)context); + PhRegisterWindowSubclass(ParentWindow, HSplitterParentWindowProc, context); } VOID PhDeleteHSplitter( diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 509a4b75a5ec..c313c7fc812e 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -417,7 +417,7 @@ VOID PhSipOnShowWindow( NULL ); - SetWindowSubclass(RestoreSummaryControl, PhSipPanelHookWndProc, 0, 0); + PhRegisterWindowSubclass(RestoreSummaryControl, PhSipPanelHookWndProc, NULL); RestoreSummaryControlHot = FALSE; EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); @@ -1155,8 +1155,8 @@ PPH_SYSINFO_SECTION PhSipCreateSection( NULL ); - SetWindowSubclass(section->GraphHandle, PhSipGraphHookWndProc, 0, (ULONG_PTR)section); - SetWindowSubclass(section->PanelHandle, PhSipPanelHookWndProc, 0, (ULONG_PTR)section); + PhRegisterWindowSubclass(section->GraphHandle, PhSipGraphHookWndProc, section); + PhRegisterWindowSubclass(section->PanelHandle, PhSipPanelHookWndProc, section); PhAddItemList(SectionList, section); @@ -1715,21 +1715,20 @@ VOID PhSipCreateSectionDialog( } } -LRESULT CALLBACK PhSipGraphHookWndProc( +BOOLEAN CALLBACK PhSipGraphHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)dwRefData; + PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)Context; switch (uMsg) { case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhSipGraphHookWndProc, uIdSubclass); + PhUnregisterWindowSubclass(hwnd, PhSipGraphHookWndProc); break; case WM_SETFOCUS: section->HasFocus = TRUE; @@ -1865,24 +1864,23 @@ LRESULT CALLBACK PhSipGraphHookWndProc( break; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return FALSE; } -LRESULT CALLBACK PhSipPanelHookWndProc( +BOOLEAN CALLBACK PhSipPanelHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)dwRefData; + PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)Context; switch (uMsg) { case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhSipPanelHookWndProc, uIdSubclass); + PhUnregisterWindowSubclass(hwnd, PhSipPanelHookWndProc); break; case WM_SETFOCUS: { @@ -1986,7 +1984,7 @@ LRESULT CALLBACK PhSipPanelHookWndProc( break; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return FALSE; } VOID PhSipUpdateThemeData( diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index 21fcfb9b95b1..fc0dc1974680 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -730,14 +730,12 @@ VOID PhTnpGetHeaderTooltipText( _Out_ PWSTR *Text ); - -LRESULT CALLBACK PhTnpHeaderHookWndProc( +BOOLEAN CALLBACK PhTnpHeaderHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ); // Drag selection diff --git a/phlib/treenew.c b/phlib/treenew.c index 492739319042..1330fc93376d 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -5845,10 +5845,9 @@ VOID PhTnpInitializeTooltips( 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); + // Hook the header control window procedures so we can forward mouse messages to the tooltip control. + PhRegisterWindowSubclass(Context->FixedHeaderHandle, PhTnpHeaderHookWndProc, Context); + PhRegisterWindowSubclass(Context->HeaderHandle, PhTnpHeaderHookWndProc, Context); SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); // no limit SendMessage(Context->TooltipsHandle, WM_SETFONT, (WPARAM)Context->Font, FALSE); @@ -6135,21 +6134,20 @@ VOID PhTnpGetHeaderTooltipText( SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH); } -LRESULT CALLBACK PhTnpHeaderHookWndProc( +BOOLEAN CALLBACK PhTnpHeaderHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PPH_TREENEW_CONTEXT context = (PPH_TREENEW_CONTEXT)dwRefData; + PPH_TREENEW_CONTEXT context = (PPH_TREENEW_CONTEXT)Context; switch (uMsg) { case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhTnpHeaderHookWndProc, uIdSubclass); + PhUnregisterWindowSubclass(hwnd, PhTnpHeaderHookWndProc); break; case WM_MOUSEMOVE: { @@ -6235,7 +6233,7 @@ LRESULT CALLBACK PhTnpHeaderHookWndProc( break; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + return FALSE; } BOOLEAN PhTnpDetectDrag( diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index f4025a6cc345..c227704b1e8a 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -25,13 +25,12 @@ static BOOLEAN SubclassActive = FALSE; -LRESULT CALLBACK MainWndDevicesSubclassProc( +BOOLEAN CALLBACK MainWndDevicesSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { // Subclassing the main window just to process drive letter notifications @@ -81,7 +80,7 @@ LRESULT CALLBACK MainWndDevicesSubclassProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return FALSE; } VOID AddRemoveDeviceChangeCallback( @@ -98,7 +97,7 @@ VOID AddRemoveDeviceChangeCallback( if (!SubclassActive) { // We have a disk device, subclass the main window to detect drive letter changes. - SetWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0, 0); + PhRegisterWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, NULL); SubclassActive = TRUE; } } @@ -107,7 +106,7 @@ VOID AddRemoveDeviceChangeCallback( if (SubclassActive) { // The user has removed the last disk device, remove the subclass. - RemoveWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0); + PhUnregisterWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc); SubclassActive = FALSE; } } diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index fabb0a4353bd..97d871d9c094 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1169,25 +1169,22 @@ NTSTATUS UploadRecheckThreadStart( return STATUS_SUCCESS; } - -LRESULT CALLBACK TaskDialogSubclassProc( +BOOLEAN CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Context; switch (uMsg) { - case WM_NCDESTROY: + case WM_DESTROY: { TaskDialogFreeContext(context); - - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); + PhUnregisterWindowSubclass(hwndDlg, TaskDialogSubclassProc); } break; case UM_UPLOAD: @@ -1243,7 +1240,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return FALSE; } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -1277,8 +1274,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( } } - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); + PhRegisterWindowSubclass(hwndDlg, TaskDialogSubclassProc, context); ShowVirusTotalUploadDialog(context); } diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 9ca760f3e568..6e653d207597 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -620,13 +620,12 @@ VOID DrawWindowBorderForTargeting( } } -LRESULT CALLBACK MainWndSubclassProc( +BOOLEAN CALLBACK MainWndSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { switch (uMsg) @@ -1306,10 +1305,10 @@ LRESULT CALLBACK MainWndSubclassProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return FALSE; DefaultWndProc: - return DefWindowProc(hWnd, uMsg, wParam, lParam); + return TRUE; } VOID NTAPI MainWindowShowingCallback( @@ -1324,7 +1323,7 @@ VOID NTAPI MainWindowShowingCallback( NULL, &LayoutPaddingCallbackRegistration ); - SetWindowSubclass(PhMainWndHandle, MainWndSubclassProc, 0, 0); + PhRegisterWindowSubclass(PhMainWndHandle, MainWndSubclassProc, NULL); ToolbarLoadSettings(); ReBarLoadLayoutSettings(); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 6e593160c14f..040435c7a5ee 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -755,16 +755,15 @@ NTSTATUS UpdateDownloadThread( return STATUS_SUCCESS; } -LRESULT CALLBACK TaskDialogSubclassProc( +BOOLEAN NTAPI TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ PVOID Context ) { - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; + PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Context; switch (uMsg) { @@ -778,9 +777,9 @@ LRESULT CALLBACK TaskDialogSubclassProc( SetForegroundWindow(hwndDlg); } break; - case WM_NCDESTROY: + case WM_DESTROY: { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); + PhUnregisterWindowSubclass(hwndDlg, TaskDialogSubclassProc); } break; //case WM_PARENTNOTIFY: @@ -825,7 +824,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( // break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return FALSE; } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -851,7 +850,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( TaskDialogCreateIcons(context); // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); + PhRegisterWindowSubclass(hwndDlg, TaskDialogSubclassProc, context); if (context->StartupCheck) ShowAvailableDialog(context); diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 98360506b553..865fb60ba65c 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -94,11 +94,12 @@ INT CALLBACK WepPropSheetProc( _In_ LPARAM lParam ); -LRESULT CALLBACK WepPropSheetWndProc( +BOOLEAN CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam + _In_ LPARAM lParam, + _In_ PVOID Context ); HPROPSHEETPAGE WepCommonCreatePage( @@ -357,12 +358,9 @@ static INT CALLBACK WepPropSheetProc( { case PSCB_INITIALIZED: { - WNDPROC oldWndProc; HWND refreshButtonHandle; - oldWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); - SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc); - PhSetWindowContext(hwndDlg, 1, (HANDLE)oldWndProc); + PhRegisterWindowSubclass(hwndDlg, WepPropSheetWndProc, NULL); // Hide the Cancel button. ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); @@ -379,27 +377,24 @@ static INT CALLBACK WepPropSheetProc( return 0; } -LRESULT CALLBACK WepPropSheetWndProc( +BOOLEAN CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam + _In_ LPARAM lParam, + _In_ PVOID Context ) { - WNDPROC oldWndProc = PhGetWindowContext(hwnd, 1); - switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwnd, 1); - PhRemoveWindowContext(hwnd, 2); } break; case WM_SHOWWINDOW: { - if (!PhGetWindowContext(hwnd, 2)) + if (!PhGetWindowContext(hwnd, 1)) { // Move the Refresh button to where the OK button is, and move the OK button to // where the Cancel button is. @@ -407,7 +402,8 @@ LRESULT CALLBACK WepPropSheetWndProc( // in the right places. PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDOK), GetDlgItem(hwnd, IDC_REFRESH)); PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDCANCEL), GetDlgItem(hwnd, IDOK)); - PhSetWindowContext(hwnd, 2, UlongToPtr(1)); + + PhSetWindowContext(hwnd, 1, UlongToPtr(1)); } } break; @@ -433,7 +429,7 @@ LRESULT CALLBACK WepPropSheetWndProc( break; } - return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); + return FALSE; } static HPROPSHEETPAGE WepCommonCreatePage( From 9a42ebf84501f464159a3dd56caa56002688b754 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 05:54:19 +1100 Subject: [PATCH 754/839] Add listview group wrappers --- phlib/guisup.c | 45 ++++++++++++++++++++++++++++++++++++++++++ phlib/include/guisup.h | 19 ++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/phlib/guisup.c b/phlib/guisup.c index b5e0001a1d57..c5eb12b8346d 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -246,6 +246,51 @@ VOID PhSetListViewSubItem( 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, diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index 832df97fc2b4..216536889b6f 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -247,6 +247,25 @@ VOID PhSetListViewSubItem( _In_ PWSTR Text ); +PHLIBAPI +INT +NTAPI +PhAddListViewGroup( + _In_ HWND ListViewHandle, + _In_ INT GroupId, + _In_ PWSTR Text + ); + +PHLIBAPI +INT +NTAPI PhAddListViewGroupItem( + _In_ HWND ListViewHandle, + _In_ INT GroupId, + _In_ INT Index, + _In_ PWSTR Text, + _In_opt_ PVOID Param + ); + PHLIBAPI INT PhAddTabControlTab( _In_ HWND TabControlHandle, From aa8cbb286f7a73f1d2a66f9af339822af392c677 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 05:59:06 +1100 Subject: [PATCH 755/839] Update process statistics tab layout --- ProcessHacker/ProcessHacker.rc | 68 +------- ProcessHacker/prpgstat.c | 304 ++++++++++++++++++++++----------- ProcessHacker/resource.h | 5 +- 3 files changed, 214 insertions(+), 163 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index f057df71c700..b3f3f753654d 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1247,65 +1247,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + CONTROL "",IDC_STATISTICS_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,3,255,255 END IDD_OPTADVANCED DIALOGEX 0, 0, 317, 225 @@ -2206,10 +2148,10 @@ BEGIN IDD_PROCSTATISTICS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 + LEFTMARGIN, 2 + RIGHTMARGIN, 257 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 END IDD_OPTADVANCED, DIALOG diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 18eecfc8b870..67f9b6f125b2 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-2018 dmex * * This file is part of Process Hacker. * @@ -23,67 +24,142 @@ #include #include #include - #include +#include -static VOID NTAPI StatisticsUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context +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_KERNELTIME, + PH_PROCESS_STATISTICS_INDEX_USERTIME, + PH_PROCESS_STATISTICS_INDEX_TOTALTIME, + + PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTES, + PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, + PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, + PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, + PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, + 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_READBYTES, + PH_PROCESS_STATISTICS_INDEX_WRITES, + PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, + PH_PROCESS_STATISTICS_INDEX_OTHER, + PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, + 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_CONTEXTSWITCHES, + PH_PROCESS_STATISTICS_INDEX_DISKENERGY, + PH_PROCESS_STATISTICS_INDEX_NETWORKTAILENERGY, + PH_PROCESS_STATISTICS_INDEX_MBBTAILENERGY, + PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, + PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES, + PH_PROCESS_STATISTICS_INDEX_FOREGROUNDDURATION +} PH_PROCESS_STATISTICS_INDEX; + +VOID PhpUpdateStatisticsAddListViewGroups( + _In_ HWND ListViewHandle ) { - PPH_STATISTICS_CONTEXT statisticsContext = (PPH_STATISTICS_CONTEXT)Context; - - if (statisticsContext->Enabled) - PostMessage(statisticsContext->WindowHandle, WM_PH_STATISTICS_UPDATE, 0, 0); + ListView_EnableGroupView(ListViewHandle, TRUE); + + PhAddListViewGroup(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, L"CPU"); + PhAddListViewGroup(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, L"Memory"); + PhAddListViewGroup(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, L"I/O"); + PhAddListViewGroup(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, L"Other"); + PhAddListViewGroup(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, L"Extension"); + + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_PRIORITY, L"Priority", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_CYCLES, L"Cycles", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_KERNELTIME, L"Kernel time", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_USERTIME, L"User time", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_TOTALTIME, L"Total time", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTES, L"Private bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, L"Peak private bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, L"Virtual size", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, L"Peak virtual size", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, L"Page faults", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_WORKINGSET, L"Working set", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKWORKINGSET, L"Peak working set", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PRIVATEWS, L"Private WS", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_SHAREABLEWS, L"Shareable WS", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_SHAREDWS, L"Shared WS", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, L"Page priority", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READS, L"Reads", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READBYTES, L"Read bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITES, L"Writes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, L"Write bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHER, L"Other", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, L"Other bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, L"I/O priority", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_HANDLES, L"Handles", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_PEAKHANDLES, L"Peak handles", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, L"GDI handles", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, L"USER handles", NULL); + + if (WindowsVersion >= WINDOWS_10_RS3 || !PhIsExecutingInWow64()) + { + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, L"ContextSwitches", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_DISKENERGY, L"DiskEnergy", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_NETWORKTAILENERGY, L"NetworkTailEnergy", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_MBBTAILENERGY, L"MBBTailEnergy", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, L"NetworkTxRxBytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES, L"MBBTxRxBytes", NULL); + PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_FOREGROUNDDURATION, L"ForegroundDuration", NULL); + } } 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_PRIVATEBYTES, 1, PhaFormatSize(ProcessItem->VmCounters.PagefileUsage, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, 1, PhaFormatSize(ProcessItem->VmCounters.PeakPagefileUsage, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, 1, PhaFormatSize(ProcessItem->VmCounters.VirtualSize, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, 1, PhaFormatSize(ProcessItem->VmCounters.PeakVirtualSize, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, 1, PhaFormatUInt64(ProcessItem->VmCounters.PageFaultCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WORKINGSET, 1, PhaFormatSize(ProcessItem->VmCounters.WorkingSetSize, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKWORKINGSET, 1, PhaFormatSize(ProcessItem->VmCounters.PeakWorkingSetSize, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_READS, 1, PhaFormatUInt64(ProcessItem->IoCounters.ReadOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_READBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.ReadTransferCount, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WRITES, 1, PhaFormatUInt64(ProcessItem->IoCounters.WriteOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.WriteTransferCount, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_OTHER, 1, PhaFormatUInt64(ProcessItem->IoCounters.OtherOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.OtherTransferCount, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_HANDLES, 1, PhaFormatUInt64(ProcessItem->NumberOfHandles, TRUE)->Buffer); // Optional information if (!PH_IS_FAKE_PROCESS_ID(ProcessItem->ProcessId)) @@ -141,39 +217,64 @@ VOID PhpUpdateProcessStatistics( if (!gotWsCounters) privateWs = PhaFormatSize(ProcessItem->WorkingSetPrivateSize, -1); - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, PhGetStringOrDefault(peakHandles, L"Unknown")); - 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, L"Unknown")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_CYCLES, 1, PhGetStringOrEmpty(cycles)); if (pagePriority != -1 && pagePriority <= MEMORY_PRIORITY_NORMAL) - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, PhPagePriorityNames[pagePriority]); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, 1, PhPagePriorityNames[pagePriority]); else - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, L"Unknown"); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, 1, L""); if (ioPriority != -1 && ioPriority < MaxIoPriorityTypes) - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, PhIoPriorityHintNames[ioPriority]); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, 1, PhIoPriorityHintNames[ioPriority]); else - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, L"Unknown"); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, 1, L""); - 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")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PRIVATEWS, 1, PhGetStringOrEmpty(privateWs)); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_SHAREABLEWS, 1, PhGetStringOrEmpty(shareableWs)); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_SHAREDWS, 1, PhGetStringOrEmpty(sharedWs)); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKHANDLES, 1, PhGetStringOrEmpty(peakHandles)); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, 1, PhGetStringOrEmpty(gdiHandles)); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, 1, PhGetStringOrEmpty(userHandles)); } - else + + if (WindowsVersion >= WINDOWS_10_RS3 || !PhIsExecutingInWow64()) { - 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"); + PVOID processes; + PSYSTEM_PROCESS_INFORMATION processInfo; + PSYSTEM_PROCESS_INFORMATION_EXTENSION processExtension; + + if (NT_SUCCESS(PhEnumProcesses(&processes))) + { + 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); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_DISKENERGY, 1, PhaFormatUInt64(processExtension->EnergyValues.DiskEnergy, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_NETWORKTAILENERGY, 1, PhaFormatSize(processExtension->EnergyValues.NetworkTailEnergy, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_MBBTAILENERGY, 1, PhaFormatSize(processExtension->EnergyValues.MBBTailEnergy, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, 1, PhaFormatSize(processExtension->EnergyValues.NetworkTxRxBytes, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES, 1, PhaFormatSize(processExtension->EnergyValues.MBBTxRxBytes, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_FOREGROUNDDURATION, 1, PhaFormatUInt64(processExtension->EnergyValues.ForegroundDuration.Value, TRUE)->Buffer); + } + + PhFree(processes); + } } } +static VOID NTAPI StatisticsUpdateHandler( + _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, @@ -186,8 +287,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( PPH_PROCESS_ITEM processItem; PPH_STATISTICS_CONTEXT statisticsContext; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { statisticsContext = (PPH_STATISTICS_CONTEXT)propPageContext->Context; } @@ -200,20 +300,31 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( { case WM_INITDIALOG: { - statisticsContext = propPageContext->Context = - PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); + propPageContext->Context = statisticsContext = PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); + memset(statisticsContext, 0, sizeof(PH_STATISTICS_CONTEXT)); statisticsContext->WindowHandle = hwndDlg; + statisticsContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_STATISTICS_LIST); 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->ListViewHandle); PhRegisterCallback( &PhProcessesUpdatedEvent, @@ -222,7 +333,9 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( &statisticsContext->ProcessesUpdatedRegistration ); - PhpUpdateProcessStatistics(hwndDlg, processItem, statisticsContext); + PhpUpdateProcessStatistics(processItem, statisticsContext); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; case WM_DESTROY: @@ -246,27 +359,17 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( { PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, statisticsContext->ListViewHandle, dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); + ExtendedListView_SetColumnWidth(statisticsContext->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + propPageContext->LayoutInitialized = TRUE; } } break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_DETAILS: - { - PhShowHandleStatisticsDialog(hwndDlg, processItem->ProcessId); - } - break; - } - } - break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; @@ -284,7 +387,12 @@ 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; } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 1aac68d75d05..80e0ffeb0da9 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -533,6 +533,7 @@ #define IDC_DISABLED 1401 #define IDC_LIST_DISABLED 1402 #define IDC_COLUMNSETLIST 1403 +#define IDC_STATISTICS_LIST 1404 #define ID_HACKER_EXIT 40001 #define ID_PROCESS_PROPERTIES 40006 #define ID_PROCESS_TERMINATE 40007 @@ -739,9 +740,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 245 +#define _APS_NEXT_RESOURCE_VALUE 246 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1404 +#define _APS_NEXT_CONTROL_VALUE 1405 #define _APS_NEXT_SYMED_VALUE 170 #endif #endif From 38242adc14d7fe233bf629efb34d60b435e9544f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 06:02:23 +1100 Subject: [PATCH 756/839] Add missing file from previous commit --- ProcessHacker/include/procprpp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index 061aa3892692..4fd34d324121 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -326,6 +326,7 @@ typedef struct _PH_STATISTICS_CONTEXT PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; HWND WindowHandle; + HWND ListViewHandle; BOOLEAN Enabled; HANDLE ProcessHandle; } PH_STATISTICS_CONTEXT, *PPH_STATISTICS_CONTEXT; From 592d5a9cd0099e122f3db4cd6051d81816dd8297 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 06:04:33 +1100 Subject: [PATCH 757/839] Fix typo --- ProcessHacker/procprv.c | 13 ++++++------- ProcessHacker/proctree.c | 18 +++++++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index adae0dbd01f5..d7b77cf4cc39 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1000,7 +1000,7 @@ VOID PhpProcessQueryStage1( if (processHandleLimited) { BOOLEAN isDotNet = FALSE; - PPH_STRING commandLine; + PPH_STRING commandLine = NULL; HANDLE processHandle = NULL; ULONG processQueryFlags = 0; @@ -1040,7 +1040,7 @@ VOID PhpProcessQueryStage1( status = PhGetProcessCommandLine(processHandle, &commandLine); } - if (NT_SUCCESS(status)) + if (NT_SUCCESS(status) && 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. @@ -1373,11 +1373,10 @@ VOID PhpFillProcessItem( // Open a handle to the process for later usage. if (PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId)) { - ProcessItem->IsValidHandle = NT_SUCCESS(PhOpenProcess( - &ProcessItem->QueryHandle, - PROCESS_QUERY_INFORMATION, - ProcessItem->ProcessId - )); + if (NT_SUCCESS(PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId))) + { + ProcessItem->IsValidHandle = TRUE; + } if (!ProcessItem->QueryHandle) { diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index f0c32570aff7..42aa4d10f58a 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -838,19 +838,15 @@ static VOID PhpUpdateProcessNodeWsCounters( BOOLEAN success = FALSE; HANDLE processHandle; - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION, - ProcessNode->ProcessItem->ProcessId - ))) + if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId)) { - if (NT_SUCCESS(PhGetProcessWsCounters( - processHandle, - &ProcessNode->WsCounters - ))) - success = TRUE; + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, ProcessNode->ProcessItem->ProcessId))) + { + if (NT_SUCCESS(PhGetProcessWsCounters(processHandle, &ProcessNode->WsCounters))) + success = TRUE; - NtClose(processHandle); + NtClose(processHandle); + } } if (!success) From b3ac10496fc68e3a2ccb807e288b189de142b24b Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 07:49:27 +1100 Subject: [PATCH 758/839] Fix process statistics tab crash --- ProcessHacker/prpgstat.c | 3 +-- phlib/extlv.c | 9 +++++---- phlib/guisup.c | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 67f9b6f125b2..6b49061e2655 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -348,9 +348,8 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( if (statisticsContext->ProcessHandle) NtClose(statisticsContext->ProcessHandle); - PhFree(statisticsContext); - PhpPropPageDlgProcDestroy(hwndDlg); + PhFree(statisticsContext); } break; case WM_SHOWWINDOW: diff --git a/phlib/extlv.c b/phlib/extlv.c index 0e210ed59863..0bed9ab7f6cf 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -113,7 +113,6 @@ VOID PhSetExtendedListView( context = PhAllocate(sizeof(PH_EXTLV_CONTEXT)); context->Handle = hWnd; - context->OldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); context->Context = NULL; context->TriState = FALSE; context->SortColumn = 0; @@ -127,7 +126,9 @@ VOID PhSetExtendedListView( context->EnableRedraw = 1; context->Cursor = NULL; - PhSetWindowContext(hWnd, 5, context); + PhSetWindowContext(hWnd, MAXBYTE, context); + + context->OldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PhpExtendedListViewWndProc); ExtendedListView_Init(hWnd); @@ -140,14 +141,14 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( _In_ LPARAM lParam ) { - PPH_EXTLV_CONTEXT context = PhGetWindowContext(hwnd, 5); + PPH_EXTLV_CONTEXT context = PhGetWindowContext(hwnd, MAXBYTE); switch (uMsg) { case WM_DESTROY: { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)context->OldWndProc); - PhRemoveWindowContext(hwnd, 5); + PhRemoveWindowContext(hwnd, MAXBYTE); PhFree(context); } break; diff --git a/phlib/guisup.c b/phlib/guisup.c index c5eb12b8346d..8bb8adc2abab 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -290,7 +290,6 @@ INT PhAddListViewGroupItem( return ListView_InsertItem(ListViewHandle, &item); } - INT PhAddTabControlTab( _In_ HWND TabControlHandle, _In_ INT Index, From ad60fed1e348ade43c370e0092bb90252e569126 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 07:56:06 +1100 Subject: [PATCH 759/839] Remove plugin error prompt --- ProcessHacker/plugin.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 5dde37daed95..8554c80d545d 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -299,25 +299,11 @@ VOID PhLoadPlugins( PhDereferenceObject(baseName); } - PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); - - if (PhShowMessage2( - NULL, - TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, - TD_ERROR_ICON, + PhShowError2( + NULL, L"Unable to load the following plugin(s)", - L"%s", sb.String->Buffer - ) == IDYES) - { - for (i = 0; i < LoadErrors->Count; i++) - { - loadError = LoadErrors->Items[i]; - baseName = PhGetBaseName(loadError->FileName); - PhSetPluginDisabled(&baseName->sr, TRUE); - PhDereferenceObject(baseName); - } - } + ); PhDeleteStringBuilder(&sb); } From 3531bfe3671a46e9a9dd2eb4740a317bce0f2679 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 08:03:03 +1100 Subject: [PATCH 760/839] Fix stat page context --- ProcessHacker/prpgstat.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 6b49061e2655..6ec4ed2bdd9f 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -289,7 +289,15 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { - statisticsContext = (PPH_STATISTICS_CONTEXT)propPageContext->Context; + if (propPageContext->Context) + { + statisticsContext = propPageContext->Context; + } + else + { + statisticsContext = propPageContext->Context = PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); + memset(propPageContext->Context, 0, sizeof(PH_STATISTICS_CONTEXT)); + } } else { @@ -300,9 +308,6 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( { case WM_INITDIALOG: { - propPageContext->Context = statisticsContext = PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); - memset(statisticsContext, 0, sizeof(PH_STATISTICS_CONTEXT)); - statisticsContext->WindowHandle = hwndDlg; statisticsContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_STATISTICS_LIST); statisticsContext->Enabled = TRUE; From 9a400fb43390e7e6e71ff4177850c6782b12da2f Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 08:31:03 +1100 Subject: [PATCH 761/839] Fix build --- ProcessHacker/ProcessHacker.def | 2 ++ ProcessHacker/prpgstat.c | 16 +++++----------- tools/peview/include/prpsh.h | 4 ++-- tools/peview/prpsh.c | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index eafb53d70c24..8a5dc9bb6eec 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -508,6 +508,8 @@ EXPORTS PhSetImageListBitmap PhSetListViewItemImageIndex PhSetListViewSubItem + PhAddListViewGroup + PhAddListViewGroupItem PhSetStateAllListViewItems PhGetWindowContext PhSetWindowContext diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 6ec4ed2bdd9f..e2cb541e8c58 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -289,15 +289,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { - if (propPageContext->Context) - { - statisticsContext = propPageContext->Context; - } - else - { - statisticsContext = propPageContext->Context = PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); - memset(propPageContext->Context, 0, sizeof(PH_STATISTICS_CONTEXT)); - } + statisticsContext = (PPH_STATISTICS_CONTEXT)propPageContext->Context; } else { @@ -308,6 +300,8 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( { case WM_INITDIALOG: { + statisticsContext = propPageContext->Context = PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); + statisticsContext->WindowHandle = hwndDlg; statisticsContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_STATISTICS_LIST); statisticsContext->Enabled = TRUE; @@ -368,10 +362,10 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( PhDoPropPageLayout(hwndDlg); - ExtendedListView_SetColumnWidth(statisticsContext->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); - propPageContext->LayoutInitialized = TRUE; } + + ExtendedListView_SetColumnWidth(statisticsContext->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; case WM_NOTIFY: diff --git a/tools/peview/include/prpsh.h b/tools/peview/include/prpsh.h index 1290e7f63120..6857565c5061 100644 --- a/tools/peview/include/prpsh.h +++ b/tools/peview/include/prpsh.h @@ -116,10 +116,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/prpsh.c b/tools/peview/prpsh.c index f697d1bcb282..14fcba4fe066 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -155,7 +155,7 @@ INT CALLBACK PvpPropSheetProc( PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); - SetProp(hwndDlg, L"PvContext", (HANDLE)propSheetContext); + PhSetWindowContext(hwndDlg, ULONG_MAX, propSheetContext); SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); if (MinimumSize.left == -1) @@ -181,7 +181,7 @@ PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( _In_ HWND hwnd ) { - return (PPV_PROPSHEETCONTEXT)GetProp(hwnd, L"PvContext"); + return PhGetWindowContext(hwnd, ULONG_MAX); } LRESULT CALLBACK PvpPropSheetWndProc( From 3e6d9e4415e6ea7b94625e3558536a3242b6c136 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 20:13:57 +1100 Subject: [PATCH 762/839] Plugins: Fix #235 --- plugins/Updater/updater.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 040435c7a5ee..0570ee4c60ad 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -216,7 +216,7 @@ ULONG64 ParseVersionString( PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - PhInitializeStringRef(&remaining, PhGetString(VersionString)); + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(VersionString)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); @@ -880,6 +880,7 @@ NTSTATUS ShowUpdateDialogThread( // 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; From bc8b88f1432e964be15552d72b5fcc5cdb485145 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 4 Feb 2018 20:16:23 +1100 Subject: [PATCH 763/839] Fix crash #234 --- phlib/guisup.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/phlib/guisup.c b/phlib/guisup.c index 8bb8adc2abab..3edefee52593 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1378,6 +1378,25 @@ static LRESULT CALLBACK PhWindowSubclassCallback( } } + if (uMsg == WM_DESTROY) + { + LRESULT result; + + result = CallWindowProc(context->DefaultWindowProc, WindowHandle, uMsg, wParam, lParam); + + if (context->WindowCallbackArray.Count == 0) + { + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + + PhRemoveWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT); + + PhDeleteArray(&context->WindowCallbackArray); + PhFree(context); + } + + return result; + } + return CallWindowProc(context->DefaultWindowProc, WindowHandle, uMsg, wParam, lParam); } @@ -1432,14 +1451,4 @@ VOID PhUnregisterWindowSubclass( if (entry->SubclassCallback == SubclassCallback) PhRemoveItemArray(&context->WindowCallbackArray, i); } - - if (context->WindowCallbackArray.Count == 0) - { - SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); - - PhRemoveWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT); - - PhDeleteArray(&context->WindowCallbackArray); - PhFree(context); - } } From 33e0ab1d9678927b677e62d8b55b6085bfe440ba Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 04:00:58 +1100 Subject: [PATCH 764/839] Revert commit 6a02c5b0 --- ProcessHacker/ProcessHacker.def | 2 - ProcessHacker/include/procprpp.h | 6 +- ProcessHacker/include/procprv.h | 2 +- ProcessHacker/include/sysinfo.h | 3 + ProcessHacker/include/sysinfop.h | 10 ++- ProcessHacker/memsrch.c | 24 ++++--- ProcessHacker/miniinfo.c | 6 +- ProcessHacker/procprp.c | 28 +++++--- ProcessHacker/procprv.c | 60 ++++++++--------- ProcessHacker/searchbox.c | 2 +- ProcessHacker/splitter.c | 32 +++++---- ProcessHacker/sysinfo.c | 57 +++++++++++----- phlib/guisup.c | 99 ---------------------------- phlib/include/guisup.h | 39 ----------- phlib/include/treenewp.h | 8 ++- phlib/treenew.c | 32 ++++++--- plugins/ExtendedNotifications/main.c | 28 +++++--- plugins/HardwareDevices/disknotify.c | 29 ++++---- plugins/HardwareDevices/prpsh.c | 39 +++++------ plugins/HardwareDevices/prpsh.h | 1 + plugins/OnlineChecks/onlnchk.h | 1 + plugins/OnlineChecks/upload.c | 22 +++++-- plugins/ToolStatus/main.c | 21 +++--- plugins/Updater/updater.c | 33 ++++++---- plugins/Updater/updater.h | 1 + plugins/WindowExplorer/wndprp.c | 26 +++++--- tools/peview/include/prpsh.h | 3 +- tools/peview/pdbprp.c | 9 +++ tools/peview/prpsh.c | 37 ++++++----- 29 files changed, 320 insertions(+), 340 deletions(-) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index 8a5dc9bb6eec..c021c8433379 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -514,8 +514,6 @@ EXPORTS PhGetWindowContext PhSetWindowContext PhRemoveWindowContext - PhRegisterWindowSubclass - PhUnregisterWindowSubclass ; hndlinfo PhEnumObjectTypes diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index 4fd34d324121..9c1846441c34 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -9,6 +9,7 @@ typedef struct _PH_PROCESS_PROPSHEETCONTEXT { + WNDPROC PropSheetWindowHookProc; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM TabPageItem; BOOLEAN LayoutInitialized; @@ -29,12 +30,11 @@ PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( _In_ HWND hwnd ); -BOOLEAN CALLBACK PhpPropSheetWndProc( +LRESULT CALLBACK PhpPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ); VOID NTAPI PhpProcessPropPageContextDeleteProcedure( diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index 0405382525f6..6b6d218ce201 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -158,7 +158,7 @@ typedef struct _PH_PROCESS_ITEM ULONG IsInJob : 1; ULONG IsInSignificantJob : 1; ULONG IsPacked : 1; - ULONG IsValidHandle : 1; + ULONG IsHandleValid : 1; ULONG IsSuspended : 1; ULONG IsWow64 : 1; ULONG IsImmersive : 1; diff --git a/ProcessHacker/include/sysinfo.h b/ProcessHacker/include/sysinfo.h index 89127b2a383d..4d7931ff1b18 100644 --- a/ProcessHacker/include/sysinfo.h +++ b/ProcessHacker/include/sysinfo.h @@ -128,6 +128,9 @@ typedef struct _PH_SYSINFO_SECTION HWND DialogHandle; HWND PanelHandle; ULONG PanelId; + + WNDPROC GraphWindowProc; + WNDPROC PanelWindowProc; // begin_phapppub } PH_SYSINFO_SECTION, *PPH_SYSINFO_SECTION; // end_phapppub diff --git a/ProcessHacker/include/sysinfop.h b/ProcessHacker/include/sysinfop.h index 87ed3b71b470..875a38587a07 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -189,20 +189,18 @@ VOID PhSipCreateSectionDialog( _In_ PPH_SYSINFO_SECTION Section ); -BOOLEAN CALLBACK PhSipGraphHookWndProc( +LRESULT CALLBACK PhSipGraphHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ); -BOOLEAN CALLBACK PhSipPanelHookWndProc( +LRESULT CALLBACK PhSipPanelHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ); // Misc. diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index 78e4e0e62b91..9d665ab2cbe2 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -56,6 +56,7 @@ typedef struct _MEMORY_STRING_CONTEXT HWND ParentWindowHandle; HWND WindowHandle; + WNDPROC DefaultWindowProc; PH_MEMORY_STRING_OPTIONS Options; PPH_LIST Results; } MEMORY_STRING_CONTEXT, *PMEMORY_STRING_CONTEXT; @@ -650,20 +651,25 @@ NTSTATUS PhpMemoryStringThreadStart( return STATUS_SUCCESS; } -BOOLEAN CALLBACK PhpMemoryStringTaskDialogSubclassProc( +LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)Context; + PMEMORY_STRING_CONTEXT context = PhGetWindowContext(hwndDlg, 0xF); + + if (!context) + return 0; switch (uMsg) { - case WM_NCDESTROY: - PhUnregisterWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc); + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + PhRemoveWindowContext(hwndDlg, 0xF); + } break; case WM_PH_MEMORY_STATUS_UPDATE: { @@ -680,7 +686,7 @@ BOOLEAN CALLBACK PhpMemoryStringTaskDialogSubclassProc( break; } - return FALSE; + return CallWindowProc(context->DefaultWindowProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( @@ -714,7 +720,9 @@ HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); // Subclass the Taskdialog. - PhRegisterWindowSubclass(hwndDlg, PhpMemoryStringTaskDialogSubclassProc, context); + 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); diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index a3be5d07940e..4208da0cfc09 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -470,7 +470,7 @@ VOID PhMipOnInitDialog( // Subclass the window procedure. oldWndProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC); - PhSetWindowContext(GetDlgItem(PhMipWindow, IDC_SECTION), 10, oldWndProc); + PhSetWindowContext(GetDlgItem(PhMipWindow, IDC_SECTION), 0xF, oldWndProc); SetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC, (LONG_PTR)PhMipSectionControlHookWndProc); } @@ -1159,7 +1159,7 @@ LRESULT CALLBACK PhMipSectionControlHookWndProc( { WNDPROC oldWndProc; - if (!(oldWndProc = PhGetWindowContext(hwnd, 10))) + if (!(oldWndProc = PhGetWindowContext(hwnd, 0xF))) return 0; switch (uMsg) @@ -1167,7 +1167,7 @@ LRESULT CALLBACK PhMipSectionControlHookWndProc( case WM_DESTROY: { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); - PhRemoveWindowContext(hwnd, 10); + PhRemoveWindowContext(hwnd, 0xF); } break; case WM_SETCURSOR: diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index fec4120ed7fb..9273e4c19a71 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -166,9 +166,11 @@ INT CALLBACK PhpPropSheetProc( memset(propSheetContext, 0, sizeof(PH_PROCESS_PROPSHEETCONTEXT)); PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); - PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, propSheetContext); - PhRegisterWindowSubclass(hwndDlg, PhpPropSheetWndProc, propSheetContext); + + propSheetContext->PropSheetWindowHookProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, propSheetContext); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PhpPropSheetWndProc); if (MinimumSize.left == -1) { @@ -196,15 +198,19 @@ PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( return PhGetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); } -BOOLEAN CALLBACK PhpPropSheetWndProc( +LRESULT CALLBACK PhpPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext = (PPH_PROCESS_PROPSHEETCONTEXT)Context; + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + + propSheetContext = PhGetWindowContext(hwnd, 0xF); + + if (!propSheetContext) + return 0; switch (uMsg) { @@ -234,11 +240,15 @@ BOOLEAN CALLBACK PhpPropSheetWndProc( break; case WM_NCDESTROY: { - PhUnregisterWindowSubclass(hwnd, PhpPropSheetWndProc); - PhRemoveWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); + LRESULT result; + + result = CallWindowProc(propSheetContext->PropSheetWindowHookProc, hwnd, uMsg, wParam, lParam); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)propSheetContext->PropSheetWindowHookProc); + PhRemoveWindowContext(hwnd, 0xF); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); + return result; } break; case WM_COMMAND: @@ -268,7 +278,7 @@ BOOLEAN CALLBACK PhpPropSheetWndProc( break; } - return FALSE; + return CallWindowProc(propSheetContext->PropSheetWindowHookProc, hwnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index d7b77cf4cc39..491898b25d23 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -107,7 +107,7 @@ typedef struct _PH_PROCESS_QUERY_S1_DATA ULONG IsInSignificantJob : 1; ULONG IsBeingDebugged : 1; ULONG IsImmersive : 1; - ULONG IsProtectedHandle : 1; + ULONG IsFilteredHandle : 1; ULONG Spare : 25; }; }; @@ -1000,7 +1000,6 @@ VOID PhpProcessQueryStage1( if (processHandleLimited) { BOOLEAN isDotNet = FALSE; - PPH_STRING commandLine = NULL; HANDLE processHandle = NULL; ULONG processQueryFlags = 0; @@ -1019,6 +1018,24 @@ VOID PhpProcessQueryStage1( ); } + 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 / 2; i++) + { + if (commandLine->Buffer[i] == 0) + commandLine->Buffer[i] = ' '; + } + + Data->CommandLine = commandLine; + } + } + if (NT_SUCCESS(status)) { PhGetProcessIsDotNetEx( @@ -1035,24 +1052,6 @@ VOID PhpProcessQueryStage1( Data->IsDotNet = isDotNet; } - if (NT_SUCCESS(status)) - { - status = PhGetProcessCommandLine(processHandle, &commandLine); - } - - if (NT_SUCCESS(status) && 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 / 2; i++) - { - if (commandLine->Buffer[i] == 0) - commandLine->Buffer[i] = ' '; - } - - Data->CommandLine = commandLine; - } - if (!(processQueryFlags & PH_CLR_USE_SECTION_CHECK) && processHandle) NtClose(processHandle); } @@ -1062,9 +1061,7 @@ VOID PhpProcessQueryStage1( { HANDLE tokenHandle; - status = PhOpenProcessToken(processHandleLimited, TOKEN_QUERY, &tokenHandle); - - if (NT_SUCCESS(status)) + if (NT_SUCCESS(PhOpenProcessToken(processHandleLimited, TOKEN_QUERY, &tokenHandle))) { // Elevation if (NT_SUCCESS(PhGetTokenElevationType( @@ -1156,14 +1153,14 @@ VOID PhpProcessQueryStage1( Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); } - if (processHandleLimited && processItem->IsValidHandle) + if (processHandleLimited && processItem->IsHandleValid) { OBJECT_BASIC_INFORMATION basicInfo; if (NT_SUCCESS(PhGetHandleInformationEx( NtCurrentProcess(), processHandleLimited, - -1, + ULONG_MAX, 0, NULL, &basicInfo, @@ -1174,11 +1171,11 @@ VOID PhpProcessQueryStage1( ))) { if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) - Data->IsProtectedHandle = TRUE; + Data->IsFilteredHandle = TRUE; } else { - Data->IsProtectedHandle = TRUE; + Data->IsFilteredHandle = TRUE; } } } @@ -1187,11 +1184,12 @@ VOID PhpProcessQueryStage2( _Inout_ PPH_PROCESS_QUERY_S2_DATA Data ) { - NTSTATUS status; PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; if (PhEnableProcessQueryStage2 && processItem->FileName) { + NTSTATUS status; + Data->VerifyResult = PhVerifyFileCached( processItem->FileName, processItem->PackageFullName, @@ -1316,7 +1314,7 @@ VOID PhpFillProcessItemStage1( processItem->IsInSignificantJob = Data->IsInSignificantJob; processItem->IsBeingDebugged = Data->IsBeingDebugged; processItem->IsImmersive = Data->IsImmersive; - processItem->IsProtectedHandle = Data->IsProtectedHandle; + processItem->IsProtectedHandle = Data->IsFilteredHandle; PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); @@ -1375,7 +1373,7 @@ VOID PhpFillProcessItem( { if (NT_SUCCESS(PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId))) { - ProcessItem->IsValidHandle = TRUE; + ProcessItem->IsHandleValid = TRUE; } if (!ProcessItem->QueryHandle) @@ -1514,7 +1512,7 @@ VOID PhpFillProcessItem( // On Windows 8.1 and above, processes without threads are reflected processes // which will not terminate if we have a handle open. - if (Process->NumberOfThreads == 0) + if (Process->NumberOfThreads == 0 && ProcessItem->QueryHandle) { NtClose(ProcessItem->QueryHandle); ProcessItem->QueryHandle = NULL; diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 080bd02e5484..8c12ecef56cf 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -523,7 +523,6 @@ VOID PhCreateSearchControl( memset(context, 0, sizeof(EDIT_CONTEXT)); context->WindowHandle = WindowHandle; - context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); //PhpSearchInitializeTheme(context); PhpSearchInitializeImages(context); @@ -533,6 +532,7 @@ VOID PhCreateSearchControl( Edit_SetCueBannerText(WindowHandle, BannerText); // Subclass the Edit control window procedure. + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); PhSetWindowContext(WindowHandle, 10, context); SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpSearchWndSubclassProc); diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index 1dcb8dba9783..e6ce1a57abc9 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -43,10 +43,11 @@ typedef struct _PH_HSPLITTER_CONTEXT LONG SplitterOffset; - HWND Window; + HWND WindowHandle; HWND ParentWindow; HWND TopWindow; HWND BottomWindow; + WNDPROC DefaultWindowProc; HBRUSH FocusBrush; HBRUSH HotBrush; @@ -237,7 +238,7 @@ LRESULT CALLBACK HSplitterWindowProc( ); DeferWindowPos( deferHandle, - context->Window, + context->WindowHandle, NULL, 0, cursorPos.y, @@ -264,22 +265,25 @@ LRESULT CALLBACK HSplitterWindowProc( return DefWindowProc(hwnd, uMsg, wParam, lParam); } -BOOLEAN CALLBACK HSplitterParentWindowProc( +LRESULT CALLBACK HSplitterParentWindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_HSPLITTER_CONTEXT context = (PPH_HSPLITTER_CONTEXT)Context; + PPH_HSPLITTER_CONTEXT context = PhGetWindowContext(hwnd, 0x200); + + if (!context) + return 0; switch (uMsg) { case WM_DESTROY: { - PhUnregisterWindowSubclass(hwnd, HSplitterParentWindowProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); PhDeleteHSplitter(context); + PhRemoveWindowContext(hwnd, 0x200); } break; case WM_SIZE: @@ -289,7 +293,7 @@ BOOLEAN CALLBACK HSplitterParentWindowProc( break; } - return FALSE; + return CallWindowProc(context->DefaultWindowProc, hwnd, uMsg, wParam, lParam); } VOID PhInitializeHSplitter( @@ -335,7 +339,7 @@ VOID PhInitializeHSplitter( PhEndInitOnce(&initOnce); } - context->Window = CreateWindowEx( + context->WindowHandle = CreateWindowEx( WS_EX_TRANSPARENT, L"PhHSplitter", NULL, @@ -350,10 +354,12 @@ VOID PhInitializeHSplitter( context ); - ShowWindow(context->Window, SW_SHOW); - UpdateWindow(context->Window); + ShowWindow(context->WindowHandle, SW_SHOW); + UpdateWindow(context->WindowHandle); - PhRegisterWindowSubclass(ParentWindow, HSplitterParentWindowProc, context); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(ParentWindow, GWLP_WNDPROC); + PhSetWindowContext(ParentWindow, 0x200, context); + SetWindowLongPtr(ParentWindow, GWLP_WNDPROC, (LONG_PTR)HSplitterParentWindowProc); } VOID PhDeleteHSplitter( @@ -391,7 +397,7 @@ VOID PhHSplitterHandleWmSize( DeferWindowPos( deferHandle, - Context->Window, + Context->WindowHandle, NULL, 0, Context->SplitterOffset - 65, diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index c313c7fc812e..c2fa5ecd4b6f 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -75,6 +75,7 @@ static PPH_SYSINFO_SECTION CurrentSection; static HWND ContainerControl; static HWND SeparatorControl; static HWND RestoreSummaryControl; +static WNDPROC RestoreSummaryControlOldWndProc; static BOOLEAN RestoreSummaryControlHot; static BOOLEAN RestoreSummaryControlHasFocus; @@ -417,7 +418,8 @@ VOID PhSipOnShowWindow( NULL ); - PhRegisterWindowSubclass(RestoreSummaryControl, PhSipPanelHookWndProc, NULL); + RestoreSummaryControlOldWndProc = (WNDPROC)GetWindowLongPtr(RestoreSummaryControl, GWLP_WNDPROC); + SetWindowLongPtr(RestoreSummaryControl, GWLP_WNDPROC, (LONG_PTR)PhSipPanelHookWndProc); RestoreSummaryControlHot = FALSE; EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); @@ -1150,13 +1152,19 @@ PPH_SYSINFO_SECTION PhSipCreateSection( 3, 3, PhSipWindow, - (HMENU)(ULONG_PTR)section->PanelId, + UlongToHandle(section->PanelId), PhInstanceHandle, NULL ); - PhRegisterWindowSubclass(section->GraphHandle, PhSipGraphHookWndProc, section); - PhRegisterWindowSubclass(section->PanelHandle, PhSipPanelHookWndProc, section); + 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); @@ -1715,20 +1723,27 @@ VOID PhSipCreateSectionDialog( } } -BOOLEAN CALLBACK PhSipGraphHookWndProc( +LRESULT CALLBACK PhSipGraphHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)Context; + PPH_SYSINFO_SECTION section; + + section = PhGetWindowContext(hwnd, 0xF); + + if (!section) + return 0; switch (uMsg) { case WM_DESTROY: - PhUnregisterWindowSubclass(hwnd, PhSipGraphHookWndProc); + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)section->GraphWindowProc); + PhRemoveWindowContext(hwnd, 0xF); + } break; case WM_SETFOCUS: section->HasFocus = TRUE; @@ -1864,23 +1879,33 @@ BOOLEAN CALLBACK PhSipGraphHookWndProc( break; } - return FALSE; + return CallWindowProc(section->GraphWindowProc, hwnd, uMsg, wParam, lParam); } -BOOLEAN CALLBACK PhSipPanelHookWndProc( +LRESULT CALLBACK PhSipPanelHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)Context; + PPH_SYSINFO_SECTION section; + WNDPROC oldWndProc; + + section = PhGetWindowContext(hwnd, 0xF); + + if (section) + oldWndProc = section->PanelWindowProc; + else + oldWndProc = RestoreSummaryControlOldWndProc; switch (uMsg) { case WM_DESTROY: - PhUnregisterWindowSubclass(hwnd, PhSipPanelHookWndProc); + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } break; case WM_SETFOCUS: { @@ -1984,7 +2009,7 @@ BOOLEAN CALLBACK PhSipPanelHookWndProc( break; } - return FALSE; + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } VOID PhSipUpdateThemeData( diff --git a/phlib/guisup.c b/phlib/guisup.c index 3edefee52593..2d24f76a515b 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1353,102 +1353,3 @@ VOID PhRemoveWindowContext( PhRemoveEntryHashtable(WindowContextHashTable, &lookupEntry); PhReleaseQueuedLockExclusive(&WindowContextListLock); } - -static LRESULT CALLBACK PhWindowSubclassCallback( - _In_ HWND WindowHandle, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPH_WINDOW_SUBCLASS context; - PPH_WINDOW_SUBCLASS_ENTRY entry; - ULONG i; - - if (!(context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT))) - return 0; - - for (i = 0; i < context->WindowCallbackArray.Count; i++) - { - entry = PhItemArray(&context->WindowCallbackArray, i); - - if (entry->SubclassCallback(WindowHandle, uMsg, wParam, lParam, entry->Context)) - { - return DefWindowProc(WindowHandle, uMsg, wParam, lParam); - } - } - - if (uMsg == WM_DESTROY) - { - LRESULT result; - - result = CallWindowProc(context->DefaultWindowProc, WindowHandle, uMsg, wParam, lParam); - - if (context->WindowCallbackArray.Count == 0) - { - SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); - - PhRemoveWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT); - - PhDeleteArray(&context->WindowCallbackArray); - PhFree(context); - } - - return result; - } - - return CallWindowProc(context->DefaultWindowProc, WindowHandle, uMsg, wParam, lParam); -} - -VOID PhRegisterWindowSubclass( - _In_ HWND WindowHandle, - _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback, - _In_opt_ PVOID Context - ) -{ - PPH_WINDOW_SUBCLASS context; - PH_WINDOW_SUBCLASS_ENTRY entry; - - entry.SubclassCallback = SubclassCallback; - entry.Context = Context; - - if (context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT)) - { - PhAddItemArray(&context->WindowCallbackArray, &entry); - } - else - { - context = PhAllocate(sizeof(PH_WINDOW_SUBCLASS)); - memset(context, 0, sizeof(PH_WINDOW_SUBCLASS)); - - PhInitializeArray(&context->WindowCallbackArray, sizeof(PH_WINDOW_SUBCLASS_ENTRY), 1); - PhAddItemArray(&context->WindowCallbackArray, &entry); - - context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); - - PhSetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT, context); - - SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhWindowSubclassCallback); - } -} - -VOID PhUnregisterWindowSubclass( - _In_ HWND WindowHandle, - _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback - ) -{ - PPH_WINDOW_SUBCLASS context; - PPH_WINDOW_SUBCLASS_ENTRY entry; - ULONG i; - - if (!(context = PhGetWindowContext(WindowHandle, PH_WINDOW_SUBCLASS_CONTEXT))) - return; - - for (i = 0; i < context->WindowCallbackArray.Count; i++) - { - entry = PhItemArray(&context->WindowCallbackArray, i); - - if (entry->SubclassCallback == SubclassCallback) - PhRemoveItemArray(&context->WindowCallbackArray, i); - } -} diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index 216536889b6f..06d4799f1cf9 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -500,45 +500,6 @@ PhRemoveWindowContext( _In_ ULONG PropertyHash ); -typedef BOOLEAN (NTAPI *PPH_WINDOW_SUBCLASS_CALLBACK)( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context - ); - -typedef struct _PH_WINDOW_SUBCLASS_ENTRY -{ - PPH_WINDOW_SUBCLASS_CALLBACK SubclassCallback; - PVOID Context; -} PH_WINDOW_SUBCLASS_ENTRY, *PPH_WINDOW_SUBCLASS_ENTRY; - -typedef struct _PH_WINDOW_SUBCLASS -{ - WNDPROC DefaultWindowProc; - PH_ARRAY WindowCallbackArray; -} PH_WINDOW_SUBCLASS, *PPH_WINDOW_SUBCLASS; - -#define PH_WINDOW_SUBCLASS_CONTEXT 0xFFFFFF - -PHLIBAPI -VOID -NTAPI -PhRegisterWindowSubclass( - _In_ HWND WindowHandle, - _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassWindowProc, - _In_opt_ PVOID Context - ); - -PHLIBAPI -VOID -NTAPI -PhUnregisterWindowSubclass( - _In_ HWND WindowHandle, - _In_ PPH_WINDOW_SUBCLASS_CALLBACK SubclassWindowProc - ); - FORCEINLINE VOID PhResizingMinimumSize( _Inout_ PRECT Rect, _In_ WPARAM Edge, diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index fc0dc1974680..e1d9eb397500 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -160,6 +160,9 @@ typedef struct _PH_TREENEW_CONTEXT HRGN SuspendUpdateRegion; PH_STRINGREF EmptyText; + + WNDPROC HeaderWindowProc; + WNDPROC FixedHeaderWindowProc; } PH_TREENEW_CONTEXT, *PPH_TREENEW_CONTEXT; LRESULT CALLBACK PhTnpWndProc( @@ -730,12 +733,11 @@ VOID PhTnpGetHeaderTooltipText( _Out_ PWSTR *Text ); -BOOLEAN CALLBACK PhTnpHeaderHookWndProc( +LRESULT CALLBACK PhTnpHeaderHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ); // Drag selection diff --git a/phlib/treenew.c b/phlib/treenew.c index 1330fc93376d..fa4818e5461f 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -5846,8 +5846,14 @@ VOID PhTnpInitializeTooltips( SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); // Hook the header control window procedures so we can forward mouse messages to the tooltip control. - PhRegisterWindowSubclass(Context->FixedHeaderHandle, PhTnpHeaderHookWndProc, Context); - PhRegisterWindowSubclass(Context->HeaderHandle, PhTnpHeaderHookWndProc, Context); + 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 SendMessage(Context->TooltipsHandle, WM_SETFONT, (WPARAM)Context->Font, FALSE); @@ -6134,20 +6140,30 @@ VOID PhTnpGetHeaderTooltipText( SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH); } -BOOLEAN CALLBACK PhTnpHeaderHookWndProc( +LRESULT CALLBACK PhTnpHeaderHookWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_TREENEW_CONTEXT context = (PPH_TREENEW_CONTEXT)Context; + 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: - PhUnregisterWindowSubclass(hwnd, PhTnpHeaderHookWndProc); + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } break; case WM_MOUSEMOVE: { @@ -6233,7 +6249,7 @@ BOOLEAN CALLBACK PhTnpHeaderHookWndProc( break; } - return FALSE; + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam);; } BOOLEAN PhTnpDetectDrag( diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index d8940a4879d5..d92de3748d08 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -606,15 +606,21 @@ LRESULT CALLBACK TextBoxSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { + WNDPROC oldWndProc = PhGetWindowContext(hWnd, UCHAR_MAX); + + if (!oldWndProc) + return 0; + switch (uMsg) { - case WM_NCDESTROY: - RemoveWindowSubclass(hWnd, TextBoxSubclassProc, uIdSubclass); + case WM_DESTROY: + { + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, UCHAR_MAX); + } break; case WM_GETDLGCODE: { @@ -633,7 +639,7 @@ LRESULT CALLBACK TextBoxSubclassProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } VOID FixControlStates( @@ -665,8 +671,14 @@ INT_PTR HandleCommonMessages( { case WM_INITDIALOG: { - SetWindowSubclass(GetDlgItem(hwndDlg, IDC_TEXT), TextBoxSubclassProc, 0, 0); - + 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); diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index c227704b1e8a..8330bd2eaf63 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -23,20 +23,17 @@ #include "devices.h" #include -static BOOLEAN SubclassActive = FALSE; +static WNDPROC SubclassMainWindowProc = NULL; -BOOLEAN CALLBACK MainWndDevicesSubclassProc( +LRESULT CALLBACK MainWndDevicesSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - // 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. + if (!SubclassMainWindowProc) + return 0; switch (uMsg) { @@ -80,7 +77,7 @@ BOOLEAN CALLBACK MainWndDevicesSubclassProc( break; } - return FALSE; + return CallWindowProc(SubclassMainWindowProc, hWnd, uMsg, wParam, lParam); } VOID AddRemoveDeviceChangeCallback( @@ -92,22 +89,22 @@ 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 (!SubclassMainWindowProc) { // We have a disk device, subclass the main window to detect drive letter changes. - PhRegisterWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, NULL); - SubclassActive = TRUE; + SubclassMainWindowProc = (WNDPROC)GetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC); + SetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC, (LONG_PTR)MainWndDevicesSubclassProc); } } else { - if (SubclassActive) + if (SubclassMainWindowProc) { // The user has removed the last disk device, remove the subclass. - PhUnregisterWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc); - SubclassActive = FALSE; + SetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC, (LONG_PTR)SubclassMainWindowProc); + SubclassMainWindowProc = NULL; } } } \ No newline at end of file diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index 4b0aedf56512..d0d7d107b799 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( @@ -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); - SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); - PhSetWindowContext(hwndDlg, ULONG_MAX, propSheetContext); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, ULONG_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PvpPropSheetWndProc); if (MinimumSize.left == -1) { @@ -177,23 +176,25 @@ 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 = PhGetWindowContext(hWnd, ULONG_MAX); + + if (!context) + return 0; switch (uMsg) { - case WM_NCDESTROY: + case WM_DESTROY: { - RemoveWindowSubclass(hWnd, PvpPropSheetWndProc, uIdSubclass); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + 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: @@ -212,7 +213,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( { if (!IsIconic(hWnd)) { - PhLayoutManagerLayout(&propSheetContext->LayoutManager); + PhLayoutManagerLayout(&context->LayoutManager); } } break; @@ -223,7 +224,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( diff --git a/plugins/HardwareDevices/prpsh.h b/plugins/HardwareDevices/prpsh.h index dd2411ab700c..f3f0e6d3e7b5 100644 --- a/plugins/HardwareDevices/prpsh.h +++ b/plugins/HardwareDevices/prpsh.h @@ -30,6 +30,7 @@ typedef struct _PV_PROPSHEETCONTEXT { BOOLEAN LayoutInitialized; + WNDPROC DefaultWindowProc; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM TabPageItem; } PV_PROPSHEETCONTEXT, *PPV_PROPSHEETCONTEXT; diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index e07b3245d69c..4f109a9aeccd 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -122,6 +122,7 @@ typedef struct _UPLOAD_CONTEXT ULONG TotalFileLength; HWND DialogHandle; + WNDPROC DialogWindowProc; HANDLE UploadThreadHandle; HICON IconLargeHandle; HICON IconSmallHandle; diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index 97d871d9c094..ab421a4a79b7 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1169,22 +1169,28 @@ NTSTATUS UploadRecheckThreadStart( return STATUS_SUCCESS; } -BOOLEAN CALLBACK TaskDialogSubclassProc( +LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Context; + PUPLOAD_CONTEXT context; + + context = PhGetWindowContext(hwndDlg, 0xF); + + if (!context) + return 0; switch (uMsg) { case WM_DESTROY: { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DialogWindowProc); + PhRemoveWindowContext(hwndDlg, 0xF); + TaskDialogFreeContext(context); - PhUnregisterWindowSubclass(hwndDlg, TaskDialogSubclassProc); } break; case UM_UPLOAD: @@ -1240,7 +1246,7 @@ BOOLEAN CALLBACK TaskDialogSubclassProc( break; } - return FALSE; + return CallWindowProc(context->DialogWindowProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -1274,7 +1280,9 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( } } - PhRegisterWindowSubclass(hwndDlg, TaskDialogSubclassProc, context); + context->DialogWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)TaskDialogSubclassProc); ShowVirusTotalUploadDialog(context); } diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 6e653d207597..dd16c542cc45 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -49,6 +49,7 @@ 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; @@ -620,16 +621,18 @@ VOID DrawWindowBorderForTargeting( } } -BOOLEAN CALLBACK MainWndSubclassProc( +LRESULT CALLBACK MainWndSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _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)) @@ -721,7 +724,7 @@ BOOLEAN CALLBACK MainWndSubclassProc( case PHAPP_ID_VIEW_ALWAYSONTOP: { // Let Process Hacker perform the default processing. - DefSubclassProc(hWnd, uMsg, wParam, lParam); + CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); // Query the settings. BOOLEAN isAlwaysOnTopEnabled = (BOOLEAN)PhGetIntegerSetting(L"MainWindowAlwaysOnTop"); @@ -737,7 +740,7 @@ BOOLEAN CALLBACK MainWndSubclassProc( case PHAPP_ID_UPDATEINTERVAL_VERYSLOW: { // Let Process Hacker perform the default processing. - DefSubclassProc(hWnd, uMsg, wParam, lParam); + CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); StatusBarUpdate(TRUE); } @@ -1305,10 +1308,10 @@ BOOLEAN CALLBACK MainWndSubclassProc( break; } - return FALSE; + return CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); DefaultWndProc: - return TRUE; + return DefWindowProc(hWnd, uMsg, wParam, lParam); } VOID NTAPI MainWindowShowingCallback( @@ -1323,7 +1326,9 @@ VOID NTAPI MainWindowShowingCallback( NULL, &LayoutPaddingCallbackRegistration ); - PhRegisterWindowSubclass(PhMainWndHandle, MainWndSubclassProc, NULL); + + MainWindowHookProc = (WNDPROC)GetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC); + SetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC, (LONG_PTR)MainWndSubclassProc); ToolbarLoadSettings(); ReBarLoadLayoutSettings(); diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 0570ee4c60ad..3b5245112115 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -755,18 +755,26 @@ NTSTATUS UpdateDownloadThread( return STATUS_SUCCESS; } -BOOLEAN NTAPI TaskDialogSubclassProc( +LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Context; + PPH_UPDATER_CONTEXT context = PhGetWindowContext(hwndDlg, UCHAR_MAX); + + if (!context) + return 0; switch (uMsg) { + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + PhRemoveWindowContext(hwndDlg, UCHAR_MAX); + } + break; case PH_SHOWDIALOG: { if (IsMinimized(hwndDlg)) @@ -777,11 +785,6 @@ BOOLEAN NTAPI TaskDialogSubclassProc( SetForegroundWindow(hwndDlg); } break; - case WM_DESTROY: - { - PhUnregisterWindowSubclass(hwndDlg, TaskDialogSubclassProc); - } - break; //case WM_PARENTNOTIFY: // { // if (wParam == WM_CREATE) @@ -823,8 +826,8 @@ BOOLEAN NTAPI TaskDialogSubclassProc( // } // break; } - - return FALSE; + + return CallWindowProc(context->DefaultWindowProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -846,11 +849,13 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( // 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 + // Create the Taskdialog icons. TaskDialogCreateIcons(context); - // Subclass the Taskdialog - PhRegisterWindowSubclass(hwndDlg, TaskDialogSubclassProc, context); + // 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); diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index ebc867f041a1..d7749ce02c92 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -85,6 +85,7 @@ typedef struct _PH_UPDATER_CONTEXT HICON IconLargeHandle; HWND DialogHandle; + WNDPROC DefaultWindowProc; ULONG ErrorCode; PPH_STRING SetupFilePath; diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 865fb60ba65c..33f4a2e09380 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -94,12 +94,11 @@ INT CALLBACK WepPropSheetProc( _In_ LPARAM lParam ); -BOOLEAN CALLBACK WepPropSheetWndProc( +LRESULT CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ); HPROPSHEETPAGE WepCommonCreatePage( @@ -360,7 +359,8 @@ static INT CALLBACK WepPropSheetProc( { HWND refreshButtonHandle; - PhRegisterWindowSubclass(hwndDlg, WepPropSheetWndProc, NULL); + PhSetWindowContext(hwndDlg, 0xF, (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC)); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc); // Hide the Cancel button. ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); @@ -377,19 +377,27 @@ static INT CALLBACK WepPropSheetProc( return 0; } -BOOLEAN CALLBACK WepPropSheetWndProc( +LRESULT CALLBACK WepPropSheetWndProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ PVOID Context + _In_ LPARAM lParam ) { + WNDPROC defaultPropSheetWindowProc = PhGetWindowContext(hwnd, 0xF); + + if (!defaultPropSheetWindowProc) + return 0; + switch (uMsg) { case WM_DESTROY: { - PhRemoveWindowContext(hwnd, 1); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)defaultPropSheetWindowProc); + PhRemoveWindowContext(hwnd, 0xF); + + if (PhGetWindowContext(hwnd, 1)) + PhRemoveWindowContext(hwnd, 1); } break; case WM_SHOWWINDOW: @@ -429,7 +437,7 @@ BOOLEAN CALLBACK WepPropSheetWndProc( break; } - return FALSE; + return CallWindowProc(defaultPropSheetWindowProc, hwnd, uMsg, wParam, lParam); } static HPROPSHEETPAGE WepCommonCreatePage( diff --git a/tools/peview/include/prpsh.h b/tools/peview/include/prpsh.h index 6857565c5061..14d8801f10a9 100644 --- a/tools/peview/include/prpsh.h +++ b/tools/peview/include/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 diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c index d6b471f28854..7d4751f39084 100644 --- a/tools/peview/pdbprp.c +++ b/tools/peview/pdbprp.c @@ -1040,6 +1040,9 @@ VOID CALLBACK PvSymbolTreeUpdateCallback( { ULONG i; + if (!Context->UpdateTimerHandle) + return; + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); PhAcquireQueuedLockExclusive(&SearchResultsLock); @@ -1121,10 +1124,16 @@ INT_PTR CALLBACK PvpSymbolsDlgProc( case WM_DESTROY: { if (context->UpdateTimerHandle) + { RtlDeleteTimer(context->TimerQueueHandle, context->UpdateTimerHandle, NULL); + context->UpdateTimerHandle = NULL; + } if (context->TimerQueueHandle) + { RtlDeleteTimerQueue(context->TimerQueueHandle); + context->TimerQueueHandle = NULL; + } PvDeleteSymbolTree(context); } diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 14fcba4fe066..10842ae18765 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -49,9 +49,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( @@ -148,15 +146,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); - PhSetWindowContext(hwndDlg, ULONG_MAX, propSheetContext); - SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, UCHAR_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PvpPropSheetWndProc); if (MinimumSize.left == -1) { @@ -181,19 +180,20 @@ PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( _In_ HWND hwnd ) { - return PhGetWindowContext(hwnd, ULONG_MAX); + 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 = PhGetWindowContext(hWnd, UCHAR_MAX); + + if (!propSheetContext) + return 0; switch (uMsg) { @@ -223,10 +223,15 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; case WM_NCDESTROY: { - RemoveWindowSubclass(hWnd, PvpPropSheetWndProc, uIdSubclass); + LRESULT result; + + result = CallWindowProc(propSheetContext->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)propSheetContext->DefaultWindowProc); + PhRemoveWindowContext(hWnd, UCHAR_MAX); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); + return result; } break; case WM_COMMAND: @@ -256,7 +261,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(propSheetContext->DefaultWindowProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( From f6e9f29598eea32ba52537d925ec5528b0783df9 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 04:23:29 +1100 Subject: [PATCH 765/839] BuildTools: update binaries --- tools/CustomBuildTool/Source Files/Build.cs | 30 ++++++++++-------- tools/CustomBuildTool/Source Files/Program.cs | 2 ++ .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 05ea621f630e..27cda72084db 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -257,11 +257,6 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse HEAD").Trim(); - Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildBranch, ConsoleColor.White); - Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.White); - 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(); @@ -277,8 +272,13 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (ShowBuildInfo && !GitExportBuild) { - Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildVersion + Environment.NewLine, ConsoleColor.White); + Program.PrintColorMessage("Branch: ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildBranch, ConsoleColor.Green, true); + Program.PrintColorMessage("Version: ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildVersion, ConsoleColor.Green, false); + Program.PrintColorMessage(" (", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.DarkYellow, false); + Program.PrintColorMessage(")" + Environment.NewLine, ConsoleColor.DarkGray, true); if (!BuildNightly && ShowLogInfo && File.Exists(GitExePath)) { @@ -292,6 +292,7 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo //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); } + } } @@ -299,10 +300,11 @@ public static void ShowBuildStats() { TimeSpan buildTime = DateTime.Now - TimeStart; - Console.WriteLine( - Environment.NewLine + "Build Time: " + - buildTime.Minutes + " minute(s), " + - buildTime.Seconds + " second(s)"); + 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() @@ -1040,14 +1042,16 @@ public static bool AppveyorUploadBuildFiles() //BuildOutputFolder + "\\processhacker-build-websetup.exe", BuildOutputFolder + "\\processhacker-build-setup.exe", BuildOutputFolder + "\\processhacker-build-bin.zip", - BuildOutputFolder + "\\processhacker-build-checksums.txt" + BuildOutputFolder + "\\processhacker-build-checksums.txt", + BuildOutputFolder + "\\processhacker-build-pdb.zip" }; string[] releaseFileArray = { //BuildOutputFolder + "\\processhacker-websetup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt", + BuildOutputFolder + "\\processhacker-" + BuildVersion + "-pdb.zip" }; if (!BuildNightly) diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 572f1a1d682c..82d1f4f7d1bb 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -228,6 +228,8 @@ public static void Main(string[] args) return; //if (!Build.BuildWebSetupExe()) // return; + if (!Build.BuildPdbZip()) + return; if (!Build.BuildSetupExe()) return; if (!Build.BuildChecksumsFile()) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 66b2a6015e56716d1ae01195b8cd4b1ef15531b9..2b5fcdef709a14d4ced37d19ebf0522d453cb0d1 100644 GIT binary patch delta 13787 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 delta 13774 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 D^Kk~x From dc203a1fa2ced798305e897e2f4ee1b9f44c8cfa Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 04:39:08 +1100 Subject: [PATCH 766/839] Fix crash --- phlib/extlv.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/phlib/extlv.c b/phlib/extlv.c index 0bed9ab7f6cf..2ca05cc32704 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -126,9 +126,8 @@ VOID PhSetExtendedListView( context->EnableRedraw = 1; context->Cursor = NULL; - PhSetWindowContext(hWnd, MAXBYTE, context); - context->OldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); + PhSetWindowContext(hWnd, MAXCHAR, context); SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PhpExtendedListViewWndProc); ExtendedListView_Init(hWnd); @@ -141,14 +140,22 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( _In_ LPARAM lParam ) { - PPH_EXTLV_CONTEXT context = PhGetWindowContext(hwnd, MAXBYTE); + 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)context->OldWndProc); - PhRemoveWindowContext(hwnd, MAXBYTE); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, MAXCHAR); PhFree(context); } break; @@ -469,7 +476,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( return TRUE; } - return CallWindowProc(context->OldWndProc, hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } /** From 8cfb5f794c7682c81dcb34ca9580c276d62a9ab6 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 04:40:40 +1100 Subject: [PATCH 767/839] Fix typo --- phlib/extlv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phlib/extlv.c b/phlib/extlv.c index 2ca05cc32704..35f442bea4d9 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -169,7 +169,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( { HWND headerHandle; - headerHandle = (HWND)CallWindowProc(context->OldWndProc, hwnd, LVM_GETHEADER, 0, 0); + headerHandle = (HWND)CallWindowProc(oldWndProc, hwnd, LVM_GETHEADER, 0, 0); if (header->hwndFrom == headerHandle) { @@ -357,7 +357,7 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( { if (i != column) { - if (CallWindowProc(context->OldWndProc, hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) + if (CallWindowProc(oldWndProc, hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) { availableWidth -= lvColumn.cx; } @@ -371,10 +371,10 @@ LRESULT CALLBACK PhpExtendedListViewWndProc( } if (availableWidth >= 40) - return CallWindowProc(context->OldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); + return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); } - return CallWindowProc(context->OldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, width); + return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, width); } break; case ELVM_SETCOMPAREFUNCTION: From f2d5edce93f3a29e0a17e220bfd9bc21afa24896 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 05:03:14 +1100 Subject: [PATCH 768/839] Fix commit 33e0ab1 --- ProcessHacker/memsrch.c | 11 +++++++---- ProcessHacker/procprp.c | 11 +++++------ ProcessHacker/searchbox.c | 13 ++++++++----- ProcessHacker/splitter.c | 14 +++++++++----- ProcessHacker/sysinfo.c | 11 ++++++----- plugins/HardwareDevices/prpsh.c | 11 +++++++---- plugins/OnlineChecks/upload.c | 11 ++++++----- plugins/Updater/updater.c | 13 ++++++++----- plugins/WindowExplorer/wndprp.c | 8 ++++---- tools/peview/prpsh.c | 15 +++++++-------- 10 files changed, 67 insertions(+), 51 deletions(-) diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index 9d665ab2cbe2..7e6a85d81fb8 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -658,16 +658,19 @@ LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( _In_ LPARAM lParam ) { - PMEMORY_STRING_CONTEXT context = PhGetWindowContext(hwndDlg, 0xF); + PMEMORY_STRING_CONTEXT context; + WNDPROC oldWndProc; - if (!context) + if (!(context = PhGetWindowContext(hwndDlg, 0xF))) return 0; + oldWndProc = context->DefaultWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwndDlg, 0xF); } break; @@ -686,7 +689,7 @@ LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( break; } - return CallWindowProc(context->DefaultWindowProc, hwndDlg, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 9273e4c19a71..8fde7b3904dd 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -206,12 +206,15 @@ LRESULT CALLBACK PhpPropSheetWndProc( ) { PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + WNDPROC oldWndProc; propSheetContext = PhGetWindowContext(hwnd, 0xF); if (!propSheetContext) return 0; + oldWndProc = propSheetContext->PropSheetWindowHookProc; + switch (uMsg) { case WM_DESTROY: @@ -240,15 +243,11 @@ LRESULT CALLBACK PhpPropSheetWndProc( break; case WM_NCDESTROY: { - LRESULT result; - - result = CallWindowProc(propSheetContext->PropSheetWindowHookProc, hwnd, uMsg, wParam, lParam); - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)propSheetContext->PropSheetWindowHookProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwnd, 0xF); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); - return result; } break; case WM_COMMAND: @@ -278,7 +277,7 @@ LRESULT CALLBACK PhpPropSheetWndProc( break; } - return CallWindowProc(propSheetContext->PropSheetWindowHookProc, hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c index 8c12ecef56cf..436e91e94d8f 100644 --- a/ProcessHacker/searchbox.c +++ b/ProcessHacker/searchbox.c @@ -254,20 +254,23 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( ) { PEDIT_CONTEXT context; + WNDPROC oldWndProc; if (!(context = PhGetWindowContext(hWnd, 10))) return 0; + oldWndProc = context->DefaultWindowProc; + switch (uMsg) { - case WM_NCDESTROY: + case WM_DESTROY: { PhpSearchFreeTheme(context); if (context->WindowFont) DeleteObject(context->WindowFont); - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hWnd, 10); PhFree(context); } @@ -279,7 +282,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( LPNCCALCSIZE_PARAMS ncCalcSize = (NCCALCSIZE_PARAMS*)lParam; // Let Windows handle the non-client defaults. - CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); // Deflate the client area to accommodate the custom button. ncCalcSize->rgrc[0].right -= context->CXWidth; @@ -290,7 +293,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( RECT windowRect; // Let Windows handle the non-client defaults. - CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); // Get the screen coordinates of the window. GetWindowRect(hWnd, &windowRect); @@ -482,7 +485,7 @@ LRESULT CALLBACK PhpSearchWndSubclassProc( break; } - return CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } HICON PhpSearchBitmapToIcon( diff --git a/ProcessHacker/splitter.c b/ProcessHacker/splitter.c index e6ce1a57abc9..74a9eb79a3e9 100644 --- a/ProcessHacker/splitter.c +++ b/ProcessHacker/splitter.c @@ -272,18 +272,22 @@ LRESULT CALLBACK HSplitterParentWindowProc( _In_ LPARAM lParam ) { - PPH_HSPLITTER_CONTEXT context = PhGetWindowContext(hwnd, 0x200); + PPH_HSPLITTER_CONTEXT context; + WNDPROC oldWndProc; - if (!context) + if (!(context = PhGetWindowContext(hwnd, 0x200))) return 0; + oldWndProc = context->DefaultWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); - PhDeleteHSplitter(context); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwnd, 0x200); + + PhDeleteHSplitter(context); } break; case WM_SIZE: @@ -293,7 +297,7 @@ LRESULT CALLBACK HSplitterParentWindowProc( break; } - return CallWindowProc(context->DefaultWindowProc, hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } VOID PhInitializeHSplitter( diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index c2fa5ecd4b6f..140af05c3af2 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -1731,17 +1731,18 @@ LRESULT CALLBACK PhSipGraphHookWndProc( ) { PPH_SYSINFO_SECTION section; + WNDPROC oldWndProc; - section = PhGetWindowContext(hwnd, 0xF); - - if (!section) + if (!(section = PhGetWindowContext(hwnd, 0xF))) return 0; + oldWndProc = section->GraphWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)section->GraphWindowProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwnd, 0xF); } break; @@ -1879,7 +1880,7 @@ LRESULT CALLBACK PhSipGraphHookWndProc( break; } - return CallWindowProc(section->GraphWindowProc, hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } LRESULT CALLBACK PhSipPanelHookWndProc( diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index d0d7d107b799..9da89afa7c3d 100644 --- a/plugins/HardwareDevices/prpsh.c +++ b/plugins/HardwareDevices/prpsh.c @@ -179,16 +179,19 @@ LRESULT CALLBACK PvpPropSheetWndProc( _In_ LPARAM lParam ) { - PPV_PROPSHEETCONTEXT context = PhGetWindowContext(hWnd, ULONG_MAX); + PPV_PROPSHEETCONTEXT context; + WNDPROC oldWndProc; - if (!context) + if (!(context = PhGetWindowContext(hWnd, ULONG_MAX))) return 0; + oldWndProc = context->DefaultWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hWnd, ULONG_MAX); PhSaveWindowPlacementToSetting(SETTING_NAME_DISK_POSITION, SETTING_NAME_DISK_SIZE, hWnd); @@ -224,7 +227,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return CallWindowProc(context->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index ab421a4a79b7..a8026d87ba8c 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1177,17 +1177,18 @@ LRESULT CALLBACK TaskDialogSubclassProc( ) { PUPLOAD_CONTEXT context; + WNDPROC oldWndProc; - context = PhGetWindowContext(hwndDlg, 0xF); - - if (!context) + if (!(context = PhGetWindowContext(hwndDlg, 0xF))) return 0; + oldWndProc = context->DialogWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DialogWindowProc); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwndDlg, 0xF); TaskDialogFreeContext(context); @@ -1246,7 +1247,7 @@ LRESULT CALLBACK TaskDialogSubclassProc( break; } - return CallWindowProc(context->DialogWindowProc, hwndDlg, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK TaskDialogBootstrapCallback( diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 3b5245112115..a448b8892a31 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -762,16 +762,19 @@ LRESULT CALLBACK TaskDialogSubclassProc( _In_ LPARAM lParam ) { - PPH_UPDATER_CONTEXT context = PhGetWindowContext(hwndDlg, UCHAR_MAX); + PPH_UPDATER_CONTEXT context; + WNDPROC oldWndProc; - if (!context) + if (!(context = PhGetWindowContext(hwndDlg, UCHAR_MAX))) return 0; + oldWndProc = context->DefaultWindowProc; + switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)context->DefaultWindowProc); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwndDlg, UCHAR_MAX); } break; @@ -826,8 +829,8 @@ LRESULT CALLBACK TaskDialogSubclassProc( // } // break; } - - return CallWindowProc(context->DefaultWindowProc, hwndDlg, uMsg, wParam, lParam); + + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK TaskDialogBootstrapCallback( diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index 33f4a2e09380..8c6aabc94e8e 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -384,16 +384,16 @@ LRESULT CALLBACK WepPropSheetWndProc( _In_ LPARAM lParam ) { - WNDPROC defaultPropSheetWindowProc = PhGetWindowContext(hwnd, 0xF); + WNDPROC oldWndProc; - if (!defaultPropSheetWindowProc) + if (!(oldWndProc = PhGetWindowContext(hwnd, 0xF))) return 0; switch (uMsg) { case WM_DESTROY: { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)defaultPropSheetWindowProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hwnd, 0xF); if (PhGetWindowContext(hwnd, 1)) @@ -437,7 +437,7 @@ LRESULT CALLBACK WepPropSheetWndProc( break; } - return CallWindowProc(defaultPropSheetWindowProc, hwnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); } static HPROPSHEETPAGE WepCommonCreatePage( diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 10842ae18765..7cda0029f125 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -190,11 +190,14 @@ LRESULT CALLBACK PvpPropSheetWndProc( _In_ LPARAM lParam ) { - PPV_PROPSHEETCONTEXT propSheetContext = PhGetWindowContext(hWnd, UCHAR_MAX); + PPV_PROPSHEETCONTEXT propSheetContext; + WNDPROC oldWndProc; - if (!propSheetContext) + if (!(propSheetContext = PhGetWindowContext(hWnd, UCHAR_MAX))) return 0; + oldWndProc = propSheetContext->DefaultWindowProc; + switch (uMsg) { case WM_DESTROY: @@ -223,15 +226,11 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; case WM_NCDESTROY: { - LRESULT result; - - result = CallWindowProc(propSheetContext->DefaultWindowProc, hWnd, uMsg, wParam, lParam); - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)propSheetContext->DefaultWindowProc); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); PhRemoveWindowContext(hWnd, UCHAR_MAX); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); - return result; } break; case WM_COMMAND: @@ -261,7 +260,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return CallWindowProc(propSheetContext->DefaultWindowProc, hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( From 237f5727a420ce231608490e7cf3ec5c38a95dda Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 05:53:58 +1100 Subject: [PATCH 769/839] Add ProcessErrorMode wrappers --- ProcessHacker/main.c | 50 ++++++++++++++++++++++++++++--------- phlib/include/phnativeinl.h | 31 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 517b191ec328..29122e1acb4c 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -75,6 +75,10 @@ VOID PhpEnablePrivileges( VOID ); +BOOLEAN PhInitializeExceptionPolicy( + VOID + ); + BOOLEAN PhInitializeMitigationPolicy( VOID ); @@ -115,14 +119,13 @@ INT WINAPI wWinMain( HANDLE currentTokenHandle; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); -#ifndef DEBUG - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); -#endif if (!NT_SUCCESS(PhInitializePhLibEx(ULONG_MAX, Instance, 0, 0))) return 1; - if (PhInitializeMitigationPolicy()) - return 0; + if (!PhInitializeExceptionPolicy()) + return 1; + if (!PhInitializeMitigationPolicy()) + return 1; if (!PhInitializeAppSystem()) return 1; @@ -562,13 +565,27 @@ VOID PhInitializeRestartPolicy( PhClearReference(&argumentsString); } +BOOLEAN PhInitializeExceptionPolicy( + VOID + ) +{ +#ifndef DEBUG + ULONG errorMode; + + if (NT_SUCCESS(PhGetProcessErrorMode(NtCurrentProcess(), &errorMode))) + { + errorMode &= ~(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + PhSetProcessErrorMode(NtCurrentProcess(), errorMode); + } +#endif + return TRUE; +} + BOOLEAN PhInitializeMitigationPolicy( VOID ) { -#ifdef DEBUG - return FALSE; -#else +#ifndef DEBUG #define DEFAULT_MITIGATION_POLICY_FLAGS \ (PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | \ PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | \ @@ -580,17 +597,20 @@ BOOLEAN PhInitializeMitigationPolicy( PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON) static PH_STRINGREF commandlinePart = PH_STRINGREF_INIT(L" -nomp"); - BOOLEAN success = FALSE; + 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); if (PhEndsWithStringRef(&commandlineSr, &commandlinePart, FALSE)) - goto CleanupExit; + return TRUE; if (!(LdrSystemDllInitBlock_I = PhGetModuleProcAddress(L"ntdll.dll", "LdrSystemDllInitBlock"))) goto CleanupExit; @@ -613,7 +633,8 @@ BOOLEAN PhInitializeMitigationPolicy( goto CleanupExit; commandline = PhConcatStringRef2(&commandlineSr, &commandlinePart); - success = NT_SUCCESS(PhCreateProcessWin32Ex( + + if (NT_SUCCESS(PhCreateProcessWin32Ex( NULL, PhGetString(commandline), NULL, @@ -624,7 +645,10 @@ BOOLEAN PhInitializeMitigationPolicy( NULL, NULL, NULL - )); + ))) + { + success = FALSE; + } CleanupExit: @@ -638,6 +662,8 @@ BOOLEAN PhInitializeMitigationPolicy( } return success; +#else + return TRUE; #endif } diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index c3bc53598e63..cd136d9d6d49 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -238,6 +238,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. * From 74c73d5d1fb4899e12d817379c005a838c4522da Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 07:39:26 +1100 Subject: [PATCH 770/839] Improve dynamic dbghelp initialization --- ProcessHacker/appsup.c | 84 ------------- ProcessHacker/include/appsup.h | 14 --- ProcessHacker/include/mainwndp.h | 5 - ProcessHacker/include/phsvc.h | 5 - ProcessHacker/include/phsvcapi.h | 12 +- ProcessHacker/include/phsvccl.h | 4 - ProcessHacker/main.c | 17 +-- ProcessHacker/mainwnd.c | 28 +---- ProcessHacker/mdump.c | 5 - ProcessHacker/phsvc/clapi.c | 67 +++++------ ProcessHacker/phsvc/svcapi.c | 24 ---- phlib/include/symprv.h | 8 -- phlib/symprv.c | 201 +++++++++++++++++++++---------- tools/peview/main.c | 26 ++++ tools/peview/peprp.c | 91 -------------- 15 files changed, 209 insertions(+), 382 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index b4d9388062fc..66413ad6e5cc 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -1007,89 +1006,6 @@ VOID PhShellExecuteUserString( PhDereferenceObject(executeString); } -PPH_STRING PhFindDbghelpPath( - VOID - ) -{ - static struct - { - ULONG Folder; - PWSTR AppendPath; - } locations[] = - { -#ifdef _WIN64 - { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILES, 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 PhLoadSymbolProviderDbgHelpFromPath( - _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 PhLoadSymbolProviderOptions( _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider ) diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 335339db4ce7..62038ebf4caf 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -159,20 +159,6 @@ PhShellExecuteUserString( _In_opt_ PWSTR ErrorMessage ); -PHAPPAPI -PPH_STRING -NTAPI -PhFindDbghelpPath( - VOID - ); - -PHAPPAPI -VOID -NTAPI -PhLoadSymbolProviderDbgHelpFromPath( - _In_ PWSTR DbgHelpPath - ); - PHAPPAPI VOID NTAPI diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 21351beb4ae3..14f212db7e8c 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -137,11 +137,6 @@ VOID PhMwpSaveWindowState( // Misc. -VOID PhMwpSymInitHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - VOID PhMwpUpdateLayoutPadding( VOID ); diff --git a/ProcessHacker/include/phsvc.h b/ProcessHacker/include/phsvc.h index d2f762541a4d..b35b87017bb1 100644 --- a/ProcessHacker/include/phsvc.h +++ b/ProcessHacker/include/phsvc.h @@ -225,11 +225,6 @@ NTSTATUS PhSvcApiSetServiceSecurity( _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 diff --git a/ProcessHacker/include/phsvcapi.h b/ProcessHacker/include/phsvcapi.h index cb9458073036..ad6c9105e80b 100644 --- a/ProcessHacker/include/phsvcapi.h +++ b/ProcessHacker/include/phsvcapi.h @@ -23,8 +23,7 @@ typedef enum _PHSVC_API_NUMBER PhSvcSendMessageApiNumber = 15, PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber = 16, PhSvcSetServiceSecurityApiNumber = 17, - PhSvcLoadDbgHelpApiNumber = 18, // WOW64 compatible - PhSvcWriteMiniDumpProcessApiNumber = 19, // WOW64 compatible + PhSvcWriteMiniDumpProcessApiNumber = 18, // WOW64 compatible PhSvcMaximumApiNumber } PHSVC_API_NUMBER, *PPHSVC_API_NUMBER; @@ -241,14 +240,6 @@ typedef union _PHSVC_API_SETSERVICESECURITY } 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 @@ -285,7 +276,6 @@ typedef union _PHSVC_API_PAYLOAD PHSVC_API_POSTMESSAGE PostMessage; PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER CreateProcessIgnoreIfeoDebugger; PHSVC_API_SETSERVICESECURITY SetServiceSecurity; - PHSVC_API_LOADDBGHELP LoadDbgHelp; PHSVC_API_WRITEMINIDUMPPROCESS WriteMiniDumpProcess; } u; }; diff --git a/ProcessHacker/include/phsvccl.h b/ProcessHacker/include/phsvccl.h index 93fe474f2cf2..94edd38987e5 100644 --- a/ProcessHacker/include/phsvccl.h +++ b/ProcessHacker/include/phsvccl.h @@ -141,10 +141,6 @@ NTSTATUS PhSvcCallSetServiceSecurity( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor ); -NTSTATUS PhSvcCallLoadDbgHelp( - _In_ PWSTR DbgHelpPath - ); - NTSTATUS PhSvcCallWriteMiniDumpProcess( _In_ HANDLE ProcessHandle, _In_ HANDLE ProcessId, diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 29122e1acb4c..e2afe98cf44e 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -83,7 +83,7 @@ BOOLEAN PhInitializeMitigationPolicy( VOID ); -VOID PhInitializeRestartPolicy( +BOOLEAN PhInitializeRestartPolicy( VOID ); @@ -126,6 +126,8 @@ INT WINAPI wWinMain( return 1; if (!PhInitializeMitigationPolicy()) return 1; + if (!PhInitializeRestartPolicy()) + return 1; if (!PhInitializeAppSystem()) return 1; @@ -313,9 +315,6 @@ INT WINAPI wWinMain( PhSetProcessPriority(NtCurrentProcess(), priorityClass); } - // Create the restart policy. - PhInitializeRestartPolicy(); - if (!PhMainWndInitialization(CmdShow)) { PhShowError(NULL, L"Unable to initialize the main window."); @@ -535,10 +534,11 @@ VOID PhInitializeFont( } } -VOID PhInitializeRestartPolicy( +BOOLEAN PhInitializeRestartPolicy( VOID ) { +#ifndef DEBUG PH_STRINGREF commandLineSr; PH_STRINGREF fileNameSr; PH_STRINGREF argumentsSr; @@ -547,7 +547,7 @@ VOID PhInitializeRestartPolicy( PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLineSr); if (!PhParseCommandLineFuzzy(&commandLineSr, &fileNameSr, &argumentsSr, NULL)) - return; + return FALSE; if (argumentsSr.Length) { @@ -562,7 +562,10 @@ VOID PhInitializeRestartPolicy( // MSDN: Do not include the file name in the command line. RegisterApplicationRestart(PhGetString(argumentsString), 0); - PhClearReference(&argumentsString); + if (argumentsString) + PhDereferenceObject(argumentsString); +#endif + return TRUE; } BOOLEAN PhInitializeExceptionPolicy( diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 4dda48ddfb67..65c5e9ce78d9 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -104,22 +103,17 @@ BOOLEAN PhMainWndInitialization( 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(PhFindDbghelpPath())) - PhSetStringSetting2(L"DbgHelpPath", &autoDbghelpPath->sr); + // Set FirstRun default settings. + if (PhGetIntegerSetting(L"FirstRun")) PhSetIntegerSetting(L"FirstRun", FALSE); - } - // This was added to be able to delay-load dbghelp.dll and symsrv.dll. - PhRegisterCallback(&PhSymInitCallback, PhMwpSymInitHandler, NULL, &SymInitRegistration); + // Initialize the main providers. PhMwpInitializeProviders(); + // Initialize the window. + if ((windowAtom = PhMwpInitializeWindowClass()) == INVALID_ATOM) return FALSE; @@ -2214,18 +2208,6 @@ VOID PhMwpSaveWindowState( PhSetIntegerSetting(L"MainWindowState", SW_MAXIMIZE); } -VOID PhMwpSymInitHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_STRING dbghelpPath; - - dbghelpPath = PhGetStringSetting(L"DbgHelpPath"); - PhLoadSymbolProviderDbgHelpFromPath(dbghelpPath->Buffer); - PhDereferenceObject(dbghelpPath); -} - VOID PhMwpUpdateLayoutPadding( VOID ) diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index f923a0f01ee4..549b549bbba5 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -234,11 +234,6 @@ NTSTATUS PhpProcessMiniDumpThreadStart( 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, diff --git a/ProcessHacker/phsvc/clapi.c b/ProcessHacker/phsvc/clapi.c index 8863149b2333..8afb38cf4cad 100644 --- a/ProcessHacker/phsvc/clapi.c +++ b/ProcessHacker/phsvc/clapi.c @@ -1128,33 +1128,6 @@ NTSTATUS PhSvcCallSetServiceSecurity( 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, @@ -1178,22 +1151,40 @@ NTSTATUS PhSvcCallWriteMiniDumpProcess( m.p.ApiNumber = PhSvcWriteMiniDumpProcessApiNumber; - if (!NT_SUCCESS(status = PhOpenProcess(&serverHandle, PROCESS_DUP_HANDLE, PhSvcClServerProcessId))) - { + status = PhOpenProcess( + &serverHandle, + PROCESS_DUP_HANDLE, + PhSvcClServerProcessId + ); + + if (!NT_SUCCESS(status)) goto CleanupExit; - } - if (!NT_SUCCESS(status = NtDuplicateObject(NtCurrentProcess(), ProcessHandle, serverHandle, &remoteProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0))) - { + status = NtDuplicateObject( + NtCurrentProcess(), + ProcessHandle, + serverHandle, + &remoteProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) goto CleanupExit; - } - if (!NT_SUCCESS(status = NtDuplicateObject(NtCurrentProcess(), FileHandle, serverHandle, &remoteFileHandle, - FILE_GENERIC_WRITE, 0, 0))) - { + 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); diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index 9f8ded087e2e..dda20bdde1a8 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -63,7 +63,6 @@ PPHSVC_API_PROCEDURE PhSvcApiCallTable[] = PhSvcApiSendMessage, PhSvcApiCreateProcessIgnoreIfeoDebugger, PhSvcApiSetServiceSecurity, - PhSvcApiLoadDbgHelp, PhSvcApiWriteMiniDumpProcess }; C_ASSERT(sizeof(PhSvcApiCallTable) / sizeof(PPHSVC_API_PROCEDURE) == PhSvcMaximumApiNumber - 1); @@ -1392,29 +1391,6 @@ NTSTATUS PhSvcApiSetServiceSecurity( 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); - PhLoadSymbolProviderDbgHelpFromPath(dbgHelpPath->Buffer); - alreadyLoaded = TRUE; - } - - return status; -} - NTSTATUS PhSvcApiWriteMiniDumpProcess( _In_ PPHSVC_CLIENT Client, _Inout_ PPHSVC_API_PAYLOAD Payload diff --git a/phlib/include/symprv.h b/phlib/include/symprv.h index f0a83df00d5d..dfa8a2900bd5 100644 --- a/phlib/include/symprv.h +++ b/phlib/include/symprv.h @@ -6,7 +6,6 @@ extern "C" { #endif extern PPH_OBJECT_TYPE PhSymbolProviderType; -extern PH_CALLBACK PhSymInitCallback; #define PH_MAX_SYMBOL_NAME_LEN 128 @@ -72,13 +71,6 @@ PhSymbolProviderInitialization( VOID ); -PHLIBAPI -VOID -NTAPI -PhSymbolProviderCompleteInitialization( - _In_opt_ PVOID DbgHelpBase - ); - PHLIBAPI PPH_SYMBOL_PROVIDER NTAPI diff --git a/phlib/symprv.c b/phlib/symprv.c index 04bb84099eec..93f32b500d35 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -3,6 +3,7 @@ * symbol provider * * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -24,6 +25,7 @@ #include #include +#include #include #include @@ -60,13 +62,9 @@ LONG NTAPI PhpSymbolModuleCompareFunction( ); 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) @@ -108,64 +106,6 @@ BOOLEAN PhSymbolProviderInitialization( return TRUE; } -VOID PhSymbolProviderCompleteInitialization( - _In_opt_ PVOID DbgHelpBase - ) -{ - PVOID dbghelpHandle; - PVOID 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 = PhGetDllHandle(L"dbghelp.dll"); - - symsrvHandle = PhGetDllHandle(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); - UnDecorateSymbolName_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolName", 0); - UnDecorateSymbolNameW_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolNameW", 0); - - if (SymGetOptions_I && SymSetOptions_I) - { - 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 - ); - } -} - PPH_SYMBOL_PROVIDER PhCreateSymbolProvider( _In_opt_ HANDLE ProcessId ) @@ -309,13 +249,148 @@ BOOL CALLBACK PhpSymbolCallbackFunction( return FALSE; } +VOID PhpSymbolProviderCompleteInitialization( + VOID + ) +{ + static struct + { + ULONG Folder; + PWSTR AppendPath; + } locations[] = + { +#ifdef _WIN64 + { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll" }, + { CSIDL_PROGRAM_FILES, 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 + }; + + PVOID dbghelpHandle; + PVOID symsrvHandle; + PPH_STRING dbghelpPath = NULL; + + dbghelpHandle = PhGetDllHandle(L"dbghelp.dll"); + symsrvHandle = PhGetDllHandle(L"symsrv.dll"); + + if (dbghelpHandle && symsrvHandle) + return; + + for (ULONG i = 0; i < ARRAYSIZE(locations); i++) + { + if (dbghelpPath = PhGetKnownLocation(locations[i].Folder, locations[i].AppendPath)) + { + if (RtlDoesFileExists_U(dbghelpPath->Buffer)) + break; + + PhClearReference(&dbghelpPath); + } + } + + if (dbghelpPath) + { + if (dbghelpHandle = LoadLibrary(dbghelpPath->Buffer)) + { + PPH_STRING fullDbghelpPath; + ULONG indexOfFileName; + PH_STRINGREF dbghelpFolder; + PPH_STRING symsrvPath; + + if (fullDbghelpPath = PhGetDllFileName(dbghelpHandle, &indexOfFileName)) + { + 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); + symsrvHandle = LoadLibrary(symsrvPath->Buffer); + PhDereferenceObject(symsrvPath); + } + + PhDereferenceObject(fullDbghelpPath); + } + } + + PhDereferenceObject(dbghelpPath); + } + + if (!dbghelpHandle) + dbghelpHandle = LoadLibrary(L"dbghelp.dll"); + + if (!symsrvHandle) + symsrvHandle = LoadLibrary(L"symsrv.dll"); + + if (dbghelpHandle) + { + // The Unicode versions aren't available in dbghelp.dll 5.1, so we fallback on the ANSI versions. + + 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); + UnDecorateSymbolName_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolName", 0); + UnDecorateSymbolNameW_I = PhGetProcedureAddress(dbghelpHandle, "UnDecorateSymbolNameW", 0); + } + + if (symsrvHandle) + { + SymbolServerGetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerGetOptions", 0); + SymbolServerSetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerSetOptions", 0); + } +} + VOID PhpRegisterSymbolProvider( _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider ) { if (PhBeginInitOnce(&PhSymInitOnce)) { - PhInvokeCallback(&PhSymInitCallback, NULL); + 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 + ); + + PH_UNLOCK_SYMBOLS(); + } + PhEndInitOnce(&PhSymInitOnce); } diff --git a/tools/peview/main.c b/tools/peview/main.c index a0a3e54ef6a5..73988a910c25 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -55,6 +55,32 @@ INT WINAPI wWinMain( if (!NT_SUCCESS(PhInitializePhLib())) return 1; + // Create a mutant for the installer. + { + HANDLE mutantHandle; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING mutantName; + PPH_STRING objectName; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PeViewer_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 2, 16); + PhStringRefToUnicodeString(&objectName->sr, &mutantName); + + InitializeObjectAttributes( + &oa, + &mutantName, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), + NULL + ); + + NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + PhDereferenceObject(objectName); + } + PhGuiSupportInitialization(); PhSettingsInitialization(); PeInitializeSettings(); diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 265271a3a3ce..b448a5938bc6 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -655,98 +655,12 @@ INT_PTR CALLBACK PvpPeGeneralDlgProc( return FALSE; } -VOID PvpLoadDbgHelpFromPath( - _In_ PWSTR DbgHelpPath - ) -{ - HMODULE dbghelpModule; - - if (DbgHelpPath && (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); -} - -PPH_STRING PhFindDbghelpPath( - 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; -} - - BOOLEAN PvpLoadDbgHelp( _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider ) { static UNICODE_STRING symbolPathVarName = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); PPH_STRING symbolSearchPath; - PPH_STRING dbgHelpPath; PPH_SYMBOL_PROVIDER symbolProvider; UNICODE_STRING symbolPathUs; WCHAR buffer[512]; @@ -756,8 +670,6 @@ BOOLEAN PvpLoadDbgHelp( if (!PhSymbolProviderInitialization()) return FALSE; - dbgHelpPath = PhFindDbghelpPath(); - PvpLoadDbgHelpFromPath(PhGetString(dbgHelpPath)); symbolProvider = PhCreateSymbolProvider(NULL); // Load symbol path from _NT_SYMBOL_PATH if configured by the user. @@ -774,9 +686,6 @@ BOOLEAN PvpLoadDbgHelp( PhSetSearchPathSymbolProvider(symbolProvider, symbolSearchPath->Buffer); PhDereferenceObject(symbolSearchPath); - if (dbgHelpPath) - PhDereferenceObject(dbgHelpPath); - *SymbolProvider = symbolProvider; return TRUE; } \ No newline at end of file From 5ed252623ff7c398baa745c7da03194b6d6ed069 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 09:25:41 +1100 Subject: [PATCH 771/839] BuildTools: Update build log format --- tools/CustomBuildTool/Source Files/Build.cs | 7 ++++--- tools/CustomBuildTool/Source Files/Program.cs | 6 +++--- .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 75264 bytes 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 27cda72084db..c30c120f55fd 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -278,14 +278,15 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo Program.PrintColorMessage(BuildVersion, ConsoleColor.Green, false); Program.PrintColorMessage(" (", ConsoleColor.DarkGray, false); Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.DarkYellow, false); - Program.PrintColorMessage(")" + Environment.NewLine, ConsoleColor.DarkGray, true); + Program.PrintColorMessage(")", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(Environment.NewLine, ConsoleColor.DarkGray, true); 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(yellow)%h%Creset)\" --abbrev-commit"); + 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"); @@ -377,7 +378,7 @@ public static bool CopyLibFiles(BuildFlags Flags) public static bool CopyWow64Files(BuildFlags Flags) { - Program.PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); + //Program.PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); try { diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 82d1f4f7d1bb..2d089279e9e2 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -32,7 +32,7 @@ public static class Program private static bool BuildSdk(BuildFlags Flags) { - PrintColorMessage("Copying Plugin SDK...", ConsoleColor.Cyan); + //PrintColorMessage("Copying Plugin SDK...", ConsoleColor.Cyan); if (!Build.CopyTextFiles()) return false; @@ -228,8 +228,8 @@ public static void Main(string[] args) return; //if (!Build.BuildWebSetupExe()) // return; - if (!Build.BuildPdbZip()) - return; + //if (!Build.BuildPdbZip()) + // return; if (!Build.BuildSetupExe()) return; if (!Build.BuildChecksumsFile()) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 2b5fcdef709a14d4ced37d19ebf0522d453cb0d1..fd6f7537e90a034be99c58d475746b45ed3e9764 100644 GIT binary patch delta 8733 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 74e8fe35eb15763cc893ebd651eae19c1e9eb5d5..e9427627252d9655f9e3aaba42f6942b4d5b35f8 100644 GIT binary patch delta 9824 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 From d304fcdd0416c144af4b44c1fbcd3d319c0c69fa Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 10:35:04 +1100 Subject: [PATCH 772/839] Update process token tab layout --- ProcessHacker/ProcessHacker.rc | 16 +- ProcessHacker/resource.h | 2 - ProcessHacker/tokprp.c | 337 +++++++++++++++++++++------------ 3 files changed, 216 insertions(+), 139 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index b3f3f753654d..030c715f4038 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -328,18 +328,6 @@ BEGIN 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" @@ -737,9 +725,7 @@ BEGIN 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,112 - CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,168,246,57 - LTEXT "To view capabilities, claims and other attributes, click Advanced.",IDC_INSTRUCTION,7,228,206,8 + CONTROL "",IDC_GROUPS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,53,246,183 PUSHBUTTON "Integrity",IDC_INTEGRITY,149,239,50,14 PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14 END diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 80e0ffeb0da9..8727dc0e5325 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -39,7 +39,6 @@ #define IDD_FINDOBJECTS 130 #define IDD_OBJTOKEN 131 #define ID_PLUGIN_MENU_ITEM 131 -#define IDR_PRIVILEGE 133 #define IDC_SEPARATOR 133 #define IDR_FINDOBJ 134 #define IDD_HIDDENPROCESSES 135 @@ -188,7 +187,6 @@ #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 diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 85de914d2542..05b19f7160e3 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -32,6 +32,29 @@ #include #include +typedef enum _PH_PROCESS_TOKEN_CATEGORY +{ + PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, + PH_PROCESS_TOKEN_CATEGORY_GROUPS +} PH_PROCESS_TOKEN_CATEGORY; + +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 +{ + BOOLEAN IsTokenGroupEntry; + union + { + PSID_AND_ATTRIBUTES TokenGroup; + PLUID_AND_ATTRIBUTES TokenPrivilege; + }; +} PHP_TOKEN_PAGE_LISTVIEW_ITEM, *PPHP_TOKEN_PAGE_LISTVIEW_ITEM; + typedef struct _ATTRIBUTE_NODE { PH_TREENEW_NODE Node; @@ -51,8 +74,8 @@ typedef struct _TOKEN_PAGE_CONTEXT PVOID Context; DLGPROC HookProc; - HWND GroupsListViewHandle; - HWND PrivilegesListViewHandle; + HWND ListViewHandle; + HIMAGELIST ListViewImageList; PTOKEN_GROUPS Groups; PTOKEN_GROUPS RestrictedSids; @@ -285,15 +308,30 @@ COLORREF PhGetGroupAttributesColor( return RGB(0xf0, 0xe0, 0xe0); } +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 PhpTokenGroupColorFunction( _In_ INT Index, _In_ PVOID Param, _In_opt_ PVOID Context ) { - PSID_AND_ATTRIBUTES sidAndAttributes = Param; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM entry = Param; - return PhGetGroupAttributesColor(sidAndAttributes->Attributes); + if (entry->IsTokenGroupEntry) + return PhGetGroupAttributesColor(entry->TokenGroup->Attributes); + else + return PhGetPrivilegeAttributesColor(entry->TokenPrivilege->Attributes); } PWSTR PhGetPrivilegeAttributesString( @@ -308,29 +346,6 @@ PWSTR PhGetPrivilegeAttributesString( 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 ) @@ -346,8 +361,29 @@ PWSTR PhGetElevationTypeString( } } +VOID PhpTokenPageFreeListViewEntries( + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext + ) +{ + ULONG index = -1; + + while ((index = PhFindListViewItemByFlags( + TokenPageContext->ListViewHandle, + index, + LVNI_ALL + )) != -1) + { + PPHP_TOKEN_PAGE_LISTVIEW_ITEM entry; + + if (PhGetListViewItemParam(TokenPageContext->ListViewHandle, index, &entry)) + { + PhFree(entry); + } + } +} + VOID PhpUpdateSidsFromTokenGroups( - _In_ HWND GroupsLv, + _In_ HWND ListViewHandle, _In_ PTOKEN_GROUPS Groups, _In_ BOOLEAN Restricted ) @@ -365,9 +401,23 @@ VOID PhpUpdateSidsFromTokenGroups( if (fullName) { - lvItemIndex = PhAddListViewItem(GroupsLv, MAXINT, fullName->Buffer, &Groups->Groups[i]); + PPHP_TOKEN_PAGE_LISTVIEW_ITEM lvitem; + + lvitem = PhAllocate(sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + memset(lvitem, 0, sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + + lvitem->IsTokenGroupEntry = TRUE; + lvitem->TokenGroup = &Groups->Groups[i]; + + lvItemIndex = PhAddListViewGroupItem( + ListViewHandle, + PH_PROCESS_TOKEN_CATEGORY_GROUPS, + PH_PROCESS_TOKEN_INDEX_NAME, + fullName->Buffer, + lvitem + ); attributesString = PhGetGroupAttributesString(Groups->Groups[i].Attributes, Restricted); - PhSetListViewSubItem(GroupsLv, lvItemIndex, 1, attributesString->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, attributesString->Buffer); PhDereferenceObject(attributesString); PhDereferenceObject(fullName); @@ -378,7 +428,7 @@ VOID PhpUpdateSidsFromTokenGroups( BOOLEAN PhpUpdateTokenGroups( _In_ HWND hwndDlg, _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, - _In_ HWND GroupsLv, + _In_ HWND ListViewHandle, _In_ HANDLE TokenHandle ) { @@ -388,19 +438,13 @@ BOOLEAN PhpUpdateTokenGroups( if (!NT_SUCCESS(PhGetTokenGroups(TokenHandle, &groups))) return FALSE; - ExtendedListView_SetRedraw(GroupsLv, FALSE); - ListView_DeleteAllItems(GroupsLv); - - PhpUpdateSidsFromTokenGroups(GroupsLv, groups, FALSE); + PhpUpdateSidsFromTokenGroups(ListViewHandle, groups, FALSE); if (NT_SUCCESS(PhGetTokenRestrictedSids(TokenHandle, &restrictedSIDs))) { - PhpUpdateSidsFromTokenGroups(GroupsLv, restrictedSIDs, TRUE); + PhpUpdateSidsFromTokenGroups(ListViewHandle, restrictedSIDs, TRUE); } - ExtendedListView_SortItems(GroupsLv); - ExtendedListView_SetRedraw(GroupsLv, TRUE); - if (TokenPageContext->RestrictedSids) PhFree(TokenPageContext->RestrictedSids); @@ -417,7 +461,7 @@ BOOLEAN PhpUpdateTokenGroups( BOOLEAN PhpUpdateTokenPrivileges( _In_ HWND hwndDlg, _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, - _In_ HWND PrivilegesLv, + _In_ HWND ListViewHandle, _In_ HANDLE TokenHandle ) { @@ -427,9 +471,6 @@ BOOLEAN PhpUpdateTokenPrivileges( if (!NT_SUCCESS(PhGetTokenPrivileges(TokenHandle, &privileges))) return FALSE; - ExtendedListView_SetRedraw(PrivilegesLv, FALSE); - ListView_DeleteAllItems(PrivilegesLv); - for (i = 0; i < privileges->PrivilegeCount; i++) { INT lvItemIndex; @@ -441,24 +482,34 @@ BOOLEAN PhpUpdateTokenPrivileges( &privilegeName )) { + PPHP_TOKEN_PAGE_LISTVIEW_ITEM lvitem; + + lvitem = PhAllocate(sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + memset(lvitem, 0, sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + + lvitem->TokenPrivilege = &privileges->Privileges[i]; + privilegeDisplayName = NULL; PhLookupPrivilegeDisplayName(&privilegeName->sr, &privilegeDisplayName); // Name - lvItemIndex = PhAddListViewItem(PrivilegesLv, MAXINT, privilegeName->Buffer, &privileges->Privileges[i]); + lvItemIndex = PhAddListViewGroupItem( + ListViewHandle, + PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, + PH_PROCESS_TOKEN_INDEX_NAME, + privilegeName->Buffer, + lvitem + ); // Status - PhSetListViewSubItem(PrivilegesLv, lvItemIndex, 1, PhGetPrivilegeAttributesString(privileges->Privileges[i].Attributes)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, PhGetPrivilegeAttributesString(privileges->Privileges[i].Attributes)); // Description - PhSetListViewSubItem(PrivilegesLv, lvItemIndex, 2, PhGetString(privilegeDisplayName)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_DESCRIPTION, PhGetString(privilegeDisplayName)); if (privilegeDisplayName) PhDereferenceObject(privilegeDisplayName); PhDereferenceObject(privilegeName); } } - ExtendedListView_SortItems(PrivilegesLv); - ExtendedListView_SetRedraw(PrivilegesLv, TRUE); - if (TokenPageContext->Privileges) PhFree(TokenPageContext->Privileges); @@ -501,30 +552,25 @@ INT_PTR CALLBACK PhpTokenPageProc( { 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"); + tokenPageContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_GROUPS); + tokenPageContext->ListViewImageList = ImageList_Create(2, 20, ILC_COLOR, 1, 1); - PhAddListViewColumn(groupsLv, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); - PhAddListViewColumn(groupsLv, 1, 1, 1, LVCFMT_LEFT, 200, L"Flags"); + 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"); - 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(tokenPageContext->ListViewHandle); + ExtendedListView_SetItemColorFunction(tokenPageContext->ListViewHandle, PhpTokenGroupColorFunction); + PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); - PhSetExtendedListView(groupsLv); - ExtendedListView_SetItemColorFunction(groupsLv, PhpTokenGroupColorFunction); - PhSetExtendedListView(privilegesLv); - ExtendedListView_SetItemColorFunction(privilegesLv, PhpTokenPrivilegeColorFunction); - PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", groupsLv); - PhLoadListViewColumnsFromSetting(L"TokenPrivilegesListViewColumns", privilegesLv); + ListView_EnableGroupView(tokenPageContext->ListViewHandle, TRUE); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, L"Privileges"); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_GROUPS, L"Groups"); + ListView_SetImageList(tokenPageContext->ListViewHandle, tokenPageContext->ListViewImageList, LVSIL_SMALL); SetDlgItemText(hwndDlg, IDC_USER, L"Unknown"); SetDlgItemText(hwndDlg, IDC_USERSID, L"Unknown"); @@ -615,30 +661,26 @@ INT_PTR CALLBACK PhpTokenPageProc( SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"N/A"); } - // Groups - PhpUpdateTokenGroups(hwndDlg, tokenPageContext, groupsLv, tokenHandle); - - // Privileges - PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, privilegesLv, tokenHandle); + 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 (PhGetIntegerSetting(L"TokenSplitterEnable")) - { - PhInitializeHSplitter( - L"TokenSplitterPosition", - hwndDlg, - tokenPageContext->GroupsListViewHandle, - tokenPageContext->PrivilegesListViewHandle - ); - } } break; case WM_DESTROY: { - PhSaveListViewColumnsToSetting(L"TokenGroupsListViewColumns", tokenPageContext->GroupsListViewHandle); - PhSaveListViewColumnsToSetting(L"TokenPrivilegesListViewColumns", tokenPageContext->PrivilegesListViewHandle); + if (tokenPageContext->ListViewImageList) + ImageList_Destroy(tokenPageContext->ListViewImageList); + + PhpTokenPageFreeListViewEntries(tokenPageContext); + + PhSaveListViewColumnsToSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); if (tokenPageContext->RestrictedSids) PhFree(tokenPageContext->RestrictedSids); @@ -654,11 +696,33 @@ INT_PTR CALLBACK PhpTokenPageProc( case ID_PRIVILEGE_REMOVE: { NTSTATUS status; - PLUID_AND_ATTRIBUTES *privileges; - ULONG numberOfPrivileges; + BOOLEAN listViewGroupItemsValid = FALSE; + 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]->IsTokenGroupEntry) + { + listViewGroupItemsValid = TRUE; + break; + } + } + + if (listViewGroupItemsValid) + { + PhFree(listViewItems); + break; + } + if (LOWORD(wParam) == ID_PRIVILEGE_REMOVE) { if (!PhShowConfirmMessage( @@ -672,12 +736,6 @@ INT_PTR CALLBACK PhpTokenPageProc( break; } - PhGetSelectedListViewItemParams( - tokenPageContext->PrivilegesListViewHandle, - &privileges, - &numberOfPrivileges - ); - status = tokenPageContext->OpenObject( &tokenHandle, TOKEN_ADJUST_PRIVILEGES, @@ -686,14 +744,14 @@ INT_PTR CALLBACK PhpTokenPageProc( if (NT_SUCCESS(status)) { - ExtendedListView_SetRedraw(tokenPageContext->PrivilegesListViewHandle, FALSE); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); - for (i = 0; i < numberOfPrivileges; i++) + for (i = 0; i < numberOfItems; i++) { PPH_STRING privilegeName = NULL; ULONG newAttributes; - PhLookupPrivilegeName(&privileges[i]->Luid, &privilegeName); + PhLookupPrivilegeName(&listViewItems[i]->TokenPrivilege->Luid, &privilegeName); PH_AUTO(privilegeName); switch (LOWORD(wParam)) @@ -713,7 +771,7 @@ INT_PTR CALLBACK PhpTokenPageProc( // modified except to remove them. if ( - privileges[i]->Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT && + listViewItems[i]->TokenPrivilege->Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT && LOWORD(wParam) != ID_PRIVILEGE_REMOVE ) { @@ -734,32 +792,31 @@ INT_PTR CALLBACK PhpTokenPageProc( if (PhSetTokenPrivilege( tokenHandle, NULL, - &privileges[i]->Luid, + &listViewItems[i]->TokenPrivilege->Luid, newAttributes )) { INT lvItemIndex = PhFindListViewItemByParam( - tokenPageContext->PrivilegesListViewHandle, + tokenPageContext->ListViewHandle, -1, - privileges[i] + listViewItems[i] ); if (LOWORD(wParam) != ID_PRIVILEGE_REMOVE) { - // Refresh the status text (and background - // color). - privileges[i]->Attributes = newAttributes; + // Refresh the status text (and background color). + listViewItems[i]->TokenPrivilege->Attributes = newAttributes; PhSetListViewSubItem( - tokenPageContext->PrivilegesListViewHandle, + tokenPageContext->ListViewHandle, lvItemIndex, - 1, + PH_PROCESS_TOKEN_INDEX_STATUS, PhGetPrivilegeAttributesString(newAttributes) ); } else { ListView_DeleteItem( - tokenPageContext->PrivilegesListViewHandle, + tokenPageContext->ListViewHandle, lvItemIndex ); } @@ -791,7 +848,7 @@ INT_PTR CALLBACK PhpTokenPageProc( } } - ExtendedListView_SetRedraw(tokenPageContext->PrivilegesListViewHandle, TRUE); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); NtClose(tokenHandle); } @@ -800,14 +857,14 @@ INT_PTR CALLBACK PhpTokenPageProc( PhShowStatus(hwndDlg, L"Unable to open the token", status, 0); } - PhFree(privileges); + PhFree(listViewItems); - ExtendedListView_SortItems(tokenPageContext->PrivilegesListViewHandle); + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); } break; case ID_PRIVILEGE_COPY: { - PhCopyListView(tokenPageContext->PrivilegesListViewHandle); + PhCopyListView(tokenPageContext->ListViewHandle); } break; case IDC_INTEGRITY: @@ -822,7 +879,6 @@ INT_PTR CALLBACK PhpTokenPageProc( 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); @@ -920,8 +976,13 @@ INT_PTR CALLBACK PhpTokenPageProc( if (NT_SUCCESS(status)) { - PhpUpdateTokenGroups(hwndDlg, tokenPageContext, tokenPageContext->GroupsListViewHandle, tokenHandle); - PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, tokenPageContext->PrivilegesListViewHandle, tokenHandle); + 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); @@ -957,13 +1018,12 @@ INT_PTR CALLBACK PhpTokenPageProc( break; } - PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->GroupsListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->PrivilegesListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); } break; case WM_CONTEXTMENU: { - if ((HWND)wParam == tokenPageContext->PrivilegesListViewHandle) + if ((HWND)wParam == tokenPageContext->ListViewHandle) { POINT point; @@ -973,23 +1033,56 @@ INT_PTR CALLBACK PhpTokenPageProc( if (point.x == -1 && point.y == -1) PhGetListViewContextMenuPoint((HWND)wParam, &point); - if (ListView_GetSelectedCount(tokenPageContext->PrivilegesListViewHandle) != 0) + if (ListView_GetSelectedCount(tokenPageContext->ListViewHandle) != 0) { PPH_EMENU menu; + BOOLEAN listViewGroupItemsValid = TRUE; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM *listviewItems; + ULONG numberOfItems; + ULONG i; + + PhGetSelectedListViewItemParams( + tokenPageContext->ListViewHandle, + &listviewItems, + &numberOfItems + ); + + for (i = 0; i < numberOfItems; i++) + { + if (listviewItems[i]->IsTokenGroupEntry) + { + listViewGroupItemsValid = FALSE; + break; + } + } 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); + if (listViewGroupItemsValid) + { + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_ENABLE, L"&Enable", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_DISABLE, L"&Disable", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_REMOVE, L"&Remove", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); + } + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_COPY, L"&Copy", NULL, NULL), -1); + 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(listviewItems); } } } break; } - REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->GroupsListViewHandle, uMsg, wParam, lParam); - REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->PrivilegesListViewHandle, uMsg, wParam, lParam); + REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->ListViewHandle, uMsg, wParam, lParam); return FALSE; } From dd5645ce176aa188af1dc89da5cb37123b3095bc Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 10:55:19 +1100 Subject: [PATCH 773/839] Fix build --- ProcessHacker/prpgtok.c | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index 44a1e9a287d9..af2946f77f66 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -59,45 +59,30 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( { case WM_DESTROY: { - PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + if (PhGetWindowContext(hwndDlg, 0xD)) + PhRemoveWindowContext(hwndDlg, 0xD); } break; case WM_SHOWWINDOW: { - if (!PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)) // LayoutInitialized + if (!PhGetWindowContext(hwndDlg, 0xD)) // LayoutInitialized { PPH_LAYOUT_ITEM dialogItem; // 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_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); - - if (!PhGetIntegerSetting(L"TokenSplitterEnable")) - { - 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); - } + 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_ALL); + 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); - PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr(TRUE)); + PhSetWindowContext(hwndDlg, 0xD, UlongToPtr(TRUE)); } } break; From f8182c5bf427c05361e9b65f67ef3d3ece3815a5 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 12:14:58 +1100 Subject: [PATCH 774/839] Export PhEnumChildWindows --- ProcessHacker/appsup.c | 105 --------------------------------- ProcessHacker/include/appsup.h | 23 -------- phlib/guisup.c | 105 +++++++++++++++++++++++++++++++++ phlib/include/guisup.h | 23 ++++++++ 4 files changed, 128 insertions(+), 128 deletions(-) diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index 66413ad6e5cc..2662baa237b2 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -679,111 +679,6 @@ BOOLEAN PhaGetProcessKnownCommandLine( return TRUE; } -VOID PhEnumChildWindows( - _In_opt_ HWND WindowHandle, - _In_ ULONG Limit, - _In_ PH_CHILD_ENUM_CALLBACK Callback, - _In_ 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; - -BOOLEAN CALLBACK PhpGetProcessMainWindowEnumWindowsProc( - _In_ HWND WindowHandle, - _In_opt_ PVOID Context - ) -{ - PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)Context; - ULONG processId; - HWND parentWindow; - 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 (!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, ProcessQueryAccess, ProcessId); - - if (processHandle && IsImmersiveProcess_I) - context.IsImmersive = IsImmersiveProcess_I(processHandle); - - PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, &context); - - if (!ProcessHandle && processHandle) - NtClose(processHandle); - - return context.ImmersiveWindow ? context.ImmersiveWindow : context.Window; -} - PPH_STRING PhGetServiceRelevantFileName( _In_ PPH_STRINGREF ServiceName, _In_ SC_HANDLE ServiceHandle diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index 62038ebf4caf..261fdd058666 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -101,29 +101,6 @@ PhaGetProcessKnownCommandLine( ); // end_phapppub -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_ PVOID Context - ); - -HWND PhGetProcessMainWindow( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle - ); - -HWND PhGetProcessMainWindowEx( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle, - _In_ BOOLEAN SkipInvisible - ); - PPH_STRING PhGetServiceRelevantFileName( _In_ PPH_STRINGREF ServiceName, _In_ SC_HANDLE ServiceHandle diff --git a/phlib/guisup.c b/phlib/guisup.c index 2d24f76a515b..7305c65a21db 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1353,3 +1353,108 @@ VOID PhRemoveWindowContext( PhRemoveEntryHashtable(WindowContextHashTable, &lookupEntry); PhReleaseQueuedLockExclusive(&WindowContextListLock); } + +VOID PhEnumChildWindows( + _In_opt_ HWND WindowHandle, + _In_ ULONG Limit, + _In_ PH_CHILD_ENUM_CALLBACK Callback, + _In_ 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; + +BOOLEAN CALLBACK PhpGetProcessMainWindowEnumWindowsProc( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ) +{ + PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)Context; + ULONG processId; + HWND parentWindow; + 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 (!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, ProcessQueryAccess, ProcessId); + + if (processHandle && IsImmersiveProcess_I) + context.IsImmersive = IsImmersiveProcess_I(processHandle); + + PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, &context); + + if (!ProcessHandle && processHandle) + NtClose(processHandle); + + return context.ImmersiveWindow ? context.ImmersiveWindow : context.Window; +} diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index 06d4799f1cf9..38c83defb8d6 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -500,6 +500,29 @@ PhRemoveWindowContext( _In_ ULONG PropertyHash ); +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_ PVOID Context + ); + +HWND PhGetProcessMainWindow( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle + ); + +HWND PhGetProcessMainWindowEx( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ BOOLEAN SkipInvisible + ); + FORCEINLINE VOID PhResizingMinimumSize( _Inout_ PRECT Rect, _In_ WPARAM Edge, From ab93a3c5ecc8b7de1b6528d512689f7d79236647 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 12:20:39 +1100 Subject: [PATCH 775/839] SetupTool: Fix header conflict, Update to latset phsdk --- .../CustomSetupTool/CustomSetupTool.vcxproj | 2 +- .../CustomSetupTool.vcxproj.filters | 6 +- .../CustomSetupTool/CustomSetupTool/appsup.c | 190 +++++++++++------- .../CustomSetupTool/download.c | 5 +- .../CustomSetupTool/CustomSetupTool/extract.c | 62 +++--- .../CustomSetupTool/include/setup.h | 2 +- .../include/{appsup.h => setupsup.h} | 10 +- .../CustomSetupTool/licencepage.c | 9 +- tools/CustomSetupTool/CustomSetupTool/main.c | 15 +- tools/CustomSetupTool/CustomSetupTool/setup.c | 2 +- .../CustomSetupTool/startpage.c | 6 +- .../CustomSetupTool/uninstall.c | 2 +- .../CustomSetupTool/CustomSetupTool/update.c | 2 +- 13 files changed, 174 insertions(+), 139 deletions(-) rename tools/CustomSetupTool/CustomSetupTool/include/{appsup.h => setupsup.h} (92%) diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 3f43de47288d..ada56f42b999 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -126,7 +126,7 @@ - + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index 851ba8eb5075..9a3eebbb5c71 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -86,12 +86,12 @@ Header Files - - Header Files - Header Files\zip + + Header Files + diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 51e3eb7de171..dac67fa7e4f4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -30,22 +31,19 @@ VOID ExtractResourceToFile( ) { HANDLE fileHandle = NULL; - ULONG resourceLength; - HRSRC resourceHandle = NULL; - HGLOBAL resourceData; PVOID resourceBuffer; + ULONG resourceLength; IO_STATUS_BLOCK isb; - if (!(resourceHandle = FindResource(PhInstanceHandle, Resource, RT_RCDATA))) - goto CleanupExit; - - resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - - if (!(resourceData = LoadResource(PhInstanceHandle, resourceHandle))) - goto CleanupExit; - - if (!(resourceBuffer = LockResource(resourceData))) + if (!PhLoadResource( + PhInstanceHandle, + Resource, RT_RCDATA, + &resourceLength, + &resourceBuffer + )) + { goto CleanupExit; + } if (!NT_SUCCESS(PhCreateFileWin32( &fileHandle, @@ -82,43 +80,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(PhInstanceHandle, Resource, RT_RCDATA))) - goto CleanupExit; - - resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - - if (!(resourceData = LoadResource(PhInstanceHandle, 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( @@ -127,10 +88,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 }; @@ -149,18 +108,8 @@ HBITMAP LoadPngImageFromResources( if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory))) goto CleanupExit; - // Find the resource - if ((resourceHandleSource = FindResource(PhInstanceHandle, Name, L"PNG")) == NULL) - goto CleanupExit; - - // Get the resource length - resourceLength = SizeofResource(PhInstanceHandle, resourceHandleSource); - // Load the resource - if ((resourceHandle = LoadResource(PhInstanceHandle, resourceHandleSource)) == NULL) - goto CleanupExit; - - if ((resourceBuffer = (WICInProcPointer)LockResource(resourceHandle)) == NULL) + if (!PhLoadResource(PhInstanceHandle, Name, L"PNG", &resourceLength, &resourceBuffer)) goto CleanupExit; // Create the Stream @@ -270,8 +219,8 @@ HBITMAP LoadPngImageFromResources( if (wicFactory) IWICImagingFactory_Release(wicFactory); - if (resourceHandle) - FreeResource(resourceHandle); + if (resourceBuffer) + PhFree(resourceBuffer); if (success) { @@ -483,29 +432,74 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( _In_opt_ PVOID Context ) { - ULONG64 processId64; - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - if ( - PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && - PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && - PhStringToInteger64(&secondPart, 10, &processId64) + PhStartsWithStringRef2(Name, L"PhMainWindow_", TRUE) || + PhStartsWithStringRef2(Name, L"PhSetupWindow_", TRUE) || + PhStartsWithStringRef2(Name, L"PeViewerWindow_", TRUE) ) { HANDLE processHandle; + HWND hwnd; + ULONG64 sessionId64; + ULONG64 processId64; + PH_STRINGREF remaining; + PH_STRINGREF sessionIdPart; + PH_STRINGREF processIdPart; + + if (!PhSplitStringRefAtChar(Name, L'_', &remaining, &remaining)) + return TRUE; + if (!PhSplitStringRefAtChar(&remaining, L'_', &sessionIdPart, &processIdPart)) + return TRUE; + if (!PhStringToInteger64(&sessionIdPart, 10, &sessionId64)) + return TRUE; + if (!PhStringToInteger64(&processIdPart, 10, &processId64)) + return TRUE; - if (NT_SUCCESS(PhOpenProcess( + PhOpenProcess( &processHandle, - SYNCHRONIZE | PROCESS_TERMINATE, + PROCESS_TERMINATE | SYNCHRONIZE, ULongToHandle((ULONG)processId64) - ))) + ); + + if (sessionId64 == NtCurrentPeb()->SessionId) + { + if (hwnd = PhGetProcessMainWindowEx(UlongToHandle((ULONG)processId64), NULL, FALSE)) + { + SendMessageTimeout(hwnd, WM_QUIT, 0, 0, SMTO_BLOCK, 5000, NULL); + } + } + + if (processHandle) { NtTerminateProcess(processHandle, 1); NtClose(processHandle); } } + { + ULONG64 processId64; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + + if (( + PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) || + PhStartsWithStringRef2(Name, L"PhSetupMutant_", TRUE) || + PhStartsWithStringRef2(Name, L"PeViewer_", TRUE) + ) && + PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && + PhStringToInteger64(&secondPart, 10, &processId64) + ) + { + HANDLE processHandle; + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_TERMINATE | SYNCHRONIZE, ULongToHandle((ULONG)processId64)))) + { + NtTerminateProcess(processHandle, 1); + NtClose(processHandle); + } + } + } + return TRUE; } @@ -514,3 +508,47 @@ BOOLEAN ShutdownProcessHacker(VOID) PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); 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; + + bufferSize = initialBufferSize; + buffer = malloc(bufferSize); + + while ((status = NtQueryInformationFile( + VolumeOrFileHandle, + &isb, + buffer, + bufferSize, + FileProcessIdsUsingFileInformation + )) == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > SIZE_MAX) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = malloc(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + free(buffer); + return status; + } + + if (bufferSize <= 0x100000) initialBufferSize = bufferSize; + *Information = (PFILE_PROCESS_IDS_USING_FILE_INFORMATION)buffer; + + return status; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c index 51f76385dc58..a41abb3b351e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/download.c +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -79,8 +79,7 @@ ULONG64 ParseVersionString( PH_STRINGREF remaining, majorPart, minorPart, revisionPart; ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - PhInitializeStringRef(&remaining, PhGetString(VersionString)); - + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(VersionString)); PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); PhSplitStringRefAtChar(&remaining, '.', &revisionPart, &remaining); diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index f5c74259fb86..861501f39a9a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -19,36 +19,9 @@ */ #include -#include +#include #include "miniz\miniz.h" -PVOID GetZipResourceData( - _In_ PULONG resourceLength - ) -{ - HRSRC resourceHandle = NULL; - HGLOBAL resourceData; - PVOID resourceBuffer = NULL; - - if (!(resourceHandle = FindResource(PhInstanceHandle, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA))) - goto CleanupExit; - - *resourceLength = SizeofResource(PhInstanceHandle, resourceHandle); - - if (!(resourceData = LoadResource(PhInstanceHandle, resourceHandle))) - goto CleanupExit; - - if (!(resourceBuffer = LockResource(resourceData))) - goto CleanupExit; - -CleanupExit: - - if (resourceHandle) - FreeResource(resourceHandle); - - return resourceBuffer; -} - BOOLEAN SetupExtractBuild( _In_ PPH_SETUP_CONTEXT Context ) @@ -58,16 +31,13 @@ BOOLEAN SetupExtractBuild( ULONG64 currentLength = 0; mz_zip_archive zip_archive = { 0 }; PPH_STRING extractPath = NULL; - SYSTEM_INFO info; - - GetNativeSystemInfo(&info); #ifdef PH_BUILD_API ULONG resourceLength; - PVOID resourceBuffer; + PVOID resourceBuffer = NULL; - if (!(resourceBuffer = GetZipResourceData(&resourceLength))) - goto CleanupExit; + if (!PhLoadResource(PhInstanceHandle, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA, &resourceLength, &resourceBuffer)) + return FALSE; if (!(status = mz_zip_reader_init_mem(&zip_archive, resourceBuffer, resourceLength, 0))) goto CleanupExit; @@ -99,7 +69,7 @@ BOOLEAN SetupExtractBuild( fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (USER_SHARED_DATA->NativeProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; @@ -145,7 +115,7 @@ BOOLEAN SetupExtractBuild( if (PhFindStringInString(fileName, 0, L"usernotesdb.xml") != -1) continue; - if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (USER_SHARED_DATA->NativeProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; @@ -264,15 +234,29 @@ BOOLEAN SetupExtractBuild( mz_free(buffer); } - mz_zip_reader_end(&zip_archive); - if (extractPath) - PhDereferenceObject(extractPath); + { + mz_zip_reader_end(&zip_archive); + +#ifdef PH_BUILD_API + if (resourceBuffer) + PhFree(resourceBuffer); +#endif + if (extractPath) + PhDereferenceObject(extractPath); + } + return TRUE; CleanupExit: mz_zip_reader_end(&zip_archive); + +#ifdef PH_BUILD_API + if (resourceBuffer) + PhFree(resourceBuffer); +#endif if (extractPath) PhDereferenceObject(extractPath); + return FALSE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 5e025039333f..f0625c8809a5 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/setupsup.h similarity index 92% rename from tools/CustomSetupTool/CustomSetupTool/include/appsup.h rename to tools/CustomSetupTool/CustomSetupTool/include/setupsup.h index 3f0c27bb759e..876bc9cbe89e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setupsup.h @@ -40,10 +40,6 @@ PPH_STRING SetupFindInstallDirectory( VOID ); -PVOID ExtractResourceToBuffer( - _In_ PWSTR Resource - ); - VOID SetupInitializeFont( _In_ HWND ControlHandle, _In_ LONG Height, @@ -77,4 +73,10 @@ BOOLEAN ShutdownProcessHacker( VOID ); +NTSTATUS +QueryProcessesUsingVolumeOrFile( + _In_ HANDLE VolumeOrFileHandle, + _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information + ); + #endif diff --git a/tools/CustomSetupTool/CustomSetupTool/licencepage.c b/tools/CustomSetupTool/CustomSetupTool/licencepage.c index 87c3bdbe90c1..aff021b833a9 100644 --- a/tools/CustomSetupTool/CustomSetupTool/licencepage.c +++ b/tools/CustomSetupTool/CustomSetupTool/licencepage.c @@ -45,17 +45,18 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( 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 = PhConvertUtf8ToUtf16(resourceBuffer)) + if (eulaTextString = PhConvertUtf8ToUtf16Ex(resourceBuffer, resourceLength)) { SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT1), eulaTextString->Buffer); PhDereferenceObject(eulaTextString); diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index b1327e184520..5177477843b2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -220,8 +220,17 @@ VOID SetupInitializeMutant( HANDLE mutantHandle; OBJECT_ATTRIBUTES oa; UNICODE_STRING mutantName; + PPH_STRING objectName; + PH_FORMAT format[4]; + + PhInitFormatS(&format[0], L"PhSetupWindow_"); + PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); + PhInitFormatS(&format[2], L"_"); + PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 4, 16); + PhStringRefToUnicodeString(&objectName->sr, &mutantName); - RtlInitUnicodeString(&mutantName, L"PhSetupMutant"); InitializeObjectAttributes( &oa, &mutantName, @@ -230,7 +239,9 @@ VOID SetupInitializeMutant( NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); + + PhDereferenceObject(objectName); } INT WINAPI wWinMain( diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index d914e1d90444..399b0642b55d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -21,7 +21,7 @@ */ #include -#include +#include #include PH_STRINGREF UninstallKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ProcessHacker"); diff --git a/tools/CustomSetupTool/CustomSetupTool/startpage.c b/tools/CustomSetupTool/CustomSetupTool/startpage.c index 17a087b8664c..199161b43be8 100644 --- a/tools/CustomSetupTool/CustomSetupTool/startpage.c +++ b/tools/CustomSetupTool/CustomSetupTool/startpage.c @@ -108,9 +108,9 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( { case PSN_SETACTIVE: { -#ifdef _DEBUG - PostMessage(context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG3); -#endif +//#ifdef _DEBUG +// PostMessage(context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG3); +//#endif // Reset the button state. PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT); } diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c index 697576a43c22..f78b5cbb6b86 100644 --- a/tools/CustomSetupTool/CustomSetupTool/uninstall.c +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -19,7 +19,7 @@ */ #include -#include +#include #include #define WM_TASKDIALOGINIT (WM_APP + 550) diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 47f46f81e0ce..a45fcc04ed41 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -19,7 +19,7 @@ */ #include -#include +#include #define WM_TASKDIALOGINIT (WM_APP + 550) HWND UpdateDialogHandle = NULL; From 8a4641584ec32d7af4fad3df1821086e568a677a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 12:23:18 +1100 Subject: [PATCH 776/839] Fix single instance session check --- ProcessHacker/main.c | 44 ++++++++++++++++++++++++++------------------ tools/peview/main.c | 13 ++++++++----- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index e2afe98cf44e..757b52f15073 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -281,12 +281,14 @@ INT WINAPI wWinMain( OBJECT_ATTRIBUTES oa; UNICODE_STRING mutantName; PPH_STRING objectName; - PH_FORMAT format[2]; + PH_FORMAT format[4]; - PhInitFormatS(&format[0], L"PhMutant_"); - PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + PhInitFormatS(&format[0], L"PhMainWindow_"); + PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); + PhInitFormatS(&format[2], L"_"); + PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); - objectName = PhFormat(format, 2, 16); + objectName = PhFormat(format, 4, 16); PhStringRefToUnicodeString(&objectName->sr, &mutantName); InitializeObjectAttributes( @@ -297,7 +299,7 @@ INT WINAPI wWinMain( NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); PhDereferenceObject(objectName); } @@ -461,21 +463,27 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( _In_opt_ PVOID Context ) { - ULONG64 processId64; - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - - if ( - PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && - PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && - PhStringToInteger64(&secondPart, 10, &processId64) - ) + if (PhStartsWithStringRef2(Name, L"PhMainWindow_", TRUE)) { HWND hwnd; - - hwnd = PhGetProcessMainWindowEx((HANDLE)processId64, NULL, FALSE); - - if (hwnd) + ULONG64 sessionId64; + ULONG64 processId64; + PH_STRINGREF remaining; + PH_STRINGREF sessionIdPart; + PH_STRINGREF processIdPart; + + if (!PhSplitStringRefAtChar(Name, L'_', &remaining, &remaining)) + return TRUE; + if (!PhSplitStringRefAtChar(&remaining, L'_', &sessionIdPart, &processIdPart)) + return TRUE; + if (!PhStringToInteger64(&sessionIdPart, 10, &sessionId64)) + return TRUE; + if (!PhStringToInteger64(&processIdPart, 10, &processId64)) + return TRUE; + if (NtCurrentPeb()->SessionId != sessionId64) + return TRUE; + + if (hwnd = PhGetProcessMainWindowEx(UlongToHandle((ULONG)processId64), NULL, FALSE)) { ULONG_PTR result; diff --git a/tools/peview/main.c b/tools/peview/main.c index 73988a910c25..86c387a4427c 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -61,12 +61,14 @@ INT WINAPI wWinMain( OBJECT_ATTRIBUTES oa; UNICODE_STRING mutantName; PPH_STRING objectName; - PH_FORMAT format[2]; + PH_FORMAT format[4]; - PhInitFormatS(&format[0], L"PeViewer_"); - PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + PhInitFormatS(&format[0], L"PeViewerWindow_"); + PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); + PhInitFormatS(&format[2], L"_"); + PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); - objectName = PhFormat(format, 2, 16); + objectName = PhFormat(format, 4, 16); PhStringRefToUnicodeString(&objectName->sr, &mutantName); InitializeObjectAttributes( @@ -77,7 +79,8 @@ INT WINAPI wWinMain( NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); + PhDereferenceObject(objectName); } From 5cb8e7a2b6f243cf363b3decd1d0f2403cab6ad2 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 14:08:20 +1100 Subject: [PATCH 777/839] SetupTool: Fix previous version check --- tools/CustomSetupTool/CustomSetupTool/appsup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index dac67fa7e4f4..4e2606e6c35c 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -454,6 +454,8 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( return TRUE; if (!PhStringToInteger64(&processIdPart, 10, &processId64)) return TRUE; + if (UlongToHandle((ULONG)processId64) == NtCurrentProcessId()) + return TRUE; PhOpenProcess( &processHandle, From 505e37f86535d08a72b97fd2c099f889779014c2 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 5 Feb 2018 17:17:41 +1100 Subject: [PATCH 778/839] Fix incorrect PEB offsets --- phnt/include/ntpebteb.h | 10 +++++++++- phnt/include/ntpsapi.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index e6f0a82e372e..10e458f7ada9 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -124,7 +124,7 @@ typedef struct _PEB ULONG NumberOfProcessors; ULONG NtGlobalFlag; - LARGE_INTEGER CriticalSectionTimeout; + ULARGE_INTEGER CriticalSectionTimeout; SIZE_T HeapSegmentReserve; SIZE_T HeapSegmentCommit; SIZE_T HeapDeCommitTotalFreeThreshold; @@ -200,6 +200,14 @@ typedef struct _PEB ULONG CloudFileFlags; } PEB, *PPEB; +#ifdef _WIN64 +C_ASSERT(FIELD_OFFSET(PEB, SessionId) == 0x2C0); +C_ASSERT(sizeof(PEB) == 0x7B0); +#else +C_ASSERT(FIELD_OFFSET(PEB, SessionId) == 0x1D4); +C_ASSERT(sizeof(PEB) == 0x468); +#endif + #define GDI_BATCH_BUFFER_SIZE 310 typedef struct _GDI_TEB_BATCH diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index e134eb368b0c..ab6e2dfc4c73 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 From a9a0fb3da012983965a1b05a093a2ee0f5d7dc1c Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 15:18:18 +1100 Subject: [PATCH 779/839] Setup: Fix 32bit install #236 --- tools/CustomSetupTool/CustomSetupTool/extract.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 861501f39a9a..2c05c6dd9bf2 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -31,6 +31,9 @@ BOOLEAN SetupExtractBuild( ULONG64 currentLength = 0; mz_zip_archive zip_archive = { 0 }; PPH_STRING extractPath = NULL; + SYSTEM_INFO info; + + GetNativeSystemInfo(&info); #ifdef PH_BUILD_API ULONG resourceLength; @@ -69,7 +72,7 @@ BOOLEAN SetupExtractBuild( fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - if (USER_SHARED_DATA->NativeProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; @@ -115,7 +118,7 @@ BOOLEAN SetupExtractBuild( if (PhFindStringInString(fileName, 0, L"usernotesdb.xml") != -1) continue; - if (USER_SHARED_DATA->NativeProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; From 5612e9cb9235e5900119bfbbe2b7a9e3ec4e3879 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 17:25:53 +1100 Subject: [PATCH 780/839] Fix runas assert --- ProcessHacker/mainwnd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 65c5e9ce78d9..27b37e528790 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1857,7 +1857,7 @@ BOOLEAN PhMwpOnNotify( else { // Create the escaped execute string. - PhMoveReference(&executeString, fileName); + executeString = PhReferenceObject(fileName); } PhDereferenceObject(fileName); From c776627a7838c22287ff40f23e1ca0f3528695d0 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 20:01:08 +1100 Subject: [PATCH 781/839] Fix token page properties and coloring --- ProcessHacker/tokprp.c | 92 +++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 05b19f7160e3..36e4432d2547 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -3,7 +3,7 @@ * token properties * * Copyright (C) 2010-2012 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -233,15 +233,47 @@ PPH_STRING PhGetGroupAttributesString( _In_ BOOLEAN Restricted ) { - PWSTR baseString; PPH_STRING string; if (Attributes & SE_GROUP_INTEGRITY) { if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - string = PhCreateString(L"Integrity"); + string = PhReferenceEmptyString(); + else + string = PhCreateString(L"Disabled"); + } + else + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + string = PhCreateString(L"Default Enabled"); + else if (Attributes & SE_GROUP_ENABLED) + string = PhReferenceEmptyString(); + else + string = PhCreateString(L"Disabled"); + } + + if (Restricted) + { + PPH_STRING prefixString = string; + string = PhConcatStrings2(prefixString->Buffer, L" (restricted)"); + PhDereferenceObject(prefixString); + } + + return string; +} + +PWSTR PhGetGroupDescriptionString( + _In_ ULONG Attributes + ) +{ + PWSTR baseString; + + if (Attributes & SE_GROUP_INTEGRITY) + { + if (Attributes & SE_GROUP_INTEGRITY_ENABLED) + baseString = L"Integrity"; else - string = PhCreateString(L"Integrity (disabled)"); + baseString = NULL; } else { @@ -257,37 +289,12 @@ PPH_STRING PhGetGroupAttributesString( baseString = L"Use for deny only"; else baseString = NULL; - - if (!baseString) - { - if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) - string = PhCreateString(L"Default enabled"); - else if (Attributes & SE_GROUP_ENABLED) - string = PhReferenceEmptyString(); - else - string = PhCreateString(L"Disabled"); - } - else - { - 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)"); - } } - if (Restricted) - { - PPH_STRING prefixString = string; - string = PhConcatStrings2(prefixString->Buffer, L" (restricted)"); - PhDereferenceObject(prefixString); - } - - return string; + return baseString; } + COLORREF PhGetGroupAttributesColor( _In_ ULONG Attributes ) @@ -295,15 +302,15 @@ COLORREF PhGetGroupAttributesColor( if (Attributes & SE_GROUP_INTEGRITY) { if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - return RGB(0xe0, 0xf0, 0xe0); + return RGB(0xff, 0xf0, 0xc0); else return GetSysColor(COLOR_WINDOW); } if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) - return RGB(0xe0, 0xf0, 0xe0); + return RGB(0xc0, 0xf0, 0xc0); else if (Attributes & SE_GROUP_ENABLED) - return GetSysColor(COLOR_WINDOW); + return RGB(0x00, 0xff, 0x7f); else return RGB(0xf0, 0xe0, 0xe0); } @@ -315,7 +322,7 @@ COLORREF PhGetPrivilegeAttributesColor( if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) return RGB(0xc0, 0xf0, 0xc0); else if (Attributes & SE_PRIVILEGE_ENABLED) - return RGB(0xe0, 0xf0, 0xe0); + return RGB(0x00, 0xff, 0x7f); else return RGB(0xf0, 0xe0, 0xe0); } @@ -395,6 +402,7 @@ VOID PhpUpdateSidsFromTokenGroups( INT lvItemIndex; PPH_STRING fullName; PPH_STRING attributesString; + PWSTR descriptionString; if (!(fullName = PhGetSidFullName(Groups->Groups[i].Sid, TRUE, NULL))) fullName = PhSidToStringSid(Groups->Groups[i].Sid); @@ -416,10 +424,18 @@ VOID PhpUpdateSidsFromTokenGroups( fullName->Buffer, lvitem ); - attributesString = PhGetGroupAttributesString(Groups->Groups[i].Attributes, Restricted); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, attributesString->Buffer); - PhDereferenceObject(attributesString); + if (attributesString = PhGetGroupAttributesString(Groups->Groups[i].Attributes, Restricted)) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, PhGetString(attributesString)); + PhDereferenceObject(attributesString); + } + + if (descriptionString = PhGetGroupDescriptionString(Groups->Groups[i].Attributes)) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_DESCRIPTION, descriptionString); + } + PhDereferenceObject(fullName); } } From 3c1cb4fa7af4591c2ead3d03042116b74c2bfddb Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 21:15:56 +1100 Subject: [PATCH 782/839] Fix instance bug updating/running with multiple logged on users --- ProcessHacker/main.c | 110 ++++++++++++------ .../CustomSetupTool/CustomSetupTool/appsup.c | 110 +++++++++--------- tools/CustomSetupTool/CustomSetupTool/main.c | 24 +--- tools/peview/main.c | 24 +--- 4 files changed, 142 insertions(+), 126 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 757b52f15073..8f3f788481a4 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -277,31 +277,19 @@ INT WINAPI wWinMain( // Create a mutant for the installer. { - HANDLE mutantHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - PPH_STRING objectName; - PH_FORMAT format[4]; - - PhInitFormatS(&format[0], L"PhMainWindow_"); - PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); - PhInitFormatS(&format[2], L"_"); - PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); - - objectName = PhFormat(format, 4, 16); - PhStringRefToUnicodeString(&objectName->sr, &mutantName); + static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PhMutant"); + OBJECT_ATTRIBUTES objectAttributes; + HANDLE objectHandle; InitializeObjectAttributes( - &oa, - &mutantName, + &objectAttributes, + &objectNameUs, OBJ_CASE_INSENSITIVE, PhGetNamespaceHandle(), NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); - - PhDereferenceObject(objectName); + NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); } // Set the default priority. @@ -463,27 +451,68 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( _In_opt_ PVOID Context ) { - if (PhStartsWithStringRef2(Name, L"PhMainWindow_", TRUE)) + static PH_STRINGREF objectNameSr = PH_STRINGREF_INIT(L"PhMutant"); + HANDLE objectHandle; + UNICODE_STRING objectNameUs; + OBJECT_ATTRIBUTES objectAttributes; + MUTANT_OWNER_INFORMATION objectInfo; + + if (!PhEqualStringRef(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(NtQueryMutant( + objectHandle, + MutantOwnerInformation, + &objectInfo, + sizeof(MUTANT_OWNER_INFORMATION), + NULL + ))) { HWND hwnd; - ULONG64 sessionId64; - ULONG64 processId64; - PH_STRINGREF remaining; - PH_STRINGREF sessionIdPart; - PH_STRINGREF processIdPart; - - if (!PhSplitStringRefAtChar(Name, L'_', &remaining, &remaining)) - return TRUE; - if (!PhSplitStringRefAtChar(&remaining, L'_', &sessionIdPart, &processIdPart)) - return TRUE; - if (!PhStringToInteger64(&sessionIdPart, 10, &sessionId64)) - return TRUE; - if (!PhStringToInteger64(&processIdPart, 10, &processId64)) - return TRUE; - if (NtCurrentPeb()->SessionId != sessionId64) - return TRUE; - - if (hwnd = PhGetProcessMainWindowEx(UlongToHandle((ULONG)processId64), NULL, FALSE)) + HANDLE processHandle = NULL; + HANDLE tokenHandle = NULL; + PTOKEN_USER tokenCurrent = NULL; + PTOKEN_USER tokenUser = NULL; + + if (objectInfo.ClientId.UniqueProcess == NtCurrentProcessId()) + goto CleanupExit; + if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, objectInfo.ClientId.UniqueProcess))) + goto CleanupExit; + if (!NT_SUCCESS(PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle))) + goto CleanupExit; + if (!NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + goto CleanupExit; + if (!NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &tokenCurrent))) + goto CleanupExit; + if (!RtlEqualSid(tokenUser->User.Sid, tokenCurrent->User.Sid)) + goto CleanupExit; + + hwnd = PhGetProcessMainWindowEx( + objectInfo.ClientId.UniqueProcess, + processHandle, + FALSE + ); + + if (hwnd) { ULONG_PTR result; @@ -495,8 +524,15 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( RtlExitUserProcess(STATUS_SUCCESS); } } + + CleanupExit: + if (tokenUser) PhFree(tokenUser); + if (tokenCurrent) PhFree(tokenCurrent); + if (tokenHandle) NtClose(tokenHandle); + if (processHandle) NtClose(processHandle); } + NtClose(objectHandle); return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 4e2606e6c35c..bbc367e52209 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -432,76 +432,80 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( _In_opt_ PVOID Context ) { - if ( - PhStartsWithStringRef2(Name, L"PhMainWindow_", TRUE) || - PhStartsWithStringRef2(Name, L"PhSetupWindow_", TRUE) || - PhStartsWithStringRef2(Name, L"PeViewerWindow_", TRUE) - ) + HANDLE objectHandle; + UNICODE_STRING objectNameUs; + OBJECT_ATTRIBUTES objectAttributes; + MUTANT_OWNER_INFORMATION objectInfo; + + if (!PhEqualStringRef2(Name, L"PhMutant", TRUE) && + !PhEqualStringRef2(Name, L"PhSetupMutant", TRUE) && + !PhEqualStringRef2(Name, L"PeViewerMutant", TRUE)) + { + 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(NtQueryMutant( + objectHandle, + MutantOwnerInformation, + &objectInfo, + sizeof(MUTANT_OWNER_INFORMATION), + NULL + ))) { - HANDLE processHandle; HWND hwnd; - ULONG64 sessionId64; - ULONG64 processId64; - PH_STRINGREF remaining; - PH_STRINGREF sessionIdPart; - PH_STRINGREF processIdPart; + HANDLE processHandle = NULL; - if (!PhSplitStringRefAtChar(Name, L'_', &remaining, &remaining)) - return TRUE; - if (!PhSplitStringRefAtChar(&remaining, L'_', &sessionIdPart, &processIdPart)) - return TRUE; - if (!PhStringToInteger64(&sessionIdPart, 10, &sessionId64)) - return TRUE; - if (!PhStringToInteger64(&processIdPart, 10, &processId64)) - return TRUE; - if (UlongToHandle((ULONG)processId64) == NtCurrentProcessId()) - return TRUE; + if (objectInfo.ClientId.UniqueProcess == NtCurrentProcessId()) + goto CleanupExit; PhOpenProcess( - &processHandle, - PROCESS_TERMINATE | SYNCHRONIZE, - ULongToHandle((ULONG)processId64) + &processHandle, + ProcessQueryAccess, + objectInfo.ClientId.UniqueProcess + ); + + hwnd = PhGetProcessMainWindowEx( + objectInfo.ClientId.UniqueProcess, + processHandle, + FALSE ); - if (sessionId64 == NtCurrentPeb()->SessionId) + if (hwnd) { - if (hwnd = PhGetProcessMainWindowEx(UlongToHandle((ULONG)processId64), NULL, FALSE)) - { - SendMessageTimeout(hwnd, WM_QUIT, 0, 0, SMTO_BLOCK, 5000, NULL); - } + SendMessageTimeout(hwnd, WM_QUIT, 0, 0, SMTO_BLOCK, 5000, NULL); } if (processHandle) { NtTerminateProcess(processHandle, 1); - NtClose(processHandle); } - } - - { - ULONG64 processId64; - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - - if (( - PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) || - PhStartsWithStringRef2(Name, L"PhSetupMutant_", TRUE) || - PhStartsWithStringRef2(Name, L"PeViewer_", TRUE) - ) && - PhSplitStringRefAtChar(Name, L'_', &firstPart, &secondPart) && - PhStringToInteger64(&secondPart, 10, &processId64) - ) - { - HANDLE processHandle; - if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_TERMINATE | SYNCHRONIZE, ULongToHandle((ULONG)processId64)))) - { - NtTerminateProcess(processHandle, 1); - NtClose(processHandle); - } - } + CleanupExit: + if (processHandle) NtClose(processHandle); } + NtClose(objectHandle); + return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 5177477843b2..bf23797055c3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -217,31 +217,19 @@ VOID SetupInitializeMutant( VOID ) { - HANDLE mutantHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - PPH_STRING objectName; - PH_FORMAT format[4]; - - PhInitFormatS(&format[0], L"PhSetupWindow_"); - PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); - PhInitFormatS(&format[2], L"_"); - PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); - - objectName = PhFormat(format, 4, 16); - PhStringRefToUnicodeString(&objectName->sr, &mutantName); + static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PhSetupMutant"); + OBJECT_ATTRIBUTES objectAttributes; + HANDLE objectHandle; InitializeObjectAttributes( - &oa, - &mutantName, + &objectAttributes, + &objectNameUs, OBJ_CASE_INSENSITIVE, PhGetNamespaceHandle(), NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); - - PhDereferenceObject(objectName); + NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); } INT WINAPI wWinMain( diff --git a/tools/peview/main.c b/tools/peview/main.c index 86c387a4427c..7d991176f18c 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -57,31 +57,19 @@ INT WINAPI wWinMain( // Create a mutant for the installer. { - HANDLE mutantHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - PPH_STRING objectName; - PH_FORMAT format[4]; - - PhInitFormatS(&format[0], L"PeViewerWindow_"); - PhInitFormatU(&format[1], NtCurrentPeb()->SessionId); - PhInitFormatS(&format[2], L"_"); - PhInitFormatU(&format[3], HandleToUlong(NtCurrentProcessId())); - - objectName = PhFormat(format, 4, 16); - PhStringRefToUnicodeString(&objectName->sr, &mutantName); + static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PeViewerMutant"); + OBJECT_ATTRIBUTES objectAttributes; + HANDLE objectHandle; InitializeObjectAttributes( - &oa, - &mutantName, + &objectAttributes, + &objectNameUs, OBJ_CASE_INSENSITIVE, PhGetNamespaceHandle(), NULL ); - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, TRUE); - - PhDereferenceObject(objectName); + NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); } PhGuiSupportInitialization(); From 1f45ce9bc164dca54ec1bfa47379b5eaefea5faa Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 21:17:12 +1100 Subject: [PATCH 783/839] Fix token highlighting inconsistency --- ProcessHacker/tokprp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 36e4432d2547..6ee67424fe8d 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -302,7 +302,7 @@ COLORREF PhGetGroupAttributesColor( if (Attributes & SE_GROUP_INTEGRITY) { if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - return RGB(0xff, 0xf0, 0xc0); + return RGB(0xe0, 0xf0, 0xe0); else return GetSysColor(COLOR_WINDOW); } @@ -310,7 +310,7 @@ COLORREF PhGetGroupAttributesColor( if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) return RGB(0xc0, 0xf0, 0xc0); else if (Attributes & SE_GROUP_ENABLED) - return RGB(0x00, 0xff, 0x7f); + return GetSysColor(COLOR_WINDOW); else return RGB(0xf0, 0xe0, 0xe0); } @@ -322,7 +322,7 @@ COLORREF PhGetPrivilegeAttributesColor( if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) return RGB(0xc0, 0xf0, 0xc0); else if (Attributes & SE_PRIVILEGE_ENABLED) - return RGB(0x00, 0xff, 0x7f); + return RGB(0xe0, 0xf0, 0xe0); else return RGB(0xf0, 0xe0, 0xe0); } @@ -581,7 +581,7 @@ INT_PTR CALLBACK PhpTokenPageProc( PhSetExtendedListView(tokenPageContext->ListViewHandle); ExtendedListView_SetItemColorFunction(tokenPageContext->ListViewHandle, PhpTokenGroupColorFunction); - PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); + PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); ListView_EnableGroupView(tokenPageContext->ListViewHandle, TRUE); PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, L"Privileges"); From dee06e37dc654fe47f4180bb46545bf7c5944a79 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 22:19:57 +1100 Subject: [PATCH 784/839] Fix find handles window handle type dropdown --- ProcessHacker/findobj.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index ff8692d61265..85714d3d42ed 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -3,7 +3,7 @@ * object search * * Copyright (C) 2010-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -651,11 +651,36 @@ VOID PhpPopulateObjectTypes( // Sort the object types. qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PVOID), PhpStringObjectTypeCompare); - // Add the types to the object filter combobox. - for (ULONG i = 0; i < objectTypeList->Count; i++) { - ComboBox_AddString(FilterTypeCombo, PhGetString(objectTypeList->Items[i])); - PhDereferenceObject(objectTypeList->Items[i]); + LONG maxLength; + HDC screenDc; + + maxLength = 0; + screenDc = GetDC(FilterTypeCombo); + + SendMessage(FilterTypeCombo, WM_SETFONT, (WPARAM)PhApplicationFont, TRUE); + + for (ULONG i = 0; i < objectTypeList->Count; i++) + { + PPH_STRING entry = objectTypeList->Items[i]; + SIZE textSize; + + if (GetTextExtentPoint32(screenDc, 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, screenDc); + + if (maxLength) + { + SendMessage(FilterTypeCombo, CB_SETDROPPEDWIDTH, maxLength, 0); + } } PhDereferenceObject(objectTypeList); From f38f7154b5a26835f5ad6c6c9efea11ceefe4da6 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 22:34:25 +1100 Subject: [PATCH 785/839] Fix handle type combobox focus --- ProcessHacker/findobj.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 85714d3d42ed..3d5914e88bbb 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -56,6 +56,9 @@ typedef struct _PH_HANDLE_SEARCH_CONTEXT HWND WindowHandle; HWND TreeNewHandle; + HWND TypeWindowHandle; + HWND SearchWindowHandle; + ULONG TreeNewSortColumn; PH_SORT_ORDER TreeNewSortOrder; PPH_HASHTABLE NodeHashtable; @@ -1144,16 +1147,21 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( context->WindowHandle = hwndDlg; context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_TREELIST); + context->TypeWindowHandle = GetDlgItem(hwndDlg, IDC_FILTERTYPE); + context->SearchWindowHandle = GetDlgItem(hwndDlg, IDC_FILTER); + + PhCenterWindow(hwndDlg, NULL); + PhRegisterDialog(hwndDlg); - PhCreateSearchControl(hwndDlg, GetDlgItem(hwndDlg, IDC_FILTER), L"Find Handles or DLLs"); + PhCreateSearchControl(hwndDlg, context->SearchWindowHandle, L"Find Handles or DLLs"); - PhpPopulateObjectTypes(GetDlgItem(hwndDlg, IDC_FILTERTYPE)); + PhpPopulateObjectTypes(context->TypeWindowHandle); InitializeHandleObjectTree(context); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTERTYPE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + 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); @@ -1164,9 +1172,6 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( context->MinimumSize.bottom = 100; MapDialogRect(hwndDlg, &context->MinimumSize); - PhRegisterDialog(hwndDlg); - - PhCenterWindow(hwndDlg, NULL); PhLoadWindowPlacementFromSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); context->SearchResults = PhCreateList(128); @@ -1185,7 +1190,7 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( ); } - Edit_SetSel(GetDlgItem(hwndDlg, IDC_FILTER), 0, -1); + Edit_SetSel(context->SearchWindowHandle, 0, -1); Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); } break; @@ -1271,6 +1276,19 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( 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: @@ -1281,8 +1299,8 @@ INT_PTR CALLBACK PhpFindObjectsDlgProc( if (!context->SearchThreadHandle) { - PhMoveReference(&context->SearchString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTER))); - PhMoveReference(&context->SearchTypeString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTERTYPE))); + PhMoveReference(&context->SearchString, PhGetWindowText(context->SearchWindowHandle)); + PhMoveReference(&context->SearchTypeString, PhGetWindowText(context->TypeWindowHandle)); if (context->SearchRegexCompiledExpression) { From e13ce47785f65cb6b7404e67d52c9df1677102a8 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 22:43:42 +1100 Subject: [PATCH 786/839] Add debug macro for the UseColorHandleProtected setting --- phlib/native.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/phlib/native.c b/phlib/native.c index e5d80619004c..1373e48883a1 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -120,6 +120,14 @@ NTSTATUS PhOpenProcess( 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( From 6dfc9dd92b6972bbff5b726bfa04e97df609e4f3 Mon Sep 17 00:00:00 2001 From: dmex Date: Tue, 6 Feb 2018 22:51:13 +1100 Subject: [PATCH 787/839] Fix options window parenting --- ProcessHacker/include/phplug.h | 1 + ProcessHacker/options.c | 65 +++++++++++++--------------------- 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index 02cd6cfb99b8..f7d00fc2e765 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -370,6 +370,7 @@ typedef struct _PH_OPTIONS_SECTION PVOID Parameter; HWND DialogHandle; + HTREEITEM TreeItemHandle; // begin_phapppub } PH_OPTIONS_SECTION, *PPH_OPTIONS_SECTION; // end_phapppub diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 0992214bbd1d..e9dba405afcc 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -175,7 +175,7 @@ VOID PhShowOptionsDialog( DialogBox( PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTIONS), - ParentWindowHandle, + NULL, PhOptionsDialogProc ); @@ -211,10 +211,10 @@ VOID PhShowOptionsDialog( } } -static HTREEITEM PhpOptionsTreeViewAddItem( +static HTREEITEM PhpTreeViewInsertItem( + _In_opt_ HTREEITEM HandleInsertAfter, _In_ PWSTR Text, - _In_ PVOID Context, - _In_ HTREEITEM InsertAfter + _In_ PVOID Context ) { TV_INSERTSTRUCT insert; @@ -222,7 +222,7 @@ static HTREEITEM PhpOptionsTreeViewAddItem( memset(&insert, 0, sizeof(TV_INSERTSTRUCT)); insert.hParent = TVI_ROOT; - insert.hInsertAfter = InsertAfter; + insert.hInsertAfter = HandleInsertAfter; insert.item.mask = TVIF_TEXT | TVIF_PARAM; insert.item.pszText = Text; insert.item.lParam = (LPARAM)Context; @@ -234,52 +234,36 @@ static VOID PhpOptionsShowHideTreeViewItem( _In_ BOOLEAN Hide ) { - HTREEITEM tvItemGeneral = NULL; - HTREEITEM tvItemAdvanced = NULL; - HTREEITEM tvItemCurrent; - - tvItemCurrent = TreeView_GetRoot(OptionsTreeControl); + static PH_STRINGREF generalName = PH_STRINGREF_INIT(L"General"); + static PH_STRINGREF advancedName = PH_STRINGREF_INIT(L"Advanced"); - while (tvItemCurrent) + if (Hide) { - TVITEM tvItem; - WCHAR buffer[MAX_PATH]; - - tvItem.mask = TVIF_TEXT | TVIF_HANDLE; - tvItem.hItem = tvItemCurrent; - tvItem.cchTextMax = ARRAYSIZE(buffer); - tvItem.pszText = buffer; + PPH_OPTIONS_SECTION advancedSection; - if (TreeView_GetItem(OptionsTreeControl, &tvItem)) + if (advancedSection = PhOptionsFindSection(&advancedName)) { - if (PhEqualStringZ(buffer, L"Advanced", TRUE)) + if (advancedSection->TreeItemHandle) { - tvItemAdvanced = tvItemCurrent; - } - else if (PhEqualStringZ(buffer, L"General", TRUE)) - { - tvItemGeneral = tvItemCurrent; + TreeView_DeleteItem(OptionsTreeControl, advancedSection->TreeItemHandle); + advancedSection->TreeItemHandle = NULL; } } - - tvItemCurrent = TreeView_GetNextSibling(OptionsTreeControl, tvItemCurrent); - } - - if (Hide) - { - if (tvItemAdvanced) - TreeView_DeleteItem(OptionsTreeControl, tvItemAdvanced); } else { - static PH_STRINGREF sectionName = PH_STRINGREF_INIT(L"Advanced"); + PPH_OPTIONS_SECTION generalSection; + PPH_OPTIONS_SECTION advancedSection; + + generalSection = PhOptionsFindSection(&generalName); + advancedSection = PhOptionsFindSection(&advancedName); - if (tvItemGeneral) + if (generalSection && advancedSection) { - PhpOptionsTreeViewAddItem( - sectionName.Buffer, - PhOptionsFindSection(§ionName), - tvItemGeneral + advancedSection->TreeItemHandle = PhpTreeViewInsertItem( + generalSection->TreeItemHandle, + advancedName.Buffer, + advancedSection ); } } @@ -547,11 +531,10 @@ PPH_OPTIONS_SECTION PhOptionsCreateSection( section->Template = Template; section->DialogProc = DialogProc; section->Parameter = Parameter; + section->TreeItemHandle = PhpTreeViewInsertItem(TVI_LAST, Name, section); PhAddItemList(SectionList, section); - PhpOptionsTreeViewAddItem(Name, section, TVI_LAST); - return section; } From ad89fab1edb5a3661bbfd6352e4c389eb515cfc3 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Feb 2018 16:06:08 +1100 Subject: [PATCH 788/839] Fix commit 3c1cb4f --- ProcessHacker/main.c | 25 +++++++++++++++---- .../CustomSetupTool/CustomSetupTool/appsup.c | 6 ++--- tools/CustomSetupTool/CustomSetupTool/main.c | 21 +++++++++++++--- tools/peview/main.c | 21 +++++++++++++--- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 8f3f788481a4..3fdcb508f75b 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -277,9 +277,17 @@ INT WINAPI wWinMain( // Create a mutant for the installer. { - static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PhMutant"); + HANDLE mutantHandle; + PPH_STRING objectName; OBJECT_ATTRIBUTES objectAttributes; - HANDLE objectHandle; + UNICODE_STRING objectNameUs; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PhMutant_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 2, 16); + PhStringRefToUnicodeString(&objectName->sr, &objectNameUs); InitializeObjectAttributes( &objectAttributes, @@ -289,7 +297,14 @@ INT WINAPI wWinMain( NULL ); - NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); + NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ); + + PhDereferenceObject(objectName); } // Set the default priority. @@ -451,13 +466,13 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( _In_opt_ PVOID Context ) { - static PH_STRINGREF objectNameSr = PH_STRINGREF_INIT(L"PhMutant"); + static PH_STRINGREF objectNameSr = PH_STRINGREF_INIT(L"PhMutant_"); HANDLE objectHandle; UNICODE_STRING objectNameUs; OBJECT_ATTRIBUTES objectAttributes; MUTANT_OWNER_INFORMATION objectInfo; - if (!PhEqualStringRef(Name, &objectNameSr, FALSE)) + if (!PhStartsWithStringRef(Name, &objectNameSr, FALSE)) return TRUE; if (!PhStringRefToUnicodeString(Name, &objectNameUs)) return TRUE; diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index bbc367e52209..0ffce62ff37f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -437,9 +437,9 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( OBJECT_ATTRIBUTES objectAttributes; MUTANT_OWNER_INFORMATION objectInfo; - if (!PhEqualStringRef2(Name, L"PhMutant", TRUE) && - !PhEqualStringRef2(Name, L"PhSetupMutant", TRUE) && - !PhEqualStringRef2(Name, L"PeViewerMutant", TRUE)) + if (!PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && + !PhStartsWithStringRef2(Name, L"PhSetupMutant_", TRUE) && + !PhStartsWithStringRef2(Name, L"PeViewerMutant_", TRUE)) { return TRUE; } diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index bf23797055c3..a5eb7e27a9a5 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -217,9 +217,17 @@ VOID SetupInitializeMutant( VOID ) { - static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PhSetupMutant"); + HANDLE mutantHandle; + PPH_STRING objectName; OBJECT_ATTRIBUTES objectAttributes; - HANDLE objectHandle; + 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, @@ -229,7 +237,14 @@ VOID SetupInitializeMutant( NULL ); - NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); + NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ); + + PhDereferenceObject(objectName); } INT WINAPI wWinMain( diff --git a/tools/peview/main.c b/tools/peview/main.c index 7d991176f18c..4a31bb982852 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -57,9 +57,17 @@ INT WINAPI wWinMain( // Create a mutant for the installer. { - static UNICODE_STRING objectNameUs = RTL_CONSTANT_STRING(L"PeViewerMutant"); + HANDLE mutantHandle; + PPH_STRING objectName; OBJECT_ATTRIBUTES objectAttributes; - HANDLE objectHandle; + 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, @@ -69,7 +77,14 @@ INT WINAPI wWinMain( NULL ); - NtCreateMutant(&objectHandle, MUTANT_ALL_ACCESS, &objectAttributes, TRUE); + NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ); + + PhDereferenceObject(objectName); } PhGuiSupportInitialization(); From f553421c67302fd690c595c0681b7631844fa352 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Feb 2018 16:07:36 +1100 Subject: [PATCH 789/839] Add missing LDR_IS_IMAGEMAPPING macro --- phnt/include/ntldr.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 8f8fea39570d..5e90c65ea148 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -574,6 +574,10 @@ LdrDisableThreadCalloutsForDll( // Resources +#define LDR_IS_DATAFILE(BaseAddress) (((ULONG_PTR)(BaseAddress)) & (ULONG_PTR)1) +#define LDR_IS_IMAGEMAPPING(BaseAddress) (((ULONG_PTR)(BaseAddress)) & (ULONG_PTR)2) +#define LDR_IS_RESOURCE(BaseAddress) (LDR_IS_IMAGEMAPPING(BaseAddress) || LDR_IS_DATAFILE(BaseAddress)) + NTSYSAPI NTSTATUS NTAPI From 9c2ef424abc8873d00d08c90b151e11604c09ed1 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Feb 2018 16:08:36 +1100 Subject: [PATCH 790/839] Add missing IMAGE_RESOURCE flag --- phlib/util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 42dd323c6288..ce7005a51386 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -1539,7 +1539,11 @@ PVOID PhGetFileVersionInfo( PVOID libraryModule; PVOID versionInfo; - libraryModule = LoadLibraryEx(FileName, NULL, LOAD_LIBRARY_AS_DATAFILE); + libraryModule = LoadLibraryEx( + FileName, + NULL, + LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE + ); if (!libraryModule) return NULL; @@ -5339,7 +5343,7 @@ PPH_STRING PhLoadIndirectString( PhMoveReference(&libraryString, expandedString); } - if (libraryModule = LoadLibraryEx(libraryString->Buffer, NULL, LOAD_LIBRARY_AS_DATAFILE)) + if (libraryModule = LoadLibraryEx(libraryString->Buffer, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)) { indirectString = PhLoadString(libraryModule, -index); FreeLibrary(libraryModule); From b23025e7b7a9daf2e9c88b798f20de4b7b4df7b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 7 Feb 2018 18:09:11 +1100 Subject: [PATCH 791/839] Move ntldr type --- phnt/include/ntldr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 5e90c65ea148..0f2d93769799 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -179,6 +179,10 @@ typedef struct _LDR_DATA_TABLE_ENTRY UCHAR SigningLevel; // since REDSTONE2 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; +#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 NTAPI @@ -574,10 +578,6 @@ LdrDisableThreadCalloutsForDll( // Resources -#define LDR_IS_DATAFILE(BaseAddress) (((ULONG_PTR)(BaseAddress)) & (ULONG_PTR)1) -#define LDR_IS_IMAGEMAPPING(BaseAddress) (((ULONG_PTR)(BaseAddress)) & (ULONG_PTR)2) -#define LDR_IS_RESOURCE(BaseAddress) (LDR_IS_IMAGEMAPPING(BaseAddress) || LDR_IS_DATAFILE(BaseAddress)) - NTSYSAPI NTSTATUS NTAPI From 5f37aa50a5b0b4b771e8c0fd879ab32c2f7b0ed1 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Feb 2018 00:59:18 +1100 Subject: [PATCH 792/839] Updater: Fix update dialog responsiveness after startup --- plugins/Updater/page2.c | 2 +- plugins/Updater/updater.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/Updater/page2.c b/plugins/Updater/page2.c index c07909b22af8..57fc49df0d40 100644 --- a/plugins/Updater/page2.c +++ b/plugins/Updater/page2.c @@ -40,7 +40,7 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); PhReferenceObject(context); - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UpdateCheckThread, context); + PhCreateThread2(UpdateCheckThread, context); } break; } diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index a448b8892a31..8be7e0cbdd87 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -426,13 +426,16 @@ NTSTATUS UpdateCheckThread( _In_ PVOID Parameter ) { - PPH_UPDATER_CONTEXT context = NULL; + PPH_UPDATER_CONTEXT context; ULONGLONG currentVersion = 0; ULONGLONG latestVersion = 0; + PH_AUTO_POOL autoPool; context = (PPH_UPDATER_CONTEXT)Parameter; context->ErrorCode = STATUS_SUCCESS; + PhInitializeAutoPool(&autoPool); + // Check if we have cached update data if (!context->HaveData) { @@ -444,6 +447,7 @@ NTSTATUS UpdateCheckThread( ShowUpdateFailedDialog(context, FALSE, FALSE); PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); return STATUS_SUCCESS; } @@ -477,6 +481,7 @@ NTSTATUS UpdateCheckThread( } PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); return STATUS_SUCCESS; } From 152ae4e5138361139fea6c424f361eab98b3b8b5 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 8 Feb 2018 03:46:46 +1100 Subject: [PATCH 793/839] Fix default debugger directory --- ProcessHacker/ProcessHacker.vcxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 196b6949ee97..08dc681c1413 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -30,22 +30,30 @@ Unicode true v141 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ Application Unicode v141 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ Application Unicode true v141 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ Application Unicode v141 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ From 3b504dd405b08ead4b58ede7d5993485244e68b8 Mon Sep 17 00:00:00 2001 From: diversenok <30962924+diversenok@users.noreply.github.com> Date: Wed, 7 Feb 2018 21:16:56 +0300 Subject: [PATCH 794/839] Lost check for abandoned mutexes (#238) --- phlib/util.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/phlib/util.c b/phlib/util.c index ce7005a51386..f74f27e543d9 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2301,7 +2301,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; } From 45bbffcee8adaf1274f104746696199d83337be8 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 9 Feb 2018 08:03:22 +1100 Subject: [PATCH 795/839] Remove unused code --- ProcessHacker/include/procprp.h | 2 -- ProcessHacker/procprp.c | 30 +----------------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/ProcessHacker/include/procprp.h b/ProcessHacker/include/procprp.h index 065926df5f2a..db5f5d7be97c 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; diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index 8fde7b3904dd..107806984313 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -101,7 +101,6 @@ PPH_PROCESS_PROPCONTEXT PhCreateProcessPropContext( memcpy(&propContext->PropSheetHeader, &propSheetHeader, sizeof(PROPSHEETHEADER)); PhSetReference(&propContext->ProcessItem, ProcessItem); - PhInitializeEvent(&propContext->CreatedEvent); return propContext; } @@ -549,9 +548,6 @@ NTSTATUS PhpProcessPropertiesThreadStart( PPH_PROCESS_PROPCONTEXT PropContext = (PPH_PROCESS_PROPCONTEXT)Parameter; PPH_PROCESS_PROPPAGECONTEXT newPage; PPH_STRING startPage; - HWND hwnd; - BOOL result; - MSG message; PhInitializeAutoPool(&autoPool); @@ -689,33 +685,9 @@ NTSTATUS PhpProcessPropertiesThreadStart( PropContext->PropSheetHeader.dwFlags |= PSH_USEPSTARTPAGE; PropContext->PropSheetHeader.pStartPage = startPage->Buffer; - hwnd = (HWND)PropertySheet(&PropContext->PropSheetHeader); + PhModalPropertySheet(&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); From ff534a7118dd82c39fd38831267b62e63eba21dd Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 9 Feb 2018 08:12:59 +1100 Subject: [PATCH 796/839] Update runas dialog; Fix autosuggest, Fix MRU list, Update dialog layout --- ProcessHacker/ProcessHacker.rc | 27 +- ProcessHacker/include/phapp.h | 17 + ProcessHacker/include/phsvcapi.h | 1 + ProcessHacker/phsvc/clapi.c | 1 + ProcessHacker/phsvc/svcapi.c | 1 + ProcessHacker/resource.h | 8 +- ProcessHacker/runas.c | 1018 +++++++++++++++++++++--------- 7 files changed, 766 insertions(+), 307 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 030c715f4038..32a0990ecc10 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -745,30 +745,29 @@ BEGIN DEFPUSHBUTTON "Close",IDOK,280,200,50,14 END -IDD_RUNAS DIALOGEX 0, 0, 278, 127 +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_STATIC,7,7,160,8 + LTEXT "Enter the command to start as the specified user.",IDC_TITLE,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 + 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 - 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 + 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 @@ -1935,7 +1934,7 @@ BEGIN IDD_RUNAS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 271 + RIGHTMARGIN, 286 TOPMARGIN, 7 BOTTOMMARGIN, 120 END diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 651992748e26..65b9200bd700 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -569,6 +569,7 @@ typedef struct _PH_RUNAS_SERVICE_PARAMETERS PWSTR DesktopName; BOOLEAN UseLinkedToken; PWSTR ServiceName; + BOOLEAN CreateSuspendedProcess; } PH_RUNAS_SERVICE_PARAMETERS, *PPH_RUNAS_SERVICE_PARAMETERS; VOID PhShowRunAsDialog( @@ -597,6 +598,22 @@ PhExecuteRunAsCommand2( ); // 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 ); diff --git a/ProcessHacker/include/phsvcapi.h b/ProcessHacker/include/phsvcapi.h index ad6c9105e80b..a4a1b5d1bab8 100644 --- a/ProcessHacker/include/phsvcapi.h +++ b/ProcessHacker/include/phsvcapi.h @@ -60,6 +60,7 @@ typedef union _PHSVC_API_EXECUTERUNASCOMMAND PH_RELATIVE_STRINGREF DesktopName; BOOLEAN UseLinkedToken; PH_RELATIVE_STRINGREF ServiceName; + BOOLEAN CreateSuspendedProcess; } i; } PHSVC_API_EXECUTERUNASCOMMAND, *PPHSVC_API_EXECUTERUNASCOMMAND; diff --git a/ProcessHacker/phsvc/clapi.c b/ProcessHacker/phsvc/clapi.c index 8afb38cf4cad..724571350472 100644 --- a/ProcessHacker/phsvc/clapi.c +++ b/ProcessHacker/phsvc/clapi.c @@ -283,6 +283,7 @@ NTSTATUS PhSvcpCallExecuteRunAsCommand( 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; diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index dda20bdde1a8..e649469d03d6 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -395,6 +395,7 @@ NTSTATUS PhSvcpCaptureRunAsServiceParameters( 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; } diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index 8727dc0e5325..d68f44785fb2 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -204,7 +204,10 @@ #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 @@ -406,6 +409,7 @@ #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 @@ -738,9 +742,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 246 +#define _APS_NEXT_RESOURCE_VALUE 247 #define _APS_NEXT_COMMAND_VALUE 40297 -#define _APS_NEXT_CONTROL_VALUE 1405 +#define _APS_NEXT_CONTROL_VALUE 1406 #define _APS_NEXT_SYMED_VALUE 170 #endif #endif diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 1c93b78e143a..6fd8865eb0bc 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -3,6 +3,7 @@ * run as dialog * * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2018 dmex * * This file is part of Process Hacker. * @@ -63,6 +64,8 @@ #include #include #include +#include +#include #include #include @@ -71,14 +74,81 @@ #include #include #include +#include typedef struct _RUNAS_DIALOG_CONTEXT { + HWND ProgramComboBoxWindowHandle; + HWND UserComboBoxWindowHandle; + HWND TypeComboBoxWindowHandle; + HWND PasswordEditWindowHandle; + HWND SessionEditWindowHandle; + HWND DesktopEditWindowHandle; HANDLE ProcessId; PPH_LIST DesktopList; 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_ PBYTE *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, @@ -119,63 +189,16 @@ VOID PhShowRunAsDialog( _In_opt_ HANDLE ProcessId ) { - RUNAS_DIALOG_CONTEXT context; - - context.ProcessId = ProcessId; - context.DesktopList = NULL; - DialogBoxParam( PhInstanceHandle, MAKEINTRESOURCE(IDD_RUNAS), - ParentWindowHandle, + NULL, PhpRunAsDlgProc, - (LPARAM)&context + (LPARAM)ProcessId ); } -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( +BOOLEAN IsServiceAccount( _In_ PPH_STRING UserName ) { @@ -193,7 +216,34 @@ static BOOLEAN IsServiceAccount( } } -static PPH_STRING GetCurrentWinStaName( +BOOLEAN IsCurrentUserAccount( + _In_ PPH_STRING UserName + ) +{ + PTOKEN_USER userToken; + PPH_STRING userName; + + if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &userToken))) + { + if (userName = PhGetSidFullName(userToken->User.Sid, TRUE, NULL)) + { + if (PhEndsWithString(userName, UserName, TRUE)) + { + PhDereferenceObject(userName); + PhFree(userToken); + return TRUE; + } + + PhDereferenceObject(userName); + } + + PhFree(userToken); + } + + return FALSE; +} + +PPH_STRING GetCurrentWinStaName( VOID ) { @@ -219,6 +269,351 @@ static PPH_STRING GetCurrentWinStaName( } } +BOOLEAN PhpInitializeNetApi(VOID) +{ + PVOID netapiModuleHandle; + + if (!(netapiModuleHandle = LoadLibrary(L"netapi32.dll"))) + return FALSE; + + NetUserEnum_I = PhGetProcedureAddress(netapiModuleHandle, "NetUserEnum", 0); + NetApiBufferFree_I = PhGetProcedureAddress(netapiModuleHandle, "NetApiBufferFree", 0); + + if (!NetUserEnum_I && !NetApiBufferFree_I) + { + FreeLibrary(netapiModuleHandle); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhpInitializeMRUList(VOID) +{ + PVOID comctl32ModuleHandle; + + if (!(comctl32ModuleHandle = LoadLibrary(L"comctl32.dll"))) + return FALSE; + + CreateMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "CreateMRUListW", 0); + AddMRUString_I = PhGetProcedureAddress(comctl32ModuleHandle, "AddMRUStringW", 0); + EnumMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "EnumMRUListW", 0); + FreeMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "FreeMRUList", 0); + + if (!CreateMRUList_I && !AddMRUString_I && !EnumMRUList_I && !FreeMRUList_I) + { + FreeLibrary(comctl32ModuleHandle); + return FALSE; + } + + return TRUE; +} + +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_ PWSTR CommandLine + ) +{ + HANDLE listHandle; + PPH_STRING commandString; + + if (!(listHandle = PhpCreateRunMRUList())) + return; + + commandString = PhConcatStrings2(CommandLine, L"\\1"); + 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++) + { + static PH_STRINGREF keyNamePlusEquals = PH_STRINGREF_INIT(L"\\1"); + PPH_STRING programName; + PH_STRINGREF nameSr; + PH_STRINGREF firstPart; + PH_STRINGREF remainingPart; + WCHAR entry[MAX_PATH]; + + if (!EnumMRUList_I( + listHandle, + i, + entry, + ARRAYSIZE(entry) + )) + { + break; + } + + PhInitializeStringRefLongHint(&nameSr, entry); + + if (!PhSplitStringRefAtString(&nameSr, &keyNamePlusEquals, TRUE, &firstPart, &remainingPart)) + { + ComboBox_AddString(ComboBoxHandle, entry); + continue; + } + + programName = PhCreateString2(&firstPart); + ComboBox_AddString(ComboBoxHandle, PhGetString(programName)); + PhDereferenceObject(programName); + } + + FreeMRUList_I(listHandle); +} + +static VOID PhpAddAccountsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + NET_API_STATUS status; + LPUSER_INFO_0 userinfoArray = NULL; + ULONG userinfoMaxLength = MAX_PREFERRED_LENGTH; + ULONG userinfoEntriesRead = 0; + ULONG userinfoTotalEntries = 0; + ULONG userinfoResumeHandle = 0; + + ComboBox_ResetContent(ComboBoxHandle); + ComboBox_AddString(ComboBoxHandle, L"NT AUTHORITY\\SYSTEM"); + ComboBox_AddString(ComboBoxHandle, L"NT AUTHORITY\\LOCAL SERVICE"); + ComboBox_AddString(ComboBoxHandle, L"NT AUTHORITY\\NETWORK SERVICE"); + + if (!PhpInitializeNetApi()) + return; + + NetUserEnum_I( + NULL, + 0, + FILTER_NORMAL_ACCOUNT, + (PBYTE*)&userinfoArray, + userinfoMaxLength, + &userinfoEntriesRead, + &userinfoTotalEntries, + &userinfoResumeHandle + ); + + if (userinfoArray) + { + NetApiBufferFree_I(userinfoArray); + userinfoArray = NULL; + } + + status = NetUserEnum_I( + NULL, + 0, + FILTER_NORMAL_ACCOUNT, + (PBYTE*)&userinfoArray, + userinfoMaxLength, + &userinfoEntriesRead, + &userinfoTotalEntries, + &userinfoResumeHandle + ); + + if (status == NERR_Success) + { + PTOKEN_USER userToken; + PPH_STRING userDomainName = NULL; + + if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &userToken))) + { + PPH_STRING username; + + if (username = PhGetSidFullName(userToken->User.Sid, TRUE, NULL)) + { + PhpSplitUserName(username->Buffer, &userDomainName, NULL); + PhDereferenceObject(username); + } + + PhFree(userToken); + } + + 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 PhpAddSessionsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + PSESSIONIDW sessions; + ULONG numberOfSessions; + ULONG i; + + ComboBox_ResetContent(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] = 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); + } + + + PPH_RUNAS_SESSION_ITEM entry; + + entry = PhAllocate(sizeof(PH_RUNAS_SESSION_ITEM)); + entry->SessionId = sessions[i].SessionId; + entry->SessionName = menuString; + + INT itemIndex = ComboBox_AddString(ComboBoxHandle, menuString->Buffer); + + if (itemIndex != CB_ERR) + { + ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + } + } + + WinStationFreeMemory(sessions); + } +} + static BOOL CALLBACK EnumDesktopsCallback( _In_ PWSTR DesktopName, _In_ LPARAM Context @@ -231,11 +626,101 @@ static BOOL CALLBACK EnumDesktopsCallback( context->CurrentWinStaName->Buffer, L"\\", DesktopName - )); + )); return TRUE; } +static VOID PhpAddDesktopsToComboBox( + _In_ PRUNAS_DIALOG_CONTEXT Context, + _In_ HWND ComboBoxHandle + ) +{ + ULONG i; + + Context->DesktopList = PhCreateList(10); + Context->CurrentWinStaName = GetCurrentWinStaName(); + ComboBox_ResetContent(ComboBoxHandle); + + EnumDesktops(GetProcessWindowStation(), EnumDesktopsCallback, (LPARAM)Context); + + for (i = 0; i < Context->DesktopList->Count; i++) + { + PPH_RUNAS_DESKTOP_ITEM entry; + + entry = PhAllocate(sizeof(PH_RUNAS_DESKTOP_ITEM)); + entry->DesktopName = ((PPH_STRING)Context->DesktopList->Items[i]); + + INT itemIndex = ComboBox_AddString(ComboBoxHandle, entry->DesktopName->Buffer); + + if (itemIndex != CB_ERR) + { + ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + } + } + + PhClearList(Context->DesktopList); // leak + PhDereferenceObject(Context->CurrentWinStaName); +} + +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; + PH_STRINGREF desktopName; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->DesktopInfo, &desktopName); + + if ((sessionCount = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + 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, TRUE)) + { + ComboBox_SetCurSel(ComboBoxHandle, i); + break; + } + } +} + INT_PTR CALLBACK PhpRunAsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -245,14 +730,16 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { PRUNAS_DIALOG_CONTEXT context; - if (uMsg != WM_INITDIALOG) + if (uMsg == WM_INITDIALOG) { - context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + context = PhAllocate(sizeof(RUNAS_DIALOG_CONTEXT)); + memset(context, 0, sizeof(RUNAS_DIALOG_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PRUNAS_DIALOG_CONTEXT)lParam; - PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -262,43 +749,51 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { case WM_INITDIALOG: { - HWND typeComboBoxHandle = GetDlgItem(hwndDlg, IDC_TYPE); - HWND userNameComboBoxHandle = GetDlgItem(hwndDlg, IDC_USERNAME); - ULONG sessionId; + 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)); + 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; - if (SHAutoComplete_I) - { - SHAutoComplete_I( - GetDlgItem(hwndDlg, IDC_PROGRAM), - SHACF_AUTOAPPEND_FORCE_ON | SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_ONLY - ); - } + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); - 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); + { + COMBOBOXINFO info = { sizeof(COMBOBOXINFO) }; - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\SYSTEM"); - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\LOCAL SERVICE"); - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\NETWORK SERVICE"); + if (SendMessage(context->ProgramComboBoxWindowHandle, CB_GETCOMBOBOXINFO, 0, (LPARAM)&info)) + { + if (SHAutoComplete_I) + SHAutoComplete_I(info.hwndItem, SHACF_DEFAULT); + } + } - PhpAddAccountsToComboBox(userNameComboBoxHandle); + 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); - if (NT_SUCCESS(PhGetProcessSessionId(NtCurrentProcess(), &sessionId))) - SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE); + PhpAddProgramsToComboBox(context->ProgramComboBoxWindowHandle); + PhpAddAccountsToComboBox(context->UserComboBoxWindowHandle); + PhpAddSessionsToComboBox(context->SessionEditWindowHandle); + PhpAddDesktopsToComboBox(context, context->DesktopEditWindowHandle); - SetDlgItemText(hwndDlg, IDC_DESKTOP, L"WinSta0\\Default"); - SetDlgItemText(hwndDlg, IDC_PROGRAM, PhaGetStringSetting(L"RunAsProgram")->Buffer); + SetDefaultProgramEntry(context->ProgramComboBoxWindowHandle); + SetDefaultSessionEntry(context->SessionEditWindowHandle); + SetDefaultDesktopEntry(context, context->DesktopEditWindowHandle); if (!context->ProcessId) { - SetDlgItemText(hwndDlg, IDC_USERNAME, - PH_AUTO_T(PH_STRING, PhGetStringSetting(L"RunAsUserName"))->Buffer); + SetWindowText( + context->UserComboBoxWindowHandle, + 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); @@ -326,7 +821,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { if (userName = PhGetSidFullName(user->User.Sid, TRUE, NULL)) { - SetDlgItemText(hwndDlg, IDC_USERNAME, userName->Buffer); + SetWindowText(context->UserComboBoxWindowHandle, userName->Buffer); PhDereferenceObject(userName); } @@ -339,16 +834,18 @@ INT_PTR CALLBACK PhpRunAsDlgProc( NtClose(processHandle); } - EnableWindow(GetDlgItem(hwndDlg, IDC_USERNAME), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_TYPE), FALSE); + EnableWindow(context->UserComboBoxWindowHandle, FALSE); + EnableWindow(context->PasswordEditWindowHandle, FALSE); + EnableWindow(context->TypeComboBoxWindowHandle, FALSE); } - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_PROGRAM), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_PROGRAM), 0, -1); + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->ProgramComboBoxWindowHandle, TRUE); + Edit_SetSel(context->ProgramComboBoxWindowHandle, -1, -1); //if (!PhGetOwnTokenAttributes().Elevated) // SendMessage(GetDlgItem(hwndDlg, IDOK), BCM_SETSHIELD, 0, TRUE); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); } break; case WM_DESTROY: @@ -357,11 +854,36 @@ INT_PTR CALLBACK PhpRunAsDlgProc( PhDereferenceObject(context->DesktopList); PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); } break; case WM_COMMAND: - { - switch (LOWORD(wParam)) + { + 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, context->DesktopEditWindowHandle); + SetDefaultDesktopEntry(context, context->DesktopEditWindowHandle); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); @@ -369,45 +891,85 @@ INT_PTR CALLBACK PhpRunAsDlgProc( case IDOK: { NTSTATUS status; - PPH_STRING program; - PPH_STRING userName; - PPH_STRING password; + 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; - ULONG logonType; - ULONG sessionId; - PPH_STRING desktopName; - BOOLEAN useLinkedToken; + PPH_STRING desktopName = NULL; + INT selectionIndex = CB_ERR; - program = PhaGetDlgItemText(hwndDlg, IDC_PROGRAM); - userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME); - logonTypeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE); + 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) + if (PhFindCharInString(username, 0, '\\') == -1) { PSID sid; PPH_STRING newUserName; - if (NT_SUCCESS(PhLookupName(&userName->sr, &sid, NULL, NULL))) + if (NT_SUCCESS(PhLookupName(&username->sr, &sid, NULL, NULL))) { if (newUserName = PH_AUTO(PhGetSidFullName(sid, TRUE, NULL))) - userName = newUserName; + username = newUserName; PhFree(sid); } } - if (!IsServiceAccount(userName)) - password = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD)); - else - password = NULL; + if (!IsServiceAccount(username)) + { + password = PhGetWindowText(context->PasswordEditWindowHandle); + SetWindowText(context->PasswordEditWindowHandle, L""); + } - sessionId = GetDlgItemInt(hwndDlg, IDC_SESSIONID, NULL, FALSE); - desktopName = PhaGetDlgItemText(hwndDlg, IDC_DESKTOP); - useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED; + //if (IsCurrentUserAccount(username)) + //{ + // status = PhCreateProcessWin32( + // NULL, + // program->Buffer, + // NULL, + // NULL, + // 0, + // NULL, + // NULL, + // NULL + // ); + //} if (PhFindIntegerSiKeyValuePairs( PhpLogonTypePairs, @@ -431,7 +993,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( PPH_STRING domainPart = NULL; PPH_STRING userPart = NULL; - PhpSplitUserName(userName->Buffer, &domainPart, &userPart); + PhpSplitUserName(username->Buffer, &domainPart, &userPart); memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); createInfo.CommandLine = PhGetString(program); @@ -447,7 +1009,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( status = PhCreateProcessAsUser( &createInfo, - PH_CREATE_PROCESS_WITH_PROFILE, + PH_CREATE_PROCESS_WITH_PROFILE | (createSuspended ? PH_CREATE_PROCESS_SUSPENDED : 0), NULL, NULL, NULL @@ -458,16 +1020,17 @@ INT_PTR CALLBACK PhpRunAsDlgProc( } else { - status = PhExecuteRunAsCommand2( + status = PhExecuteRunAsCommand3( hwndDlg, PhGetString(program), - userName->Buffer, + PhGetString(username), PhGetStringOrEmpty(password), logonType, context->ProcessId, sessionId, - desktopName->Buffer, - useLinkedToken + PhGetString(desktopName), + useLinkedToken, + createSuspended ); } } @@ -485,12 +1048,14 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (!NT_SUCCESS(status)) { if (status != STATUS_CANCELLED) - PhShowStatus(hwndDlg, L"Unable to start the program", status, 0); + 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); + PhpAddRunMRUListEntry(program->Buffer); + + //PhSetStringSetting2(L"RunAsProgram", &program->sr); + PhSetStringSetting2(L"RunAsUserName", &username->sr); EndDialog(hwndDlg, IDOK); } } @@ -506,14 +1071,14 @@ INT_PTR CALLBACK PhpRunAsDlgProc( fileDialog = PhCreateOpenFileDialog(); PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, PhaGetDlgItemText(hwndDlg, IDC_PROGRAM)->Buffer); + PhSetFileDialogFileName(fileDialog, PH_AUTO_T(PH_STRING, PhGetWindowText(context->ProgramComboBoxWindowHandle))->Buffer); if (PhShowFileDialog(hwndDlg, fileDialog)) { PPH_STRING fileName; fileName = PhGetFileDialogFileName(fileDialog); - SetDlgItemText(hwndDlg, IDC_PROGRAM, fileName->Buffer); + SetWindowText(context->ProgramComboBoxWindowHandle, fileName->Buffer); PhDereferenceObject(fileName); } @@ -522,184 +1087,33 @@ INT_PTR CALLBACK PhpRunAsDlgProc( break; case IDC_USERNAME: { - PPH_STRING userName = NULL; + PPH_STRING username = NULL; - if (!context->ProcessId && HIWORD(wParam) == CBN_SELCHANGE) + if (!context->ProcessId && GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) { - userName = PH_AUTO(PhGetComboBoxString(GetDlgItem(hwndDlg, IDC_USERNAME), -1)); + username = PH_AUTO(PhGetComboBoxString(context->UserComboBoxWindowHandle, -1)); } else if (!context->ProcessId && ( - HIWORD(wParam) == CBN_EDITCHANGE || - HIWORD(wParam) == CBN_CLOSEUP + GET_WM_COMMAND_CMD(wParam, lParam) == CBN_EDITCHANGE || + GET_WM_COMMAND_CMD(wParam, lParam) == CBN_CLOSEUP )) { - userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME); + username = PH_AUTO(PhGetWindowText(context->UserComboBoxWindowHandle)); } - if (userName) + if (username) { - if (IsServiceAccount(userName)) + if (IsServiceAccount(username)) { - EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Service", FALSE); + EnableWindow(context->PasswordEditWindowHandle, FALSE); + PhSelectComboBoxString(context->TypeComboBoxWindowHandle, 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 - ); + EnableWindow(context->PasswordEditWindowHandle, TRUE); + PhSelectComboBoxString(context->TypeComboBoxWindowHandle, L"Interactive", 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; } @@ -744,10 +1158,9 @@ VOID PhSetDesktopWinStaAccess( (ULONG)sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(allAppPackagesSid); securityDescriptor = PhAllocate(allocationLength); - dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); + 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); @@ -909,6 +1322,22 @@ NTSTATUS PhExecuteRunAsCommand2( _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; @@ -925,6 +1354,7 @@ NTSTATUS PhExecuteRunAsCommand2( 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] != 0) @@ -994,13 +1424,17 @@ static VOID PhpSplitUserName( if (PhSplitStringRefAtChar(&userName, '\\', &domainPart, &userPart)) { - *DomainPart = PhCreateString2(&domainPart); - *UserPart = PhCreateString2(&userPart); + if (DomainPart) + *DomainPart = PhCreateString2(&domainPart); + if (UserPart) + *UserPart = PhCreateString2(&userPart); } else { - *DomainPart = NULL; - *UserPart = PhCreateString2(&userName); + if (DomainPart) + *DomainPart = NULL; + if (UserPart) + *UserPart = PhCreateString2(&userName); } } @@ -1134,6 +1568,8 @@ NTSTATUS PhInvokeRunAsService( if (Parameters->UseLinkedToken) flags |= PH_CREATE_PROCESS_USE_LINKED_TOKEN; + if (Parameters->CreateSuspendedProcess) + flags |= PH_CREATE_PROCESS_SUSPENDED; status = PhCreateProcessAsUser( &createInfo, From 0950643eed9ac33ad9dcfdbf6b8c0d3933bd427b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 00:12:41 +1100 Subject: [PATCH 797/839] Add NtCurrentProcessToken macro; Tidy up runas code --- ProcessHacker/main.c | 34 +++--------- ProcessHacker/runas.c | 119 ++++++++++++++++++++--------------------- phlib/include/lsasup.h | 8 +++ phlib/lsasup.c | 17 ++++++ phlib/native.c | 66 +++++++++++++---------- phnt/include/ntpsapi.h | 5 ++ 6 files changed, 135 insertions(+), 114 deletions(-) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 3fdcb508f75b..5efe777b2adf 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -116,7 +116,6 @@ INT WINAPI wWinMain( #ifdef DEBUG PHP_BASE_THREAD_DBG dbg; #endif - HANDLE currentTokenHandle; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); @@ -133,33 +132,14 @@ INT WINAPI wWinMain( 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) + if (!(PhApplicationFileName = PhGetApplicationFileName())) PhApplicationFileName = PhCreateString(L"ProcessHacker.exe"); - if (!PhApplicationDirectory) + if (!(PhApplicationDirectory = PhGetApplicationDirectory())) PhApplicationDirectory = PhReferenceEmptyString(); + if (!(PhLocalSystemName = PhGetSidFullName(&PhSeLocalSystemSid, TRUE, NULL))) + PhLocalSystemName = PhCreateString(L"NT AUTHORITY\\SYSTEM"); + if (!(PhCurrentUserName = PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE))) + PhLocalSystemName = PhReferenceEmptyString(); PhpProcessStartupParameters(); PhSettingsInitialization(); @@ -1324,7 +1304,7 @@ VOID PhpEnablePrivileges( { HANDLE tokenHandle; - if (NT_SUCCESS(NtOpenProcessToken( + if (NT_SUCCESS(PhOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 6fd8865eb0bc..c99eb008b8c1 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -220,24 +220,17 @@ BOOLEAN IsCurrentUserAccount( _In_ PPH_STRING UserName ) { - PTOKEN_USER userToken; PPH_STRING userName; - if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &userToken))) + if (userName = PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE)) { - if (userName = PhGetSidFullName(userToken->User.Sid, TRUE, NULL)) + if (PhEndsWithString(userName, UserName, TRUE)) { - if (PhEndsWithString(userName, UserName, TRUE)) - { - PhDereferenceObject(userName); - PhFree(userToken); - return TRUE; - } - PhDereferenceObject(userName); + return TRUE; } - PhFree(userToken); + PhDereferenceObject(userName); } return FALSE; @@ -271,42 +264,60 @@ PPH_STRING GetCurrentWinStaName( BOOLEAN PhpInitializeNetApi(VOID) { - PVOID netapiModuleHandle; + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID netapiModuleHandle = NULL; - if (!(netapiModuleHandle = LoadLibrary(L"netapi32.dll"))) - return FALSE; + if (PhBeginInitOnce(&initOnce)) + { + if (netapiModuleHandle = LoadLibrary(L"netapi32.dll")) + { + NetUserEnum_I = PhGetProcedureAddress(netapiModuleHandle, "NetUserEnum", 0); + NetApiBufferFree_I = PhGetProcedureAddress(netapiModuleHandle, "NetApiBufferFree", 0); + } - NetUserEnum_I = PhGetProcedureAddress(netapiModuleHandle, "NetUserEnum", 0); - NetApiBufferFree_I = PhGetProcedureAddress(netapiModuleHandle, "NetApiBufferFree", 0); + if (!NetUserEnum_I && !NetApiBufferFree_I) + { + FreeLibrary(netapiModuleHandle); + netapiModuleHandle = NULL; + } - if (!NetUserEnum_I && !NetApiBufferFree_I) - { - FreeLibrary(netapiModuleHandle); - return FALSE; + PhEndInitOnce(&initOnce); } - return TRUE; + if (netapiModuleHandle) + return TRUE; + + return FALSE; } BOOLEAN PhpInitializeMRUList(VOID) { - PVOID comctl32ModuleHandle; + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID comctl32ModuleHandle = NULL; - if (!(comctl32ModuleHandle = LoadLibrary(L"comctl32.dll"))) - return FALSE; + if (PhBeginInitOnce(&initOnce)) + { + if (comctl32ModuleHandle = LoadLibrary(L"comctl32.dll")) + { + CreateMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "CreateMRUListW", 0); + AddMRUString_I = PhGetProcedureAddress(comctl32ModuleHandle, "AddMRUStringW", 0); + EnumMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "EnumMRUListW", 0); + FreeMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "FreeMRUList", 0); + } - CreateMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "CreateMRUListW", 0); - AddMRUString_I = PhGetProcedureAddress(comctl32ModuleHandle, "AddMRUStringW", 0); - EnumMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "EnumMRUListW", 0); - FreeMRUList_I = PhGetProcedureAddress(comctl32ModuleHandle, "FreeMRUList", 0); + if (!CreateMRUList_I && !AddMRUString_I && !EnumMRUList_I && !FreeMRUList_I) + { + FreeLibrary(comctl32ModuleHandle); + comctl32ModuleHandle = NULL; + } - if (!CreateMRUList_I && !AddMRUString_I && !EnumMRUList_I && !FreeMRUList_I) - { - FreeLibrary(comctl32ModuleHandle); - return FALSE; + PhEndInitOnce(&initOnce); } - return TRUE; + if (comctl32ModuleHandle) + return TRUE; + + return FALSE; } static HANDLE PhpCreateRunMRUList( @@ -328,19 +339,21 @@ static HANDLE PhpCreateRunMRUList( } static VOID PhpAddRunMRUListEntry( - _In_ PWSTR CommandLine + _In_ PPH_STRING CommandLine ) { + static PH_STRINGREF prefixSr = PH_STRINGREF_INIT(L"\\1"); HANDLE listHandle; PPH_STRING commandString; if (!(listHandle = PhpCreateRunMRUList())) return; - commandString = PhConcatStrings2(CommandLine, L"\\1"); + commandString = PhConcatStringRef2(&CommandLine->sr, &prefixSr); + AddMRUString_I(listHandle, commandString->Buffer); - PhDereferenceObject(commandString); + PhDereferenceObject(commandString); FreeMRUList_I(listHandle); } @@ -366,7 +379,6 @@ static VOID PhpAddProgramsToComboBox( for (INT i = 0; i < listCount; i++) { - static PH_STRINGREF keyNamePlusEquals = PH_STRINGREF_INIT(L"\\1"); PPH_STRING programName; PH_STRINGREF nameSr; PH_STRINGREF firstPart; @@ -385,7 +397,7 @@ static VOID PhpAddProgramsToComboBox( PhInitializeStringRefLongHint(&nameSr, entry); - if (!PhSplitStringRefAtString(&nameSr, &keyNamePlusEquals, TRUE, &firstPart, &remainingPart)) + if (!PhSplitStringRefAtString(&nameSr, &prefixSr, TRUE, &firstPart, &remainingPart)) { ComboBox_AddString(ComboBoxHandle, entry); continue; @@ -448,20 +460,13 @@ static VOID PhpAddAccountsToComboBox( if (status == NERR_Success) { - PTOKEN_USER userToken; + PPH_STRING username; PPH_STRING userDomainName = NULL; - if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &userToken))) + if (username = PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE)) { - PPH_STRING username; - - if (username = PhGetSidFullName(userToken->User.Sid, TRUE, NULL)) - { - PhpSplitUserName(username->Buffer, &userDomainName, NULL); - PhDereferenceObject(username); - } - - PhFree(userToken); + PhpSplitUserName(username->Buffer, &userDomainName, NULL); + PhDereferenceObject(username); } for (ULONG i = 0; i < userinfoEntriesRead; i++) @@ -802,7 +807,6 @@ INT_PTR CALLBACK PhpRunAsDlgProc( { HANDLE processHandle; HANDLE tokenHandle; - PTOKEN_USER user; PPH_STRING userName; if (NT_SUCCESS(PhOpenProcess( @@ -817,15 +821,10 @@ INT_PTR CALLBACK PhpRunAsDlgProc( &tokenHandle ))) { - if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user))) + if (userName = PhGetTokenUserString(tokenHandle, TRUE)) { - if (userName = PhGetSidFullName(user->User.Sid, TRUE, NULL)) - { - SetWindowText(context->UserComboBoxWindowHandle, userName->Buffer); - PhDereferenceObject(userName); - } - - PhFree(user); + SetWindowText(context->UserComboBoxWindowHandle, userName->Buffer); + PhDereferenceObject(userName); } NtClose(tokenHandle); @@ -1052,7 +1051,7 @@ INT_PTR CALLBACK PhpRunAsDlgProc( } else if (status != STATUS_TIMEOUT) { - PhpAddRunMRUListEntry(program->Buffer); + PhpAddRunMRUListEntry(program); //PhSetStringSetting2(L"RunAsProgram", &program->sr); PhSetStringSetting2(L"RunAsUserName", &username->sr); @@ -1503,7 +1502,7 @@ NTSTATUS PhRunAsServiceStart( // Enable some required privileges. - if (NT_SUCCESS(NtOpenProcessToken( + if (NT_SUCCESS(PhOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle diff --git a/phlib/include/lsasup.h b/phlib/include/lsasup.h index 3784f413bd6d..dd16247d3a34 100644 --- a/phlib/include/lsasup.h +++ b/phlib/include/lsasup.h @@ -81,6 +81,14 @@ PhSidToStringSid( _In_ PSID Sid ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetTokenUserString( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN IncludeDomain + ); + #ifdef __cplusplus } #endif diff --git a/phlib/lsasup.c b/phlib/lsasup.c index d526ca2ffc67..64ba602c422b 100644 --- a/phlib/lsasup.c +++ b/phlib/lsasup.c @@ -480,3 +480,20 @@ 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; +} \ No newline at end of file diff --git a/phlib/native.c b/phlib/native.c index 1373e48883a1..95243249014a 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -78,22 +78,23 @@ PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( if (PhBeginInitOnce(&initOnce)) { - if (NT_SUCCESS(NtOpenProcessToken( - NtCurrentProcess(), - TOKEN_QUERY, - &attributes.TokenHandle - ))) - { - BOOLEAN elevated = TRUE; - TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; + BOOLEAN elevated = TRUE; + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; + + if (WindowsVersion >= WINDOWS_8) + attributes.TokenHandle = NtCurrentProcessToken(); + else + PhOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &attributes.TokenHandle); + if (attributes.TokenHandle) + { PhGetTokenIsElevated(attributes.TokenHandle, &elevated); PhGetTokenElevationType(attributes.TokenHandle, &elevationType); - - attributes.Elevated = elevated; - attributes.ElevationType = elevationType; } + attributes.Elevated = elevated; + attributes.ElevationType = elevationType; + PhEndInitOnce(&initOnce); } @@ -201,6 +202,14 @@ NTSTATUS PhOpenThread( 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( @@ -303,6 +312,14 @@ NTSTATUS PhOpenProcessToken( { NTSTATUS status; +#ifdef _DEBUG + if (WINDOWS_HAS_IMMERSIVE && ProcessHandle == NtCurrentProcess()) + { + *TokenHandle = NtCurrentProcessToken(); + return STATUS_SUCCESS; + } +#endif + if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) { status = KphOpenProcessToken( @@ -5574,35 +5591,30 @@ VOID PhpInitializePredefineKeys( ) { static UNICODE_STRING currentUserPrefix = RTL_CONSTANT_STRING(L"\\Registry\\User\\"); - NTSTATUS status; - HANDLE tokenHandle; 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 = 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 - ); + if (NT_SUCCESS(status = PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &tokenUser))) + { + stringSid.Buffer = stringSidBuffer; + stringSid.MaximumLength = sizeof(stringSidBuffer); - PhFree(tokenUser); - } + status = RtlConvertSidToUnicodeString( + &stringSid, + tokenUser->User.Sid, + FALSE + ); - NtClose(tokenHandle); + PhFree(tokenUser); } // Construct the current user key name. + if (NT_SUCCESS(status)) { currentUserKeyName = &PhPredefineKeyNames[PH_KEY_CURRENT_USER_NUMBER]; diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index ab6e2dfc4c73..28a083a8eedc 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -1004,6 +1004,11 @@ 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) + // Not NT, but useful. #define NtCurrentProcessId() (NtCurrentTeb()->ClientId.UniqueProcess) #define NtCurrentThreadId() (NtCurrentTeb()->ClientId.UniqueThread) From 2849881a2f797d3b1d6ab6b922b125a902ebdb0b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 00:38:59 +1100 Subject: [PATCH 798/839] Add PhGetAppContainerPackageName --- phlib/appresolver.c | 71 ++++++++++++++++++++++++++++++++++++ phlib/include/appresolver.h | 4 ++ phlib/include/appresolverp.h | 24 ++++++++++++ 3 files changed, 99 insertions(+) diff --git a/phlib/appresolver.c b/phlib/appresolver.c index 4082a433206b..25bb716e3e9c 100644 --- a/phlib/appresolver.c +++ b/phlib/appresolver.c @@ -24,6 +24,8 @@ #define CINTERFACE #include +#include +#include #include #include #include @@ -71,6 +73,45 @@ static PVOID PhpQueryStartMenuCacheInterface( return startMenuInterface; } +static BOOLEAN PhpQueryKernelAppCoreInitialized( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static BOOLEAN kernelAppCoreInitialized = FALSE; + + if (PhBeginInitOnce(&initOnce)) + { + if (WindowsVersion >= WINDOWS_8) + { + PVOID kernelAppBaseAddress; + + if (kernelAppBaseAddress = LoadLibrary(L"kernel.appcore.dll")) + { + AppContainerLookupMoniker_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerLookupMoniker", 0); + AppContainerFreeMemory_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerFreeMemory", 0); + AppContainerRegisterSid_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerRegisterSid", 0); + AppContainerUnregisterSid_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerUnregisterSid", 0); + AppPolicyGetWindowingModel_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppPolicyGetWindowingModel", 0); + } + + if ( + AppContainerLookupMoniker_I && + AppContainerFreeMemory_I && + AppContainerRegisterSid_I && + AppContainerUnregisterSid_I + ) + { + kernelAppCoreInitialized = TRUE; + } + } + + PhEndInitOnce(&initOnce); + } + + return kernelAppCoreInitialized; +} + BOOLEAN PhAppResolverGetAppIdForProcess( _In_ HANDLE ProcessId, _Out_ PPH_STRING *ApplicationUserModelId @@ -114,6 +155,36 @@ BOOLEAN PhAppResolverGetAppIdForProcess( return FALSE; } +PPH_STRING PhGetAppContainerPackageName( + _In_ PSID AppContainerSid + ) +{ + PPH_STRING packageFamilyName = NULL; + PWSTR packageMonikerName; + + if (!PhpQueryKernelAppCoreInitialized()) + return NULL; + + if (SUCCEEDED(AppContainerLookupMoniker_I(AppContainerSid, &packageMonikerName))) + { + packageFamilyName = PhConcatStrings2(packageMonikerName, L" (APP_PACKAGE)"); + AppContainerFreeMemory_I(packageMonikerName); + } + + return packageFamilyName; +} + +BOOLEAN PhGetAppWindowingModel( + _In_ HANDLE ProcessTokenHandle, + _Out_ AppPolicyWindowingModel *ProcessWindowingModelPolicy + ) +{ + if (!PhpQueryKernelAppCoreInitialized() && !AppPolicyGetWindowingModel_I) + return FALSE; + + return SUCCEEDED(AppPolicyGetWindowingModel_I(ProcessTokenHandle, ProcessWindowingModelPolicy)); +} + PPH_LIST PhGetPackageAssetsFromResourceFile( _In_ PWSTR FilePath ) diff --git a/phlib/include/appresolver.h b/phlib/include/appresolver.h index f1c31a91cb9e..dbea2044333a 100644 --- a/phlib/include/appresolver.h +++ b/phlib/include/appresolver.h @@ -32,6 +32,10 @@ BOOLEAN PhAppResolverGetAppIdForProcess( _Out_ PPH_STRING *ApplicationUserModelId ); +PPH_STRING PhGetAppContainerPackageName( + _In_ PSID AppContainerSid + ); + PPH_LIST PhGetPackageAssetsFromResourceFile( _In_ PWSTR FilePath ); diff --git a/phlib/include/appresolverp.h b/phlib/include/appresolverp.h index c556904470e6..4654c30abfe4 100644 --- a/phlib/include/appresolverp.h +++ b/phlib/include/appresolverp.h @@ -43,6 +43,30 @@ static IID IID_IResourceContext_I = { 0xE3C22B30, 0x8502, 0x4B2F,{ 0x91, 0x33, 0 // "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; +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 + ); + typedef enum _START_MENU_APP_ITEMS_FLAGS { SMAIF_DEFAULT = 0, From 56b9305fa1aa865a05caa1279f4a357071428a26 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 00:46:23 +1100 Subject: [PATCH 799/839] Add process token SID package name --- ProcessHacker/ProcessHacker.rc | 8 +++----- ProcessHacker/prpgtok.c | 1 - ProcessHacker/resource.h | 2 ++ ProcessHacker/tokprp.c | 25 ++++++++++++++----------- phlib/appresolver.c | 6 +++--- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 32a0990ecc10..106581f6e3c7 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -713,9 +713,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "Token" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - LTEXT "User:",IDC_STATIC,7,7,18,8 + 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_STATIC,7,18,32,8 + 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 @@ -723,9 +723,7 @@ BEGIN 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,183 + 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 END diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index af2946f77f66..5b46413e304a 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -75,7 +75,6 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( 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_ALL); 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); diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index d68f44785fb2..e82c9362d713 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -196,6 +196,8 @@ #define IDC_SOURCELUID 1060 #define IDC_APPCONTAINERSID 1060 #define IDC_LIST 1061 +#define IDC_TOKENUSER 1061 +#define IDC_TOKENSID 1062 #define IDC_PROCESSES 1063 #define IDC_SCAN 1064 #define IDC_SAVE 1065 diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 6ee67424fe8d..05532b519fdb 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -590,7 +591,6 @@ INT_PTR CALLBACK PhpTokenPageProc( SetDlgItemText(hwndDlg, IDC_USER, L"Unknown"); SetDlgItemText(hwndDlg, IDC_USERSID, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"Unknown"); if (NT_SUCCESS(tokenPageContext->OpenObject( &tokenHandle, @@ -606,6 +606,7 @@ INT_PTR CALLBACK PhpTokenPageProc( BOOLEAN isVirtualizationAllowed; BOOLEAN isVirtualizationEnabled; PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + PPH_STRING appContainerName; PPH_STRING appContainerSid; if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) @@ -652,30 +653,32 @@ INT_PTR CALLBACK PhpTokenPageProc( if (WINDOWS_HAS_IMMERSIVE) { + appContainerName = NULL; appContainerSid = NULL; if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) { if (appContainerInfo->TokenAppContainer) - appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); + { + appContainerName = PhGetAppContainerPackageName(appContainerInfo->TokenAppContainer); + appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); + } PhFree(appContainerInfo); } - if (appContainerSid) + if (appContainerName) { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, appContainerSid->Buffer); - PhDereferenceObject(appContainerSid); + SetDlgItemText(hwndDlg, IDC_USER, appContainerName->Buffer); + PhDereferenceObject(appContainerName); } - else + + if (appContainerSid) { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"N/A"); + SetDlgItemText(hwndDlg, IDC_USERSID, appContainerSid->Buffer); + PhDereferenceObject(appContainerSid); } } - else - { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"N/A"); - } ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); PhpTokenPageFreeListViewEntries(tokenPageContext); diff --git a/phlib/appresolver.c b/phlib/appresolver.c index 25bb716e3e9c..3087c198fc47 100644 --- a/phlib/appresolver.c +++ b/phlib/appresolver.c @@ -73,7 +73,7 @@ static PVOID PhpQueryStartMenuCacheInterface( return startMenuInterface; } -static BOOLEAN PhpQueryKernelAppCoreInitialized( +static BOOLEAN PhpKernelAppCoreInitialized( VOID ) { @@ -162,7 +162,7 @@ PPH_STRING PhGetAppContainerPackageName( PPH_STRING packageFamilyName = NULL; PWSTR packageMonikerName; - if (!PhpQueryKernelAppCoreInitialized()) + if (!PhpKernelAppCoreInitialized()) return NULL; if (SUCCEEDED(AppContainerLookupMoniker_I(AppContainerSid, &packageMonikerName))) @@ -179,7 +179,7 @@ BOOLEAN PhGetAppWindowingModel( _Out_ AppPolicyWindowingModel *ProcessWindowingModelPolicy ) { - if (!PhpQueryKernelAppCoreInitialized() && !AppPolicyGetWindowingModel_I) + if (!PhpKernelAppCoreInitialized() && !AppPolicyGetWindowingModel_I) return FALSE; return SUCCEEDED(AppPolicyGetWindowingModel_I(ProcessTokenHandle, ProcessWindowingModelPolicy)); From 8112a2442216370b5664d823d43398d694b76879 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 01:24:50 +1100 Subject: [PATCH 800/839] Export EtpRefreshUnloadedDlls --- ProcessHacker/ProcessHacker.def | 1 + phlib/include/phnative.h | 10 ++++ phlib/native.c | 96 +++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index c021c8433379..c9bbe2bba07f 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -273,6 +273,7 @@ EXPORTS PhGetProcessIsDotNetEx PhGetProcessMappedFileName PhGetProcessPebString + PhGetProcessUnloadedDlls PhGetProcessWindowTitle PhGetProcessWorkingSetInformation PhGetProcessWsCounters diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 75652c55fc2d..6676e3015984 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -279,6 +279,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 diff --git a/phlib/native.c b/phlib/native.c index 95243249014a..edba7b613e9f 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1233,6 +1233,102 @@ NTSTATUS PhGetProcessWsCounters( 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 unload a DLL. * From 41f830e5f5156781f4fda8e0545b0d7325b06160 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 02:48:18 +1100 Subject: [PATCH 801/839] Fix token usage --- ProcessHacker/memlists.c | 10 ++++++++-- ProcessHacker/tokprp.c | 7 ++++++- phlib/appresolver.c | 2 +- phlib/hndlinfo.c | 8 +------- phlib/native.c | 21 ++++++++++----------- plugins/DotNetTools/counters.c | 2 +- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index c61d9a321db0..e8c8c8a06d0c 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -170,6 +170,12 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( { case WM_INITDIALOG: { + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) + { + PhSetTokenPrivilege(tokenHandle, L"SeProfileSingleProcessPrivilege", NULL, SE_PRIVILEGE_ENABLED); + NtClose(tokenHandle); + } + PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); PhpUpdateMemoryListInfo(hwndDlg); @@ -237,9 +243,9 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( HANDLE tokenHandle; MEMORY_COMBINE_INFORMATION_EX combineInfo = { 0 }; - if (NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) { - PhSetTokenPrivilege(tokenHandle, SE_PROF_SINGLE_PROCESS_NAME, NULL, SE_PRIVILEGE_ENABLED); + PhSetTokenPrivilege(tokenHandle, L"SeProfileSingleProcessPrivilege", NULL, SE_PRIVILEGE_ENABLED); NtClose(tokenHandle); } diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 05532b519fdb..9477a0576b8e 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -669,7 +669,12 @@ INT_PTR CALLBACK PhpTokenPageProc( if (appContainerName) { - SetDlgItemText(hwndDlg, IDC_USER, appContainerName->Buffer); + PPH_STRING packageFamilyName; + + packageFamilyName = PhConcatStrings2(appContainerName->Buffer, L" (APP_PACKAGE)"); + SetDlgItemText(hwndDlg, IDC_USER, packageFamilyName->Buffer); + + PhDereferenceObject(packageFamilyName); PhDereferenceObject(appContainerName); } diff --git a/phlib/appresolver.c b/phlib/appresolver.c index 3087c198fc47..b9c0e546b505 100644 --- a/phlib/appresolver.c +++ b/phlib/appresolver.c @@ -167,7 +167,7 @@ PPH_STRING PhGetAppContainerPackageName( if (SUCCEEDED(AppContainerLookupMoniker_I(AppContainerSid, &packageMonikerName))) { - packageFamilyName = PhConcatStrings2(packageMonikerName, L" (APP_PACKAGE)"); + packageFamilyName = PhCreateString(packageMonikerName); AppContainerFreeMemory_I(packageMonikerName); } diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 877b530f9fd5..dff6e983bdcf 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -371,16 +371,10 @@ PPH_STRING PhFormatNativeKeyName( if (PhBeginInitOnce(&initOnce)) { - HANDLE currentTokenHandle; PTOKEN_USER tokenUser; PPH_STRING stringSid = NULL; - currentTokenHandle = PhGetOwnTokenAttributes().TokenHandle; - - if (currentTokenHandle && NT_SUCCESS(PhGetTokenUser( - currentTokenHandle, - &tokenUser - ))) + if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &tokenUser))) { stringSid = PhSidToStringSid(tokenUser->User.Sid); PhFree(tokenUser); diff --git a/phlib/native.c b/phlib/native.c index edba7b613e9f..d68af22a6183 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -74,7 +74,7 @@ PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( ) { static PH_INITONCE initOnce = PH_INITONCE_INIT; - static PH_TOKEN_ATTRIBUTES attributes; + static PH_TOKEN_ATTRIBUTES attributes = { 0 }; if (PhBeginInitOnce(&initOnce)) { @@ -82,9 +82,16 @@ PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; if (WindowsVersion >= WINDOWS_8) - attributes.TokenHandle = NtCurrentProcessToken(); + { + attributes.TokenHandle = NtCurrentProcessToken(); + } else - PhOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &attributes.TokenHandle); + { + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &tokenHandle))) + attributes.TokenHandle = tokenHandle; + } if (attributes.TokenHandle) { @@ -312,14 +319,6 @@ NTSTATUS PhOpenProcessToken( { NTSTATUS status; -#ifdef _DEBUG - if (WINDOWS_HAS_IMMERSIVE && ProcessHandle == NtCurrentProcess()) - { - *TokenHandle = NtCurrentProcessToken(); - return STATUS_SUCCESS; - } -#endif - if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) { status = KphOpenProcessToken( diff --git a/plugins/DotNetTools/counters.c b/plugins/DotNetTools/counters.c index badf758550f0..c299f2ac18ec 100644 --- a/plugins/DotNetTools/counters.c +++ b/plugins/DotNetTools/counters.c @@ -557,7 +557,7 @@ BOOLEAN OpenDotNetPublicControlBlock_V4( if (WINDOWS_HAS_IMMERSIVE && IsImmersive) { - if (NT_SUCCESS(NtOpenProcessToken(&tokenHandle, TOKEN_QUERY, ProcessHandle))) + if (NT_SUCCESS(NtOpenProcessToken(ProcessHandle, TOKEN_QUERY, &tokenHandle))) { ULONG returnLength = 0; From 448adba524441164f12ce916c341f70ea72b2d73 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 02:49:41 +1100 Subject: [PATCH 802/839] Fix build --- ProcessHacker/memlists.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index e8c8c8a06d0c..5bccafe87648 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -170,6 +170,8 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( { case WM_INITDIALOG: { + HANDLE tokenHandle; + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) { PhSetTokenPrivilege(tokenHandle, L"SeProfileSingleProcessPrivilege", NULL, SE_PRIVILEGE_ENABLED); From 6bc67f01edd0e8bd7760fae19f2ce99e461bf632 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 02:50:51 +1100 Subject: [PATCH 803/839] ExtendedTools: Fix unloaded dll event trace for system processes --- plugins/ExtendedTools/unldll.c | 95 +++++----------------------------- 1 file changed, 13 insertions(+), 82 deletions(-) diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index 02cd7cff34cf..d76a6d84bf03 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -64,88 +64,32 @@ BOOLEAN EtpRefreshUnloadedDlls( ) { 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); - - 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, + status = PhGetProcessUnloadedDlls( + Context->ProcessItem->ProcessId, + &capturedEventTrace, &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) + &capturedElementCount + ); + + if (!NT_SUCCESS(status)) { - status = STATUS_NO_MEMORY; - goto CleanupExit; + PhShowStatus(NULL, L"Unable to retrieve unload event trace information.", status, 0); + return FALSE; } - if (!NT_SUCCESS(status = NtReadVirtualMemory( - processHandle, - capturedEventTracePointer, - capturedEventTrace, - eventTraceSize, - NULL - ))) - goto CleanupExit; - - currentEvent = capturedEventTrace; - + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); ExtendedListView_SetRedraw(lvHandle, FALSE); - ListView_DeleteAllItems(lvHandle); + currentEvent = capturedEventTrace; + for (i = 0; i < capturedElementCount; i++) { PRTL_UNLOAD_EVENT_TRACE rtlEvent = currentEvent; @@ -199,20 +143,7 @@ BOOLEAN EtpRefreshUnloadedDlls( 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; - } + return NT_SUCCESS(status); } static INT NTAPI EtpNumberCompareFunction( From 2d38629ade2432d6cc62d7c8bfbb8078df4dffd4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 04:46:39 +1100 Subject: [PATCH 804/839] Fix options window crash --- ProcessHacker/options.c | 121 ++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index e9dba405afcc..8032b5d4e0f0 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -37,6 +37,7 @@ #include #define WM_PH_CHILD_EXIT (WM_APP + 301) +#define WM_PH_SHOWDIALOG (WM_APP + 302) INT_PTR CALLBACK PhpOptionsGeneralDlgProc( _In_ HWND hwndDlg, @@ -134,6 +135,8 @@ VOID PhpSetDefaultTaskManager( ); static HWND PhOptionsWindowHandle = NULL; +static HANDLE PhOptionsWindowThreadHandle = NULL; +static PH_EVENT PhOptionsWindowInitializedEvent = PH_EVENT_INIT; static PPH_LIST PhOptionsDialogList = NULL; static PH_LAYOUT_MANAGER WindowLayoutManager; @@ -148,19 +151,71 @@ static BOOLEAN RestartRequired = FALSE; // 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; +static BOOLEAN CurrentUserRunPresent = FALSE; +static BOOLEAN CurrentUserRunStartHidden = 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; -static HWND WindowHandleForElevate; +static PPH_STRING OldTaskMgrDebugger = NULL; +static HWND WindowHandleForElevate = NULL; // Highlighting -static HWND HighlightingListViewHandle; +static HWND HighlightingListViewHandle = NULL; + +NTSTATUS ShowUpdateDialogThread( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + NULL, + PhOptionsDialogProc + ); + + PhUpdateCachedSettings(); + ProcessHacker_SaveAllSettings(PhMainWndHandle); + PhInvalidateAllProcessNodes(); + PhReloadSettingsProcessTreeList(); + PhSiNotifyChangeSettings(); + + if (RestartRequired) + { + if (PhShowMessage2( + PhMainWndHandle, + 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); + } + } + + PhDeleteAutoPool(&autoPool); + + PhResetEvent(&PhOptionsWindowInitializedEvent); + + return STATUS_SUCCESS; +} VOID PhShowOptionsDialog( _In_ HWND ParentWindowHandle @@ -172,42 +227,14 @@ VOID PhShowOptionsDialog( } else { - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_OPTIONS), - NULL, - PhOptionsDialogProc - ); - - PhUpdateCachedSettings(); - ProcessHacker_SaveAllSettings(PhMainWndHandle); - PhInvalidateAllProcessNodes(); - PhReloadSettingsProcessTreeList(); - PhSiNotifyChangeSettings(); - - if (RestartRequired) + if (!PhTestEvent(&PhOptionsWindowInitializedEvent)) { - if (PhShowMessage2( - PhMainWndHandle, - 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); - } + PhCreateThread2(ShowUpdateDialogThread, NULL); + + PhWaitForEvent(&PhOptionsWindowInitializedEvent, NULL); } + + PostMessage(PhOptionsWindowHandle, WM_PH_SHOWDIALOG, 0, 0); } } @@ -354,6 +381,8 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhOptionsEnterSectionView(section); PhOptionsOnSize(); } + + PhSetEvent(&PhOptionsWindowInitializedEvent); } break; case WM_NCDESTROY: @@ -375,6 +404,16 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhDeleteLayoutManager(&WindowLayoutManager); } break; + case WM_PH_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; case WM_SIZE: { PhOptionsOnSize(); From 70b5cbaff05143b511a22d6320ced97e38e21b0e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 04:47:08 +1100 Subject: [PATCH 805/839] Fix typo --- phnt/include/ntrtl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index adbd3f164e50..3c480d565934 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -3212,7 +3212,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 From 4bcaaa0b76b1ede1c838a536de2230bec720a4ac Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 04:51:02 +1100 Subject: [PATCH 806/839] Update PhSearchFilePath parameters --- ProcessHacker/mainwnd.c | 6 +++--- phlib/util.c | 44 +++++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 27b37e528790..6ea93141f474 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1827,11 +1827,11 @@ BOOLEAN PhMwpOnNotify( if (fileName && !RtlDoesFileExists_U(fileName->Buffer)) { - WCHAR buffer[MAX_PATH]; + PPH_STRING filePathString; // The user typed a name without a path so attempt to locate the executable. - if (PhSearchFilePath(fileName->Buffer, L".exe", buffer)) - PhMoveReference(&fileName, PhCreateString(buffer)); + if (PhSearchFilePath(fileName->Buffer, L".exe", &filePathString)) + PhMoveReference(&fileName, filePathString); else PhClearReference(&fileName); } diff --git a/phlib/util.c b/phlib/util.c index f74f27e543d9..4c45de2b944b 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2602,11 +2602,11 @@ NTSTATUS PhCreateProcessWin32Ex( if (fileName && !RtlDoesFileExists_U(fileName->Buffer)) { - WCHAR buffer[MAX_PATH]; + PPH_STRING filePathSr; // The user typed a name without a path so attempt to locate the executable. - if (PhSearchFilePath(fileName->Buffer, L".exe", buffer)) - PhMoveReference(&fileName, PhCreateString(buffer)); + if (PhSearchFilePath(fileName->Buffer, L".exe", &filePathSr)) + PhMoveReference(&fileName, filePathSr); else PhClearReference(&fileName); } @@ -4902,7 +4902,7 @@ BOOLEAN PhParseCommandLineFuzzy( PH_STRINGREF temp; PH_STRINGREF currentPart; PH_STRINGREF remainingPart; - WCHAR buffer[MAX_PATH]; + PPH_STRING filePathSr; WCHAR originalChar; commandLine = *CommandLine; @@ -4949,9 +4949,9 @@ BOOLEAN PhParseCommandLineFuzzy( tempCommandLine = PhCreateString2(&commandLine); - if (PhSearchFilePath(tempCommandLine->Buffer, L".exe", buffer)) + if (PhSearchFilePath(tempCommandLine->Buffer, L".exe", &filePathSr)) { - *FullFileName = PhCreateString(buffer); + *FullFileName = filePathSr; } else { @@ -4995,7 +4995,7 @@ BOOLEAN PhParseCommandLineFuzzy( *(remainingPart.Buffer - 1) = 0; } - result = PhSearchFilePath(temp.Buffer, L".exe", buffer); + result = PhSearchFilePath(temp.Buffer, L".exe", &filePathSr); if (found) { @@ -5011,7 +5011,9 @@ BOOLEAN PhParseCommandLineFuzzy( *Arguments = remainingPart; if (FullFileName) - *FullFileName = PhCreateString(buffer); + *FullFileName = filePathSr; + else + PhDereferenceObject(filePathSr); PhFree(temp.Buffer); @@ -5019,6 +5021,7 @@ BOOLEAN PhParseCommandLineFuzzy( } } + PhDereferenceObject(filePathSr); PhFree(temp.Buffer); *FileName = *CommandLine; @@ -5033,57 +5036,60 @@ BOOLEAN PhParseCommandLineFuzzy( BOOLEAN PhSearchFilePath( _In_ PWSTR FileName, _In_opt_ PWSTR Extension, - _Out_writes_(MAX_PATH) PWSTR Buffer + _Out_ PPH_STRING *FilePath ) { NTSTATUS status; - ULONG result; - UNICODE_STRING fileName; + ULONG bufferLength; + UNICODE_STRING fileNameUs; OBJECT_ATTRIBUTES objectAttributes; FILE_BASIC_INFORMATION basicInfo; + WCHAR buffer[MAX_PATH + 1] = L""; - result = SearchPath( + bufferLength = SearchPath( NULL, FileName, Extension, MAX_PATH, - Buffer, + buffer, NULL ); - if (result == 0 || result >= MAX_PATH) + if (bufferLength == 0 && bufferLength <= MAX_PATH) return FALSE; // Make sure this is not a directory. if (!NT_SUCCESS(RtlDosPathNameToNtPathName_U_WithStatus( - Buffer, - &fileName, + buffer, + &fileNameUs, NULL, NULL ))) + { return FALSE; + } InitializeObjectAttributes( &objectAttributes, - &fileName, + &fileNameUs, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = NtQueryAttributesFile(&objectAttributes, &basicInfo); - RtlFreeUnicodeString(&fileName); + RtlFreeUnicodeString(&fileNameUs); if (!NT_SUCCESS(status)) return FALSE; if (basicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return FALSE; + *FilePath = PhCreateString(buffer); return TRUE; } - PPH_STRING PhGetCacheDirectory( VOID ) From 48ab1f7af46038dc9e48f5e0e795ae81786cda13 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 05:02:39 +1100 Subject: [PATCH 807/839] Remove duplicate token handle --- ProcessHacker/memlists.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index 5bccafe87648..2922350923d5 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -242,15 +242,8 @@ INT_PTR CALLBACK PhpMemoryListsDlgProc( case ID_EMPTY_COMBINEMEMORYLISTS: { NTSTATUS status; - HANDLE tokenHandle; MEMORY_COMBINE_INFORMATION_EX combineInfo = { 0 }; - if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) - { - PhSetTokenPrivilege(tokenHandle, L"SeProfileSingleProcessPrivilege", NULL, SE_PRIVILEGE_ENABLED); - NtClose(tokenHandle); - } - status = NtSetSystemInformation( SystemCombinePhysicalMemoryInformation, &combineInfo, From 87fc6e587cb428a783ffe125c3b5ba7380376bf3 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 05:08:42 +1100 Subject: [PATCH 808/839] Add permissions button to service properties window, Add ServiceStage2 delayload workaround --- ProcessHacker/ProcessHacker.rc | 1 + ProcessHacker/include/srvlist.h | 3 +- ProcessHacker/include/srvprv.h | 4 +++ ProcessHacker/srvlist.c | 50 ++++++++++--------------------- ProcessHacker/srvprp.c | 52 +++++++++++++++++---------------- ProcessHacker/srvprv.c | 9 +++--- 6 files changed, 53 insertions(+), 66 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 106581f6e3c7..12e2c50f452c 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -657,6 +657,7 @@ BEGIN 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 diff --git a/ProcessHacker/include/srvlist.h b/ProcessHacker/include/srvlist.h index 9636369dd48d..0c3cd530901a 100644 --- a/ProcessHacker/include/srvlist.h +++ b/ProcessHacker/include/srvlist.h @@ -49,10 +49,9 @@ typedef struct _PH_SERVICE_NODE PPH_STRING Description; // Key LARGE_INTEGER KeyLastWriteTime; - PPH_STRING TooltipText; - PPH_STRING KeyModifiedTimeText; + BOOLEAN ServiceQueryStage2; // begin_phapppub } PH_SERVICE_NODE, *PPH_SERVICE_NODE; // end_phapppub diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index 7a1034f557cd..a884dd2d9f20 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -106,6 +106,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/srvlist.c b/ProcessHacker/srvlist.c index 1359c062d164..3f5192bc7d0e 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -383,45 +383,19 @@ static VOID PhpUpdateServiceNodeDescription( 0 ))) { - LSTATUS result; - PWSTR buffer; - ULONG bufferSize; - ULONG returnLength = 0; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - if ((result = RegLoadMUIString( - keyHandle, - L"Description", - buffer, - bufferSize, - &returnLength, - 0, - NULL - )) == ERROR_MORE_DATA) - { - PhFree(buffer); - bufferSize = returnLength; - buffer = PhAllocate(bufferSize); - - result = RegLoadMUIString( - keyHandle, - L"Description", - buffer, - bufferSize, - &returnLength, - 0, - NULL - ); - } + PPH_STRING descriptionString; + PPH_STRING serviceDescriptionString; - if (result == ERROR_SUCCESS) + if (descriptionString = PhQueryRegistryString(keyHandle, L"Description")) { - PhMoveReference(&ServiceNode->Description, PhCreateStringEx(buffer, returnLength)); + if (serviceDescriptionString = PhLoadIndirectString(descriptionString->Buffer)) + PhMoveReference(&ServiceNode->Description, serviceDescriptionString); + else + PhSwapReference(&ServiceNode->Description, descriptionString); + + PhDereferenceObject(descriptionString); } - PhFree(buffer); NtClose(keyHandle); } @@ -797,6 +771,12 @@ BOOLEAN NTAPI PhpServiceTreeNewCallback( ServiceIconsLoaded = TRUE; } + if (!node->ServiceQueryStage2) + { + PhQueueServiceQueryStage2(node->ServiceItem); + node->ServiceQueryStage2 = TRUE; + } + if (node->ServiceItem->SmallIcon) getNodeIcon->Icon = node->ServiceItem->SmallIcon; else diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index e093b457f993..4285902595ad 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -106,9 +106,6 @@ VOID PhShowServiceProperties( PROPSHEETPAGE propSheetPage; HPROPSHEETPAGE pages[32]; SERVICE_PROPERTIES_CONTEXT context; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | @@ -136,25 +133,6 @@ VOID PhShowServiceProperties( 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; @@ -323,12 +301,11 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + 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; @@ -390,9 +367,34 @@ INT_PTR CALLBACK PhpServiceGeneralDlgProc( PhFreeFileDialog(fileDialog); } break; + case IDC_PERMISSIONS: + { + PH_STD_OBJECT_SECURITY stdObjectSecurity; + PPH_ACCESS_ENTRY accessEntries; + ULONG numberOfAccessEntries; + + stdObjectSecurity.OpenObject = PhpOpenService; + stdObjectSecurity.ObjectType = L"Service"; + stdObjectSecurity.Context = context->ServiceItem; + + if (PhGetAccessEntries(L"Service", &accessEntries, &numberOfAccessEntries)) + { + PhEditSecurity( + hwndDlg, + context->ServiceItem->DisplayName->Buffer, + PhStdGetObjectSecurity, + PhStdSetObjectSecurity, + &stdObjectSecurity, + accessEntries, + numberOfAccessEntries + ); + PhFree(accessEntries); + } + } + break; } - switch (HIWORD(wParam)) + switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case EN_CHANGE: case CBN_SELCHANGE: diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index 69912965dd0e..f6f56d58a150 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -610,7 +610,7 @@ VOID PhpQueueServiceQueryStage1( PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryStage1Worker, ServiceItem, NULL, &environment); } -VOID PhpQueueServiceQueryStage2( +VOID PhQueueServiceQueryStage2( _In_ PPH_SERVICE_ITEM ServiceItem ) { @@ -639,9 +639,10 @@ VOID PhpFillServiceItemStage1( 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. - PhpQueueServiceQueryStage2(serviceItem); + + // Note: Queue stage 2 processing after filling stage1 process data. + // HACK: We delay-load stage processing for services from the TreeNewGetNodeIcon callback in srvlist.c. + //PhQueueServiceQueryStage2(serviceItem); } VOID PhpFillServiceItemStage2( From 3b884f8a4430609c4a1dbad40c8be400f0599d03 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 10 Feb 2018 05:11:23 +1100 Subject: [PATCH 809/839] Add missing type --- phlib/include/phutil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index a135ac89f21b..8fd56b6ea7bf 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -1080,7 +1080,7 @@ NTAPI PhSearchFilePath( _In_ PWSTR FileName, _In_opt_ PWSTR Extension, - _Out_writes_(MAX_PATH) PWSTR Buffer + _Out_ PPH_STRING *FilePath ); PHLIBAPI From eeda23177a57724b62751c1702b510c4d1602bbd Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Feb 2018 02:09:29 +1100 Subject: [PATCH 810/839] Setup: Fix terminating previous instances --- tools/CustomSetupTool/CustomSetupTool/appsup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 0ffce62ff37f..2cd9a5c4037f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -480,7 +480,7 @@ static BOOLEAN NTAPI PhpPreviousInstancesCallback( PhOpenProcess( &processHandle, - ProcessQueryAccess, + ProcessQueryAccess | PROCESS_TERMINATE, objectInfo.ClientId.UniqueProcess ); From 4e282e6e710fe56da86b780983db8f47dc11f20a Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Feb 2018 04:39:41 +1100 Subject: [PATCH 811/839] Fix bug querying object information --- phlib/hndlinfo.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index dff6e983bdcf..9b1bafe98f92 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -122,7 +122,10 @@ NTSTATUS PhpGetObjectBasicInformation( { NTSTATUS status; - if (KphIsConnected()) + // 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, From 8861cc950949b1327482bfc0a5469bd8bde80d95 Mon Sep 17 00:00:00 2001 From: dmex Date: Wed, 14 Feb 2018 13:59:16 +1100 Subject: [PATCH 812/839] Fix showing appcontainer names on the security dialog --- ProcessHacker/tokprp.c | 4 +-- phlib/appresolver.c | 43 ++++++++++++++++++++++++++++-- phlib/include/appresolver.h | 6 ++++- phlib/secedit.c | 52 +++++++++---------------------------- 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 9477a0576b8e..9f8d04df62b7 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -660,7 +660,7 @@ INT_PTR CALLBACK PhpTokenPageProc( { if (appContainerInfo->TokenAppContainer) { - appContainerName = PhGetAppContainerPackageName(appContainerInfo->TokenAppContainer); + appContainerName = PhGetAppContainerName(appContainerInfo->TokenAppContainer); appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); } @@ -671,7 +671,7 @@ INT_PTR CALLBACK PhpTokenPageProc( { PPH_STRING packageFamilyName; - packageFamilyName = PhConcatStrings2(appContainerName->Buffer, L" (APP_PACKAGE)"); + packageFamilyName = PhConcatStrings2(appContainerName->Buffer, L" (APP_CONTAINER)"); SetDlgItemText(hwndDlg, IDC_USER, packageFamilyName->Buffer); PhDereferenceObject(packageFamilyName); diff --git a/phlib/appresolver.c b/phlib/appresolver.c index b9c0e546b505..961234d6e389 100644 --- a/phlib/appresolver.c +++ b/phlib/appresolver.c @@ -22,7 +22,8 @@ #define COBJMACROS #define CINTERFACE -#include +#include +#include #include #include @@ -155,7 +156,7 @@ BOOLEAN PhAppResolverGetAppIdForProcess( return FALSE; } -PPH_STRING PhGetAppContainerPackageName( +PPH_STRING PhGetAppContainerName( _In_ PSID AppContainerSid ) { @@ -174,6 +175,44 @@ PPH_STRING PhGetAppContainerPackageName( return packageFamilyName; } +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; +} + BOOLEAN PhGetAppWindowingModel( _In_ HANDLE ProcessTokenHandle, _Out_ AppPolicyWindowingModel *ProcessWindowingModelPolicy diff --git a/phlib/include/appresolver.h b/phlib/include/appresolver.h index dbea2044333a..45aeefbfa618 100644 --- a/phlib/include/appresolver.h +++ b/phlib/include/appresolver.h @@ -32,10 +32,14 @@ BOOLEAN PhAppResolverGetAppIdForProcess( _Out_ PPH_STRING *ApplicationUserModelId ); -PPH_STRING PhGetAppContainerPackageName( +PPH_STRING PhGetAppContainerName( _In_ PSID AppContainerSid ); +PPH_STRING PhGetAppContainerPackageName( + _In_ PSID Sid + ); + PPH_LIST PhGetPackageAssetsFromResourceFile( _In_ PWSTR FilePath ); diff --git a/phlib/secedit.c b/phlib/secedit.c index 83e5611e9502..be964c0fe2fa 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -3,7 +3,7 @@ * object security editor * * Copyright (C) 2010-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -560,46 +561,17 @@ HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetData( sidInfo.pwzCommonName = PhGetString(sidString); PhAddItemList(this->NameCache, sidString); } - else if (sidString = PhSidToStringSid(sidInfo.pSid)) + else if (sidString = PhGetAppContainerPackageName(sidInfo.pSid)) { - static PH_STRINGREF appcontainerMappings = PH_STRINGREF_INIT(L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Mappings\\"); - HANDLE keyHandle; - PPH_STRING keyPath; - PPH_STRING packageName = NULL; - - if (PhEqualString2(sidString, L"S-1-15-3-4096", FALSE)) - { - // Special case for Edge and Internet Explorer objects. - packageName = PhCreateString(L"InternetExplorer (APP_PACKAGE)"); - sidInfo.pwzCommonName = PhGetString(packageName);; - PhAddItemList(this->NameCache, packageName); - sidInfoList->aSidInfo[i] = sidInfo; - continue; - } - - keyPath = PhConcatStringRef2(&appcontainerMappings, &sidString->sr); - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_CURRENT_USER, - &keyPath->sr, - 0 - ))) - { - packageName = PhQueryRegistryString(keyHandle, L"Moniker"); - NtClose(keyHandle); - } - - if (packageName) - { - PhMoveReference(&packageName, PhFormatString(L"%s (APP_PACKAGE)", PhGetString(packageName))); - sidInfo.pwzCommonName = PhGetString(packageName); - PhAddItemList(this->NameCache, packageName); - } - - PhDereferenceObject(keyPath); - PhDereferenceObject(sidString); + 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); } sidInfoList->aSidInfo[i] = sidInfo; From 824e249c5f5cf9b1017416e2cf2ab07318f196c0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 01:39:20 +1100 Subject: [PATCH 813/839] BuildTools: Fix crash when git installed into custom paths --- tools/CustomBuildTool/Source Files/Build.cs | 25 ++++++++++++------ tools/CustomBuildTool/Source Files/Utils.cs | 15 +++++++++++ .../bin/Release/CustomBuildTool.exe | Bin 163840 -> 163840 bytes .../bin/Release/CustomBuildTool.pdb | Bin 75264 -> 77312 bytes 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index c30c120f55fd..9a2ebc5cccc1 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -160,8 +160,8 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) BuildOutputFolder = "build\\output"; MSBuildExePath = VisualStudio.GetMsbuildFilePath(); CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; - GitExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles%\\Git\\cmd\\git.exe"); - CertUtilExePath = Environment.ExpandEnvironmentVariables("%SystemRoot%\\system32\\Certutil.exe"); + GitExePath = VisualStudio.GetFilePathFromPath("git.exe"); + CertUtilExePath = VisualStudio.GetFilePathFromPath("certutil.exe"); MakeAppxExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\MakeAppx.exe"); BuildNightly = !string.Equals(Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%"), "%APPVEYOR_BUILD_API%", StringComparison.OrdinalIgnoreCase); @@ -207,7 +207,7 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) { if (CheckDependencies) { - Program.PrintColorMessage("[Warning] Git not installed...", ConsoleColor.Yellow); + Program.PrintColorMessage("[Warning] Git not installed.", ConsoleColor.Yellow); } } @@ -272,13 +272,22 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (ShowBuildInfo && !GitExportBuild) { - Program.PrintColorMessage("Branch: ", ConsoleColor.DarkGray, false); - Program.PrintColorMessage(BuildBranch, ConsoleColor.Green, true); + if (!string.IsNullOrEmpty(BuildBranch)) + { + Program.PrintColorMessage("Branch: ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildBranch, ConsoleColor.Green, true); + } + Program.PrintColorMessage("Version: ", ConsoleColor.DarkGray, false); Program.PrintColorMessage(BuildVersion, ConsoleColor.Green, false); - Program.PrintColorMessage(" (", ConsoleColor.DarkGray, false); - Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.DarkYellow, false); - Program.PrintColorMessage(")", ConsoleColor.DarkGray, false); + + if (!string.IsNullOrEmpty(BuildCommit)) + { + Program.PrintColorMessage(" (", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.DarkYellow, false); + Program.PrintColorMessage(")", ConsoleColor.DarkGray, false); + } + Program.PrintColorMessage(Environment.NewLine, ConsoleColor.DarkGray, true); if (!BuildNightly && ShowLogInfo && File.Exists(GitExePath)) diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 39cfc47bb98f..ba1b493d5026 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -205,6 +205,21 @@ public static string HashFile(string FileName) public static class VisualStudio { + public static string GetFilePathFromPath(string FileName) + { + string where = Environment.ExpandEnvironmentVariables("%windir%\\System32\\where.exe"); + + if (File.Exists(where)) + { + string whereResult = Win32.ShellExecute(where, FileName); + + if (!string.IsNullOrEmpty(whereResult)) + return whereResult; + } + + return null; + } + public static string GetMsbuildFilePath() { string vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe"); diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index fd6f7537e90a034be99c58d475746b45ed3e9764..e4971dd5c781e4e11dc3445a30d56af19cc44f24 100644 GIT binary patch delta 16585 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 PwOZHiS?}fQkIMfA92oC+ delta 16536 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index e9427627252d9655f9e3aaba42f6942b4d5b35f8..3ba53e1eed6b41a9a0f78f957953f165bc2b8988 100644 GIT binary patch delta 14990 zcmZ{r2Y6J)`nb<5*(HGlSpuX{5<& z>xTF&%i87O!0(mf7}r6y%~8prP8Tc3CAG~J5D;GR*Xg|!p-srCi({+!-fEYujQ2VM0;E>mIvVZkPF96-pl&?J zYjwjM9|g#fv}a^ULb&=tUQCGRxG5pbG0|ze>(&cbKFO&UAK`Oamabw|tBkvc%d%D= z5!h3ts9wHWF8Assb4*GMQ|qNwqDLK*f<%uaq^!J`nC=J&l*@?|1LFeyX^1)(DpMbJ zN0c~pqSFXGgy<^A$)<Qo~haIhGn9(Kz_; zJkC|Htojf6+h+yw^3`scn-(9j`++#C3BPFKoVC-c-yqykCPdmds1sQx&_Jt|xZ8s@8patnV$iAfK0JoOOKd)ckABt1MZ zpX_uOO5TOaax}f7%9k_gVQQA#N>5YgCCQWTaJglJXOucE)iZ{x8!|T|nd9z^gKD45 z%1l#-WOrsWl_B>s^8-6qHmP;`HA>!SY9osq$8+4DxzfAU)2g3**y>Eg7~1K-h~lrh znfldfxvzCR_!h;T9MRcf&Y%9LB?+ zVO@9~)`NlMoCvGIG#Fwvw%k@f@wG{g$*`(eJ(2Esv~)$ZuM08+=`26BiR%y$;IG3Z z%C3K9k-l2t=OIPLm6ZwOVI$ZKX2Hi`W7rQif%&khbZVTXtG+t8n87? zf^A@9*cLX0?VwKcQP>4O2A_bB!=5l#PPI*=qulMHyxsAI;_CtHKvvYL4PjsGw(uEj zeeOVW?sISm_7MEKe50`ELHcg$QaBNNIn0MEU;%s=PKG;R5&QtsONDN$k!)<&inZ<< zC3y*tF3B8N6V8S8;5?WP>CeI(xB%wDh45)8a2R|A7Qk1b7cPRc;bQn2d>y_C-+-Io zQcAoFu7n5a2x|?>_c+$W^Y9%a{t7q4i*PHn7$ncfFDwVO&AMdmgH_?@P!DMx9%I7wFgS$2zVW9TI|ClZ-_7`Y8~-!#2!7oSIvrhk zZI87quk{7WLKCpa1gwC^@Nb4+!;j!M@G$%qo`l~)UD*@xHv9nwQMr>a9R3KIE!Iyk z9iE1aL+cFe3jYJ2Rf(4Me<=NNFs%w1G}c)-3H}$-`PKzE75*w?A6up3B%(u9=WF=5 zvRS{uV0axy!5dHyftyfYq5gzf@D|hq{tnbb;V$e7|AO72<&fze(pl4XcZh1|a#)4s z@iDLM@l_7i#tw$bumVhh4?*4ZZrB=Dg6&}y_!z7zF&)!_4$`}Neae($pM~Z`5~jPi z24rQnYQjhu4XeQzsJlBB>O5*gJ-gze2iBFX9h1HOE2Ywd!~W5VRz+4H{d<_0x=E6t z&gl`Tb4Z3dhZGnG)1c0wAxn8{VJRYS3@uUw_qV$2d6;2)=z~W!|Cu- zI0J@QsjS@9x&G9*+|nm^t@j|aRrl*Ps-S=2$iu|ezuBCj+k6ge0O!Jv@MWkc=X|Kw z$c0d^NUy-5@Krb*`rvrD7`_N!hcn<3_%dAPu(}uO#dA51xZ`?XcvN&P~{^sV-4@-{IA{ zeGm2E{Q>G+bbV{WpRo1xI1Ssu|B#zr?apHBT>cCD!}D+gyZ{T}C8(GD%Tmy#8QZ?y zU7|97!>9AQ3H1v52TXu}LcONkhE3re*c{%6E#Y6{?wY8#giX6fddrmYCsG#b5fBJ# z!ys56mV=F8Fw_MOfqJ%8fW6>Du(x3!s4Myu48{K(tPID%DzFe%g)hNy$Yk_d5h%K^ zBH;!Y1veV%i=x=_!`dXO!L^)P%4 zCc)l%Ie!GD4~}H`6ik7mU@9C38^RZ0x)gO=Rmjk4iJb#mK|Q3unW`+RyX(t>|VyIQMd%92M#@J zdct?%lkh#*NA`AqoK3p>iKxsxe7Z3Gp&lIrpdK9qp)Sl|s7J?hP>+tGP>+sb(*KD> zw(tv{i1LoYr_&e*^`ICJGhjaK2q!_k_V5-V^9d=1WmKIzkApw~sG=sxcqvcTd(F&-WWfe?^YoKn9x1hdOu7^*;jqo}6Hq=#r2kNSC zf%DYwC6!JD?sQyQOE(M;)i#^7_z7_s7HzVGR970Q(i|h8*h|<^3FQ zFrNK(sAV015!nBRx~vCb9e4=pE;1Q^ourFXXJPLE*m#`E33g*GD%UDbE$nAIR z!SFg{w`<*m(-d>@50vFNZo#$iHq;lIJCMDhbr)`f_u;>xBY@Q!aywASHp_BBc0X1i z3~;b`!16E{M!*U%4OWC)#_b7_ZJ6VTWi}DOW!kC)Tfxe(7pwxgys|?H>AhUv5%!Mq zMg~|j9;a5d;Ab$AIC{VLFw}!C8R|=io^{8Ktw%%({{Mpw;RTouFG3Hz3^QRFD$xj* zhx+`7piWO;=CdLDMXwzYiK97=IM^Dd!ZuJ}|Jy>nk!%mSQL!F{`m(PR=xNv&cEZ-% z0G-hwW9vcQ1;1W+y1{v{CtL%2!L=&OvU;Pe$I%CV1fPPRK%LM5s1s&aZ1saz;Q;s> z94M;KM(&e0^@;L6k1vQ?4}}lGVK4~}hh$-mfO_2@1@-zq8V-hI;50ZE>I?dKdf_fC z#MV=L3Z#{+sZcl43>XV%!36jcY@@7h)@+n+IOf1-;XF7Vz6__r`EVIr0Cz$mqpA#r zCD?3Atz|G3E{6@^Dkw*vN|!I6YRdlD{dAOfeSr0nkFsqiXbpTH>gDr8co6P_XW?$B z%l;4|UNGKs~Si4fTz{LFj{rAk)Qb9Y*0k$NBUyZFh=Bof}S`+GUlJG*P!`al?@fW}!@v{T6&Jd3sf^{Coma(jB__?lG*I^pG z0b9eHuxA%aWluAU5aB`K0U0`hjo`!L72&@BV!g#nE)`kCq_26-s z2+zYuI6vOSq=k8~DgJ>_7i_SxN5E|S6JQQ(sPDY&rZf@e!4?F}hb`ek*cz^dZQy#? zwzyATyz<7gY15aI7ApVqx$!l(|WN9q&|g@!fsICn{|hIuooN%?Xnqr1bh;| zzTEeLh0rb<)bZ!RzD{fAe)yb|8wtm z{8gwI3Lf0r`)OTz-74Cy4HrWXd;_+DOQ7B>=y2US%V7_=0``P&!l7^#90lKkdc(LD zx~&0huu`~%X)bT{%jBur@qVFY77mJbe#XV-g2WAq3rl^vKC$(aUWS=|$YXEGE)?et z%2MirEFB!BqGadbfzEB5(pKW0tEt*btLL6~7Gpmy+n-C0d7M2B=h=yT4WlulVRz$? zl&#Z4CH(ouYMXR>KGw;W%QZ;~o^OG@7n{qxYl@t=?W7^GPPRU-S<-Dt8@JBWVY%$< zye?*2$r~Ca`-fBu^5?0nB#D_3x^QTNph5W9eb#MZW}#tzL!p;qTH!?Ygu(ly=QE18r~XKty1qf+sj4vAKojrmle7& z#)>PAd(-xEA?7JULaFzj_EuuE`42CRGkH{GrHub^(@@%-iTlA4(`ee==zn6*ma24zXH|joQXJS7E=?Zrez=h4e-+BV> z!)9g`eh!~5K0M|Lr8Y_2xLA(u#(B6MEgI)lS454EkGPWh_W;q?{}o6hPP-yGuIPI75qt%t-o-cf^f3gD#T5%GE0rj5ljAvdn7mf?mm8B~IYxN1)DY?B^{CM@m*Yg)=Y3gi zk#6j7w#bCSEcL!@=Qidexx{gw#1*})?#T9{SdJ%(vK)>EQguorM}-E`dCGLsJ~5>? zsa~5JCAm{qkixa8AF4aWo2HFc!MX;^{%)FZX*s>JV_AlDTv1CVO>e9cWasp!)w1G< z8ReBaC2=!jIkua**%4AtZqKX{5yHr!{q2P<5QVX=diI<@M;7|W8c_NhO;wsV@E6Bw(|=6s!9)7z=8YNDderctnmS_1BF_41 zpW9qIy%yDM{Gk4$`kPQ)c)fGeGo+hIO|LAvl5}zDiesPCx&P>~L&nJR@l9eHnUZC* zY0^JE14@2lDA{0%d#z@5eHXwpQ#*99Nj;L(b<8-)do8R=T{2pP=$Sal#Hb}_e4(=9 zweScX`Zc2O$%-E+okNx`ijs@31%~Jt`t3^uG0sUI-fF(Kk>LTD{EqlxADP20XG59n zt5dxtC;ZIXse_#j{Y_ZM6swNx^~KAek-<`JQJ~!QRaSMxy{IO~ltr=CXL9Cg&YaJg z$>z+eoT>AxBO@1uR?i?*Z}_-wSRNBYHz->zVyv`N+Pf%<^iD169nu%CzY`S;dEN`5 z9lm6jQ6BHtFcf|Rqu_Ti7M_4{@FWbe`tfA0Hoepy(Jz(iVn^63;a%bkmL`j%yl051 zQ~Vj~6wk6>VH0Lu!qx-oGSts8u0nl-at*eDze9baqW2^1;VtZ3cn5ZY_uzBzKIA&6 zo3W59pQT_CECXNTxs?@wvIvI@z77MGTv*)Q>u-5g+DUo{Qbzk1OjKQFZW`>xy8_f* zToLNpSAumRHxu?^9tN38c3D|3?U#H_AU8_(Gsq~YAE#DV*0l97R#|ll-$v0VZZrG< z#$&S;vbdwNA6X~LFR%BG2*(p$fvJu>iRcF&i;z9i>5a_`a@Q?f67AYc1>-WM&XPLr zxJ>_;s%6GhJ?XO~R(dQAmN`o@SZDVwiFJNV^Z{~dNxXZ2iCr==SR$9ky5||M9$k!a zD^YqbZNa%qm&Q6j;oPre-_jP9cA%B4$c>3b0V0<*R|BNavVlQg*-0oX%-#TgoTM^H(!-^SfIF7IIsut31WwN71vdmlEUXHE|<}LB5 zm06DA$+B!!9m#$(SiK+}-ki!?+GB6(7xHsgKN`Z5QcK_F=yB6T@>XT3RuZ!&K7{8_ z)>Why{*H2N)l@Zuv%;mq%?gsg+Kn}L^%H6%XC#Mw&17N&ZoNR&j$U5 zy$9JZd)IufS`^QEtBlgwuKiWbD4wwHAys1x)tQg1LyjWnkjfQ#fPwU=xFDz3!VS^R zsWjOYiQC|DPD8sXd1x=9-It{s(p=NgoF4gRL#k^AYKTWFY)p5}M6K+R_NcQ^t9fMl z##Gl!sIeaT2z54UqDO9SOs_BpHI@Ck?lfz(+}{`}UEi+kV$0xaJMFlOx5Ni8$J%S^B{6qH<)Q+dId}sBp(Oze?0{(3 zMx0R@a-_IvFcaTZt0X++uP{k|H^#LIXT1#R{%&*EJE$ocvJ7=IYG#IrD%JU_Eu=c1Ss*|=k{P{jgY2R^HP^n7Q7!-X#h)1 z3#(13m$9aIOxYj&Gu6t{>4Td8NRi=3ME79MZ)UY9JwMh?ReM`nZA-n3N}cM}4+13m z!(f#v>cg5L?X8Zb5gJJno*b)HsVB!y=y?)q&4s0;9_{?O^6>Z{x#sG~ZtZ2b`b(F)DnADPe5adB37Fz;9V6&g(n59a-bov!vinExt&vi5ehdX#$i`Xi6S{iyYLse8X2S$lK&WTYgCqyEU_aX(n* zZ|%t1`(T-W@<*P4`@vy)){d;b4>spzf8>d{AMD5*c4X~+Fu!~L$UK_+N8<;Gdv}yu zdmpTJu#B7(f@{PaWHqt}`3|{`1hZMKi)15DB4d#G$U5Xx zqEPPWwxAqE&LM%7*>50? zk*>%HWG1p2*@b+ETtzBYp>PuPQLHzkDtDmB2xKO*4%vg8M*c*?!^jqS4C#jyB8!lB zkweHiM8CkUi)15%kV0e~vIjYb=uLKQq%qPLc>!60?1*5KcN*nSBs!802#~&!TXWj0 zg?nnVY`fO$px{j25@|akWCm?#<`3wQGA5IadETOkI?Q zALpwYvi0Lcj;B5z&gX0$K1oyAQuIj{_kz1W*~rb(;7?;Y&iyo>Z|j2gC9AEHxv!b} zSSIXCQ%SN3&onu;?|BN*?lUiW9r$c^&>?n^`lhS1#B>Oi5ueXgHHvS2K3u7m#e)z0 zuHs&x_;Zle$fw9jBSum#VX!ck32k8y)8EmCvuEA z62>v}NU|!H!5lx7#Yd((Dy2$|FX}{8O7&m0^!-@_wnY~ay~S=MeZKfrt(1009}P-R z@t1O(UCJ#-XX@mReWjDA@a2z0y7Z-|hQ2jeg1n1-ja)%25mD=`sV;^Iaro)e$^WuX1c8K8t!U!*KC7EK+DktB6UB|oF zoZrcmXRs+plXCv^*PHxXnQ~Pzwalbxbbdu8;ii18On!Tf{}z*4$LnL#TS*^jyB%{5 ziuZU>On*~`BvXccj1nD(FK=v4koCtse6V!pc$hcE#64l^Ka2UR=XJ((KlfcYfQFtO}0Bt=vdkM zP0eOCoUQ^f;C25<&Rxz3LDTrO@orYu0n9ef| zpJVLxCVn^5fD`;3BAyd&?@rS>d|PFwb;fjFj48_vQ-+?#E;401W*RicbgF)tq&w#@ zR~pSTCeQO`zEv^jEjHoT{PRS%La66jzGkY@^TLRNks zmax)f-`^y^(qy~U*hS{7m2&3$FzHx*7j9Gb3*K<3D4)STcG{hU-nG+nItC7#=fpn?^WbdMU^BtTOCl3V7ZW zAV!Y;5Y~RNiM`46$ppg$b5@Fpect5K-`GxPV0O836)j3{1?W+rB=zJ~X8>(%`SL2L z&~iK&bkx!zfzt9+a}_8vPBr6Yu+pEeOmp-gp1+qZD;ljZT0fw&I30yAF?2d5zvyhg zCS`uibAE^Zvkd;x>m0%GnIxBfobAkD;+ zls{Vfoc>AuEKSZx%nn-hQ{*Ia4!Mq4v|un2j?|V;1)I0#=BjP~`J`Nw{Zg)~mB3ig z)#XL7RX@Exzh~3s$BT*VZ^~WDk_nf}OYWrv)j%d*O5?MocP@F@qnx#mH7DuauXEuOz8wrPmdY9Kc$F)nERNm3g(i1YdpFkx^e7UG=CG z8Ff`>x9sX#DY!;<*Ys(VuX*?&|2>ZVfjHQzcIW9+jF<{irt3l1B!GGaO3l?|1s^?`bHnUr(c0M>wX6<3^fF zD^9Yur+~v_BR*HOZ*F!CTyPGCG@0FMO_jF(Px~Kbk9>;X~_+B&e za@^Pd{L!3arcAltOzzz;FNf}@sAuHHVHQ-6CS z^-`JF0#r5C*jGJ3J+4~5KQursca*E92OhsM4&UUm>XG+bmR0@AsOmmg}zheRFv}vJiBaDFtyy5Ql3ElA4NUEhDY8|y+vg4@S^W<=6aA;?4-0qr<27XEYVD7g z?<>;r^cQX4`vXs-R`IJhQ2W@`QJsC)h^N~4LPAuOtANqB!R4D*PE~d4FBp#d>vGqZ zN2qgj44=68TOqYJpB4G8(IBqbalCZ2e9J;qRdvXBjEJg+?~+cfWxW2d*A8)>O5ny! zuUBC%{c`D}GQPSkM2>AskUi1W6u32^Egj+cEo4-sc2YOt-UO}+&>&K2J#9Vao38xUViMf#|}PgNvf ziywD<)v=uVJw?3i9Tw`#tw`tS_c=@cHucl|Bc-Nqh+FmWjj5=@NqwYF>!u&~Y&Zha zee)`+NJn|YN`?`J(SADh+wuLr1-eXEe5c%YIw_UFeZS@OxkFWyBgEtT(XAp?WxrX? zn6Z8{(U_@zvym}#{AN32=K9SZ#(dgu4m9R4zd6pB1%C5IW6trLuNd_w5<6QI2)p1;z z`s4+i>9dVhy$Z!?u)UQB>`l^SM*H^wy;Usp!H#A%&sypv54p)zs zSy=A>|F4_xSF5J3mr;p6ceF}YwS76!DmLY9F1Y$@igm0YJJ=yiW6fyE1vr2;A)8A| zE}K$7*|5!wW@fw1%$(+w2@yiI9C8*aA@0qbrd0Exo2M`$ zl*P=k2N5F`50y$!Do=-B2ZX3p^nYL9@5Q6%_4?22bG@(6=Q^LS@ArEv_$;8{y@2PN zhKOZZ+g&*PEvbm}ysNgkYP-~L70Pu%ZSw>K)PH}$wEGpIcORbJZR748xhq}X<-c4g zty&RXt+}Vppie^r5)zM2Tl<_<`fc&{A9BujT3g<6o!VBryQj|XY~{(_dc6HjcX4Lj zMNgMcxPGO~4vY+R1?|nW3$^xEghv1B4lH=<#G9T~A1MFi8Y--$PXp=DC^q2RI-9-2F)C@`}BUdIc!A1SMwS<|YnN>ts!$&vm|vG-{fl(X;7nHCm1}O~k$2G~D${ zfSgEsSjH#SSBGR?QX+0yQn>43w{33PtiJL|$7YEUKDTA*Cf2j6gbwmpRw)vJK2{2w z%~T8IMza*$7RlkNL~@h8YLCoH_PVZlWM^`Q>wHzYm^>peB+$u2)cG(eY91PK%b+uz zOW=EmZgPl}HBVFB1YfMVvR~>S7+NZdnJAY>H_hD^t8`l$^%hEg>zvN=Yv6 z5}DOf7jV60B5qvjR5eeQrOu=;Lt7=|X15AgRV6fSxa@9KUp0|atr8=e1pl{xzN=wb zEo(acvygZ*)p}W+mKd@AjyUTGKX2lku=8r!y1whjVCmU9A@avNB0f~hvNj^Rv%A-@ zN9BC$`gDamJrTD>dboOCa?`!al3D39RRg(>y@AAKguA-dB>Me!Z@iRIKX5q3=^>Q- zi*@8=MlCg5&SiwF$#Oj-O`Vb!-kGk8wd7Ur6!o?=XfsKjmBnpRaQC%&SCvU|W}4bA z`!aJ>tlY?)8Q8Xt$*u2CQSxHjP}Nq-+74GWB{VBdB}i_TSKTACvQpGUDZ{^3zQKM@ zBHH;>imYgthkxuA~jRW zb8;A`%Q;!zKt%c6XYozLvsHDY)gG z!f}svN@I|3bn>cplHPfy8d6c-dA)LvvO=thG9>pwX2U;o&qWNSpZ*U~{BxV7A4S*o zksV#9j_gQC9c2~V1M5N^yB-`1!{IboA3hBu;BpuV*TZPI55~avU_U8s9+`R!09m5YAc6&88}=eTh^y|Mq>Ckd<33_Pr=V2 zLu{Rc46*eeS={So6(Xs5QT@Kd#t~(G4};;4P_N+gP)~siP#-lv!7RwyvnTu&sHeiu zus{3--UokGvMMiQKW4${tSD;stjx%)HA0q zWW%-Yg-zf9$gXDH2id`_L9h=T4EsSnmG6ZQp^sAcTGqoZtB7sG8U@)5tg!^J30M=* zm%+(!9ee^iYt)(sS)h16axEkulmBJU%x7wQxo&y+G z!jo_nd>^icAHX&6|KM799MeC!}>+#zlu#yjm=Ommoj*3bJ6ERIl6A# zHmLVrq28^(k!APhxYh?tvw@LqzsINx zy8!j>co8PSpP=3kF2nZl3hV^0!7lI@m&_iRAbt7=1Ua`dWzCSjcZd0YCxXuKCe*|J z7u2iWs^VamJN7xSP#nZluwLp(?H}pc-11^I-q26kG!53j3dMv*RyNCO6(!i+9K^S#qhC)3jhCw|i9)P+s4?#UA@}Zs+BcPrWBW2dWWNy!!21fbD zVAFX_fO_~R!ZvUU>LaTZ4uxysSokv3ty~XvD>uOB;YO&h4zE?Qigv&)@DsQl zdx$kurVY;JVPoImsL(yOWw&*O&=0BRS^0i&ly5)QU@ULip**?42=s$c*Y_=$01rVu zf`?%mtb}@-Is!Yuqp%}92K&NyU_N}einWsKr}YE+82BU9xA+TiuCm5j7xApda0zaJ zm!UoouE28mGu#HR!FS+wcoO~rxzAWP;1BRG7~tYg5WpP)R)G=F4bxy%xp04YTp$`} zqZI@>8?EZ_ei#gg!VrlcvfAf7c`54}`RYGCU|ehLzt8|{!9DmQ;1Sq}l=@EA80u-3 z0QFg**V$>Ke+rvo{|YvTKfo4HpAmP#i?AiELK&$6mam{X9-To=s58_@vKQ)0owoC= zV`ZYp!)(|Jwukzt&Vl-}*Ad-0Pr*n9|fM;`@sexr@9r*+SOfzGbdiwMtR z=mXcozEF=)Ke!3@hg^-V0q_vi86AT<<5O@5yab2AEARn{81|YkrJnQ9mqBmqKYa%F zAyof8Ow1rS0@j2hVGB44X2D0G-s#6cy`ztXW8gSA7mkPe;GW3X{0XO_>m@!N>aLp+ zU`@39ZZ?L-7z$t#EQHPqi~A#e{2gKxkl@SiXp z?u9+!n^4#J0Msk(Ak=sFx1b*$f-E4Pbr=s%5Y`d+1w0DBg?ik$yxY1Sr*{IZxm+l% z4+t-SI-Dz(9nO`>I*t7`_zCei^sO%;N0;>-c1|SgN66`8orhiF1vt10_l1jiCSkY) z*@dmka3Q<`*TJ75`;T=Meg?0>-{7yXT7YH!23dY~h3Xl-0sMmsFa@jtH;+pue1{3A zS1b?qfBWzRxvjYoZVq&KFN`AKK^P6k!v=66jDhQ5L-;0)gQsC5_&tm#eWHgo4D(?+ z_D7)ZsWC=>6ne2whizcGeps@*(nNR`wk6Y`6iohnrwd#pC&jiZ?GSACB^M zc3bnZnIJt0yc_m{_rW}v5BtJLpj`{2KMHB7eS7N<3rzTY6aFk5=(ZNV4f6^A91e&2 zkQf1fhmX+8F3Nh?nt(yyPbNXVgFFiLQ{z;(HD@lIhQ0z$hh=aEWae2Gs+zENp zS@13_fO;!0g73q*vSPT8S4a^fqI`?61ycAkP;Uj#LcLY!#x*c{6ZkxKFI)nK zpyTV#c>(H6p&t&0E1-TR_y?QW! zv^<^Vds}ZYzOkDl-_uhDjPDkz3v*c>`*_#&3zGZ^QS$cq=pd&s#Vs*zLD-TBt%I`d z(v{_rH50<5&xBg4Gm*Wno^53R;7Hj%VZ8fsa$73#6SE|CVt{&4Mo#p~L-^Lpnu%%d zCkfpoXYg@j^lX*TNnZC1e7huXQkHurzWuUzQkLf_e3j{PVp3brEWGcfON~b}JhSnB zkS;wRZR=rE@O+vsi?9~pJ(n&=uomL|E?qp6W8%I`|8GatVJqNTW9e(t-?ldWA8Vy;<@otq z-1oG#YBQ|VSALotUp3i@AyHG}t2Q_GZd2lGx4@rhCEKOw2}GAwdo0qIf|;$ueo0vy zwnC4AX;60^cLe*!$r)nbxw$J8=nD;ZUi-$`2I@mK6EY2~wvZ9A^oz{JP`|KT3Ohjk zAkY!M3_HQs^ysix*zX{6;T{6I!Z%=d$SSgWz@xBF#qq}mDz!}FpJCLEA6E>1@~8@?5YINVvLy6c zv~-`*TsKl@z!xwGj z;>@nKcj3LYw)E`T<}8$lo~q>{)7euZZt}OZArlKCc=gi*X;8O5by8xeVZPvRtMYWU1A%gNKn<y5$Lz}fWJAxH!r(~RL>Qqlmoi=*pl#%v!+W}o)yVyXiY?G#ul)Mlo3;p#Q z=umwt*N-@lna~(=6-&ALL1~kTRgYM9eWavWXNi43FsYMiLreB7 z{ddBF?LI~uj+m5|+%>v>G-iFI3?FYWlkSpy?~jVtYgp&l2kTXY4chwcjwW%Bv2=dANdZ#2B9PoRt%$;{TrtG` zC!x(`@js$`?{iRBM>`F};Ky8x8$zxM_7mY3kjY`4gZh&GAE-~_uVD|UPwbwMhaUSK zC*5m*;J66KV*d$pI#`#X4_<+V@G5-4vcjxuc$Q=M75)QWx1{Zh_xYUOQMbEQ?{$=E z|J@*}uJcW(?SDZ%ScwnDFlo(-$R?l2fmgEf>jcN46m ztb~Hicyz*ThHO9fN2w!Fe~fw$MoaojLu%K;7h8h`gA7IH)+p)t`jS<#o&!vSZ`(-1 zs)VZFI*U`b%&#k>SJhWZQn;!!dsF4A#_oeeOOy+%5|8eR7#D!Lmv ziJV6QxQ4}6gsurwYOBPrO~lPxyMVjt>9szUECXInaV4k7v#<1&lP?GJW%BIHS+0R8 zvTA*TbXXUx9+JFuMSK}NwN8HoUi`}4A$q#%>%E>N@shtjOL0TlkQnj|Hr@ts0<%ix z)cPVdiB$Dv!Yd8&Eq-O75@M%>e8_Tpl3DyD@m?fl8gE!ntsfqA1^ocBRSs@As*)>; zH&#))$W1@0Nfpyyt*K(#(3Jkjcw{NE8TkacghbaW>3IK=&9Uw|bQ}%c>~$C6+a>w< z=Hh!(R&Gx7%)@siLq6Kv%EMjSb0R}(lx27p;60roJ@GEYdp1Mnm$mW~}t!_8^yaml9Fz3yl6Rg-UCPjf$q zFH{PSg>qMIe>86o@1bKEcD8;3o|&35dWD!JRT z+$-_T&~caD#TWb1ZL#iESmsD2VXN^K+xbLPB)Ut{_~k-HmU|7p<+gnV_O)oGQd!}3 zzl?8#sO?_&I(%Cs8{c|-J7xNIZ}2Pl_IsV_?+OQH|@9siQC@lblZBwj;pPmt!}riC+xU9Ui>ZJ({@~Ky))mlcHH~1=2)F? z=fH<~>Y%M%tnRn1=k2&d{wIf@?YP?7lTUBA>idTsmotfXd%3r*E=k=TRa;xTT0L)D zt9kWhCzSWqf2%~O9a&rNtVDzp`9aKgwmH_0tgYRxUbk~@>O_7Ba}O);wmHR)tgXHI zgnz5bUMDhdLjKl6?d{0gdT0B(I*}j7d}lTC?8w@BXEg>mkw;*@vl>I~$l7{mHAdKx z<$_q??y>H@ZJJ@{%KJIKkq)|To#W&hyE`D{ zZhoq8tIfqkZdjuaKVZ0JDRJsL`hV)W+|E^7@2qR7lj})xy>kk0kaN2mhTJ*Lwn)gH zh9P&3<4!xnasN}yey8*anD6ZEN*y^=TkmY?aoIjjm$+}LUto3k@H9ZmrQ-(Y=iVi#v==nQe-dkA#w=`tj)fFv_l3Vk0Q?? z>yQJ;C&*PKm@8IOqysWEj7{P(JWG+y$a~1wh*gK12GSbog^Wh@$Lp7n?Z}77c_g^5 zzO||Yjfb_aM@(cqG8b8g>_t99t|GOW!QpWAp`;(4sXS7CI2rfs;Ys{FDz7q)Czis>EFO*a zRldfv!oy(-g>rm>}$VHq!HkB*}fJ>K?F*0Hyr4%*ID zP(Oq86SXW%9zC{Dc`B|So1|3piZSp0tm4~6GWN)5WFbqau{)Q_rIdc1#kP>dZ%Sx?wC-unJRo!ti?=qze{_&Jd- zeCTb^g=#&7Jc%qvwj%E#Un74~U0+F`4of}?>{}}kJNGC3P_J)|gPDGsx!5%4F}*eQ zJ73K+CVagi-%xD(L1vi_KQ8$nh5K&MFC&4#lQlTA2D~%bt(D zYM5O9INXmlE>QB`4t)-G-)rGzBy&;(RY4p0L{)%;{(+@D=Zd1=nQ~z5( zlMHd%RZagO%t;_6pM*!OG{yEc#p*8|I=f6c_({Vaai*{prm!_8))>QFQ^}4@FP$p> zRwtQUu9*RhlcX~ZeG}MxbgVI^%=P%Se#10jpW$&++GLaKFjEVj=#C7e2%XH6ZxH}m(5Gu2$yFgf<` zhH9Vm{Vd$a*9kjvh-qM)>BLZzNxZ3SkQt>lrV^)3gZTlNo!)IKUu^6nt2*{I#=gds zbJ`3}k~80Z*5X^M(~L;2DR`tQc%`xTH9cNP({$@jn~JP31)VVkl^cD!$!ERE=eQY( zLKAO-T~TVY#?<(fNf;U|k!NGZ95cPy(d;rIhVx9Xeq;19Q_eT0oCwpv%Vs(3Gx7O7 zgkA4Fru-Eq{|QoZHayO4#;DTdHq6Ys3r3HZb7#YQ)-`!fHiMCE*utc~c5B2jpzI@uvhjD&k zvG`n(=PGl8&nfl(6XPCFHcv|Cf0Ba&se3HaTqgYI3w25IzLJQ;^w~+|GvqtuDq=Bj zf~D`Q@NM57supE`U8-j#F_yZC{2a+@nbDHJ;py`ErDT;V)h=hLR?_uyno5%yxUJ>& z%U+c(-`Z}>6|c&W`>v!Yugu47BRj98sZ9BSXV~wrRF~#Ix8ObV{XctU@XyudCA1Op z4%#ua;H%AD-ci!_s#mp?DOYu2tFCTf!|8fWznq?Z%`3C6RhI+TbfSycMph*J!pjwJ zMYCV4Dw*(Gl=S+oR*eKJ-mblVmlmRjc8sPGh{S<)h!yD5cu=8(t~9QC%+GXhCr?H+6CS zaMNWzZic*p>y?W)y%d%3mzPd_;IAwh_E&XT_gAW{fM5QVs)jGCt+LdJWxbVmS)QuC z?0MDPl{sqJenlrP`vo`Ef0t5e{uWAA_m5X93I7WiRBQh`8tMLPGjPNrCIclUoBS5ux<&WC+M1Wev zXUldk1gdBi>u(UK?orKmO$b!0UDfo>QvU_J{Ijd8yLNS{u0~W*ZhvT~3U})(=TrW3 zHB`77?LS*XMY;L$fVJ1QxcO?!8sk64gsTPq)S3k9|6DT23Dm#Z?QMGlD)35ZbRBnOCnYP~ev5wMwn{c>EJyZ{%7i_ zLF#RPi7tJsKee8Ua_!3S_pYZR)tiob#Hc46^|VpXI_lR(J@2SL8}$!cRW5%XIjU;@ zH5#Fg7U5{Ij@H!CQXI|eXzd-XtE1&P+5krz>S!YzZJeV`b+j3dHpkJ59j(OCmOEOh zqit}sEsnO+(e^u9rK25pv=1HaGe`T%(SCHaD~|TNt+`nJcE<(zX>63LfG*U!2S=hLMBW|WF_pTJbjF@55g_%P`N>5mQ_{ga8U z68(Ln$!4j4g2rq9;%Jqu_U_sdt?p(oiC$9e|No!xc16dkt5sA@e`phxqL`IURO3v3 zZDQ#!_8U3>cXM3NWyyEpG(6AA-+?2vPPG0ZFF({kotO++UcW(#pH^Qu_6`RZ^k8`$PRd*28=^ From 9209b42d029738fa0ac4dc6eb8b0ccc2dee9f7b0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 01:42:24 +1100 Subject: [PATCH 814/839] Fix typo --- phlib/appresolver.c | 50 ++++++++++++++++++++++++++++++++++--- phlib/include/appresolver.h | 6 +++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/phlib/appresolver.c b/phlib/appresolver.c index 961234d6e389..423837f22077 100644 --- a/phlib/appresolver.c +++ b/phlib/appresolver.c @@ -93,6 +93,7 @@ static BOOLEAN PhpKernelAppCoreInitialized( AppContainerFreeMemory_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerFreeMemory", 0); AppContainerRegisterSid_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerRegisterSid", 0); AppContainerUnregisterSid_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppContainerUnregisterSid", 0); + AppPolicyGetWindowingModel_I = PhGetProcedureAddress(kernelAppBaseAddress, "AppPolicyGetWindowingModel", 0); } @@ -156,11 +157,52 @@ BOOLEAN PhAppResolverGetAppIdForProcess( 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_STRING PhGetAppContainerName( _In_ PSID AppContainerSid ) -{ - PPH_STRING packageFamilyName = NULL; +{ + PPH_STRING appContainerName = NULL; PWSTR packageMonikerName; if (!PhpKernelAppCoreInitialized()) @@ -168,11 +210,11 @@ PPH_STRING PhGetAppContainerName( if (SUCCEEDED(AppContainerLookupMoniker_I(AppContainerSid, &packageMonikerName))) { - packageFamilyName = PhCreateString(packageMonikerName); + appContainerName = PhCreateString(packageMonikerName); AppContainerFreeMemory_I(packageMonikerName); } - return packageFamilyName; + return appContainerName; } PPH_STRING PhGetAppContainerPackageName( diff --git a/phlib/include/appresolver.h b/phlib/include/appresolver.h index 45aeefbfa618..85c2f6d3e81e 100644 --- a/phlib/include/appresolver.h +++ b/phlib/include/appresolver.h @@ -32,6 +32,12 @@ BOOLEAN PhAppResolverGetAppIdForProcess( _Out_ PPH_STRING *ApplicationUserModelId ); +HRESULT PhAppResolverActivateAppId( + _In_ PPH_STRING AppUserModelId, + _In_opt_ PWSTR CommandLine, + _Out_opt_ HANDLE *ProcessId + ); + PPH_STRING PhGetAppContainerName( _In_ PSID AppContainerSid ); From de2db0a7387b1b68117518fe132871e2dafa92e2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 01:50:18 +1100 Subject: [PATCH 815/839] Improve startup initialization, Fix handle check, Remove duplicate main window delayed load --- ProcessHacker/include/mainwnd.h | 4 + ProcessHacker/include/mainwndp.h | 6 +- ProcessHacker/mainwnd.c | 133 ++++++++++++------------------- 3 files changed, 57 insertions(+), 86 deletions(-) diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index 8c24ec999766..1ee352a36662 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -207,6 +207,10 @@ BOOLEAN PhMainWndInitialization( _In_ INT ShowCommand ); +BOOLEAN PhInitializeRestartPolicy( + VOID + ); + VOID PhAddMiniProcessMenuItems( _Inout_ struct _PH_EMENU_ITEM *Menu, _In_ HANDLE ProcessId diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 14f212db7e8c..cbb1f7a5d9e8 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -49,7 +49,7 @@ VOID PhMwpInitializeControls( VOID ); -NTSTATUS PhMwpDelayedLoadFunction( +NTSTATUS PhMwpLoadStage1Worker( _In_ PVOID Parameter ); @@ -106,10 +106,6 @@ VOID PhMwpOnSetFocus( VOID ); -VOID PhMwpOnTimer( - _In_ ULONG Id - ); - BOOLEAN PhMwpOnNotify( _In_ NMHDR *Header, _Out_ LRESULT *Result diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 6ea93141f474..057818e6457f 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -156,17 +156,22 @@ BOOLEAN PhMainWndInitialization( NULL ); PhDeleteStringBuilder(&stringBuilder); - PhMainWndMenuHandle = GetMenu(PhMainWndHandle); if (!PhMainWndHandle) return FALSE; + PhMainWndMenuHandle = GetMenu(PhMainWndHandle); PhMwpInitializeMainMenu(PhMainWndMenuHandle); // Choose a more appropriate rectangle for the window. PhAdjustRectangleToWorkingArea(PhMainWndHandle, &windowRectangle); - MoveWindow(PhMainWndHandle, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); + MoveWindow( + PhMainWndHandle, + windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, + FALSE + ); + UpdateWindow(PhMainWndHandle); // Allow WM_PH_ACTIVATE to pass through UIPI. ChangeWindowMessageFilter(WM_PH_ACTIVATE, MSGFLT_ADD); @@ -178,21 +183,17 @@ BOOLEAN PhMainWndInitialization( PhMwpLoadSettings(); PhLogInitialization(); - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhMwpDelayedLoadFunction, NULL); - - PhMwpSelectionChangedTabControl(-1); - - // Perform a layout. - PhMwpOnSize(); + // Start the main providers. 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); + // Queue delayed init functions. + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhMwpLoadStage1Worker, NULL); - UpdateWindow(PhMainWndHandle); + // Perform a layout. + PhMwpSelectionChangedTabControl(-1); + PhMwpOnSize(); if ((PhStartupParameters.ShowHidden || PhGetIntegerSetting(L"StartHidden")) && PhNfIconsEnabled()) ShowCommand = SW_HIDE; @@ -305,11 +306,6 @@ LRESULT CALLBACK PhMwpWndProc( PhMwpOnSetFocus(); } break; - case WM_TIMER: - { - PhMwpOnTimer((ULONG)wParam); - } - break; case WM_NOTIFY: { LRESULT result; @@ -362,18 +358,23 @@ VOID PhMwpInitializeProviders( if (interval == 0) { - interval = 1000; + interval = PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM; PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, interval); } + // See PhMwpLoadStage1Worker for more details. + if (interval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) + interval = 1000; + 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); + + PhSetEnabledProvider(&PhMwpProcessProviderRegistration, TRUE); + PhSetEnabledProvider(&PhMwpServiceProviderRegistration, TRUE); } VOID PhMwpApplyUpdateInterval( @@ -482,14 +483,32 @@ VOID PhMwpInitializeControls( CurrentPage = PageList->Items[0]; } -NTSTATUS PhMwpDelayedLoadFunction( +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 either make things appear more quickly or delay the . + + if (PhCsUpdateInterval >= PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) + { + PhDelayExecution(PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1); + } + + PhMwpApplyUpdateInterval(PhCsUpdateInterval); + + 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); + SetProcessShutdownParameters(0x100, 0); + PhInitializeRestartPolicy(); DelayedLoadCompleted = TRUE; //PostMessage(PhMainWndHandle, WM_PH_DELAYED_LOAD_COMPLETED, 0, 0); @@ -1653,51 +1672,6 @@ VOID PhMwpOnSetFocus( 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; - case 3: - { - KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); - } - break; - } - - PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); - PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); - } -} - BOOLEAN PhMwpOnNotify( _In_ NMHDR *Header, _Out_ LRESULT *Result @@ -2147,6 +2121,16 @@ VOID PhMwpLoadSettings( 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"); + PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); + if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) { AlwaysOnTop = TRUE; @@ -2154,23 +2138,10 @@ VOID PhMwpLoadSettings( SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); } - opacity = PhGetIntegerSetting(L"MainWindowOpacity"); - if (opacity != 0) PhSetWindowOpacity(PhMainWndHandle, opacity); - PhStatisticsSampleCount = PhGetIntegerSetting(L"SampleCount"); - PhEnablePurgeProcessRecords = !PhGetIntegerSetting(L"NoPurgeProcessRecords"); - PhEnableCycleCpuUsage = !!PhGetIntegerSetting(L"EnableCycleCpuUsage"); - PhEnableServiceNonPoll = !!PhGetIntegerSetting(L"EnableServiceNonPoll"); - PhEnableNetworkProviderResolve = !!PhGetIntegerSetting(L"EnableNetworkResolve"); - - PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); - PhNfLoadStage1(); - PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); - - customFont = PhaGetStringSetting(L"Font"); if (customFont->Length / 2 / 2 == sizeof(LOGFONT)) SendMessage(PhMainWndHandle, WM_PH_UPDATE_FONT, 0, 0); From 90ef1cde3d4d8b2160df1250b27872e211fa600e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 01:54:18 +1100 Subject: [PATCH 816/839] Add missing header --- phlib/include/phbasesup.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index d487e6c56e0c..98aa90c7b216 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -154,6 +154,20 @@ PhLocalTimeToSystemTime( _Out_ PLARGE_INTEGER SystemTime ); +FORCEINLINE +NTSTATUS +NTAPI +PhDelayExecution( + _In_ LONGLONG Interval + ) +{ + LARGE_INTEGER interval; + + interval.QuadPart = -Interval * PH_TIMEOUT_MS; + + return NtDelayExecution(FALSE, &interval); +} + // Heap _May_raise_ From 343c655eec812e830a0689ba229f07c405476511 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 02:23:43 +1100 Subject: [PATCH 817/839] Tidy up previous commits --- ProcessHacker/ProcessHacker.rc | 14 +-- ProcessHacker/main.c | 14 +-- ProcessHacker/options.c | 161 +++++++++------------------------ ProcessHacker/runas.c | 6 +- ProcessHacker/settings.c | 1 - 5 files changed, 54 insertions(+), 142 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 12e2c50f452c..78a1bb4076aa 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -165,13 +165,10 @@ BEGIN 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 "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 @@ -1271,12 +1268,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM 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 + LTEXT "Search path:",IDC_STATIC,7,8,42,8 + EDITTEXT IDC_DBGHELPSEARCHPATH,66,7,177,12,ES_AUTOHSCROLL + CONTROL "Undecorate symbols",IDC_UNDECORATESYMBOLS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,22,81,10 END IDD_MEMEDIT DIALOGEX 0, 0, 441, 269 diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 5efe777b2adf..6a909d2a77d5 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -83,10 +83,6 @@ BOOLEAN PhInitializeMitigationPolicy( VOID ); -BOOLEAN PhInitializeRestartPolicy( - VOID - ); - PPH_STRING PhApplicationDirectory = NULL; PPH_STRING PhApplicationFileName = NULL; PHAPPAPI HFONT PhApplicationFont = NULL; @@ -546,10 +542,14 @@ VOID PhInitializeCommonControls( icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = - ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | + ICC_TREEVIEW_CLASSES | + ICC_BAR_CLASSES | + ICC_TAB_CLASSES | ICC_PROGRESS_CLASS | - ICC_TAB_CLASSES + ICC_COOL_CLASSES | + ICC_STANDARD_CLASSES | + ICC_LINK_CLASS ; InitCommonControlsEx(&icex); @@ -937,7 +937,7 @@ VOID PhpInitializeSettings( if (status == STATUS_FILE_CORRUPT_ERROR) { if (PhShowMessage2( - PhMainWndHandle, + NULL, TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, TD_WARNING_ICON, L"Process Hacker's settings file is corrupt. Do you want to reset it?", diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 8032b5d4e0f0..0a0f0968930f 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -37,7 +37,6 @@ #include #define WM_PH_CHILD_EXIT (WM_APP + 301) -#define WM_PH_SHOWDIALOG (WM_APP + 302) INT_PTR CALLBACK PhpOptionsGeneralDlgProc( _In_ HWND hwndDlg, @@ -135,7 +134,6 @@ VOID PhpSetDefaultTaskManager( ); static HWND PhOptionsWindowHandle = NULL; -static HANDLE PhOptionsWindowThreadHandle = NULL; static PH_EVENT PhOptionsWindowInitializedEvent = PH_EVENT_INIT; static PPH_LIST PhOptionsDialogList = NULL; static PH_LAYOUT_MANAGER WindowLayoutManager; @@ -155,7 +153,6 @@ static BOOLEAN CurrentUserRunPresent = FALSE; static BOOLEAN CurrentUserRunStartHidden = 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"); @@ -165,58 +162,6 @@ static HWND WindowHandleForElevate = NULL; // Highlighting static HWND HighlightingListViewHandle = NULL; -NTSTATUS ShowUpdateDialogThread( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - - PhInitializeAutoPool(&autoPool); - - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_OPTIONS), - NULL, - PhOptionsDialogProc - ); - - PhUpdateCachedSettings(); - ProcessHacker_SaveAllSettings(PhMainWndHandle); - PhInvalidateAllProcessNodes(); - PhReloadSettingsProcessTreeList(); - PhSiNotifyChangeSettings(); - - if (RestartRequired) - { - if (PhShowMessage2( - PhMainWndHandle, - 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); - } - } - - PhDeleteAutoPool(&autoPool); - - PhResetEvent(&PhOptionsWindowInitializedEvent); - - return STATUS_SUCCESS; -} - VOID PhShowOptionsDialog( _In_ HWND ParentWindowHandle ) @@ -227,14 +172,42 @@ VOID PhShowOptionsDialog( } else { - if (!PhTestEvent(&PhOptionsWindowInitializedEvent)) - { - PhCreateThread2(ShowUpdateDialogThread, NULL); + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + !!PhGetIntegerSetting(L"ForceNoParent") ? NULL : ParentWindowHandle, + PhOptionsDialogProc + ); - PhWaitForEvent(&PhOptionsWindowInitializedEvent, NULL); - } + PhUpdateCachedSettings(); + ProcessHacker_SaveAllSettings(PhMainWndHandle); + PhInvalidateAllProcessNodes(); + PhReloadSettingsProcessTreeList(); + PhSiNotifyChangeSettings(); - PostMessage(PhOptionsWindowHandle, WM_PH_SHOWDIALOG, 0, 0); + if (RestartRequired) + { + if (PhShowMessage2( + PhMainWndHandle, + 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); + } + } } } @@ -352,8 +325,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( //PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDC_APPLY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhOptionsWindowHandle, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); - { PPH_OPTIONS_SECTION section; @@ -380,9 +351,9 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhOptionsEnterSectionView(section); PhOptionsOnSize(); - } - PhSetEvent(&PhOptionsWindowInitializedEvent); + EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); + } } break; case WM_NCDESTROY: @@ -404,16 +375,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( PhDeleteLayoutManager(&WindowLayoutManager); } break; - case WM_PH_SHOWDIALOG: - { - if (IsMinimized(hwndDlg)) - ShowWindow(hwndDlg, SW_RESTORE); - else - ShowWindow(hwndDlg, SW_SHOW); - - SetForegroundWindow(hwndDlg); - } - break; case WM_SIZE: { PhOptionsOnSize(); @@ -1162,7 +1123,6 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( ULONG i; LOGFONT font; - GeneralListviewImageList = ImageList_Create(2, 20, ILC_COLOR, 1, 1); comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); listviewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); @@ -1173,7 +1133,7 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( PhSetListViewStyle(listviewHandle, FALSE, TRUE); ListView_SetExtendedListViewStyleEx(listviewHandle, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); - ListView_SetImageList(listviewHandle, GeneralListviewImageList, LVSIL_SMALL); + //ListView_SetImageList(listviewHandle, GeneralListviewImageList, LVSIL_SMALL); PhSetControlTheme(listviewHandle, L"explorer"); PhAddListViewColumn(listviewHandle, 0, 0, 0, LVCFMT_LEFT, 250, L"Name"); PhSetExtendedListView(listviewHandle); @@ -1222,8 +1182,6 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( PhClearReference(&NewFontSelection); PhClearReference(&OldTaskMgrDebugger); - ImageList_Destroy(GeneralListviewImageList); - PhDeleteLayoutManager(&LayoutManager); } break; @@ -1654,49 +1612,13 @@ INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( { case WM_INITDIALOG: { - SetDlgItemText(hwndDlg, IDC_DBGHELPPATH, PhaGetStringSetting(L"DbgHelpPath")->Buffer); SetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); - SetDlgItemCheckForSetting(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); PhInitializeLayoutManager(&LayoutManager, hwndDlg); - PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_BROWSE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPSEARCHPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); } 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_SIZE: { PhLayoutManagerLayout(&LayoutManager); @@ -1704,13 +1626,12 @@ INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( break; case WM_DESTROY: { - PPH_STRING dbgHelpPath = PhaGetDlgItemText(hwndDlg, IDC_DBGHELPPATH); - - if (!PhEqualString(dbgHelpPath, PhaGetStringSetting(L"DbgHelpPath"), TRUE)) + if (!PhEqualString(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH), PhaGetStringSetting(L"DbgHelpSearchPath"), TRUE)) + { RestartRequired = TRUE; + PhSetStringSetting2(L"DbgHelpSearchPath", &(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH)->sr)); + } - PhSetStringSetting2(L"DbgHelpPath", &dbgHelpPath->sr); - PhSetStringSetting2(L"DbgHelpSearchPath", &(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH)->sr)); SetSettingForDlgItemCheck(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); PhDeleteLayoutManager(&LayoutManager); diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index c99eb008b8c1..74fd7ec72b4c 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -192,7 +192,7 @@ VOID PhShowRunAsDialog( DialogBoxParam( PhInstanceHandle, MAKEINTRESOURCE(IDD_RUNAS), - NULL, + !!PhGetIntegerSetting(L"ForceNoParent") ? NULL : ParentWindowHandle, PhpRunAsDlgProc, (LPARAM)ProcessId ); @@ -1213,7 +1213,6 @@ NTSTATUS PhExecuteRunAsCommand( PPH_STRING portName; UNICODE_STRING portNameUs; ULONG attempts; - LARGE_INTEGER interval; if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE))) return PhGetLastWin32ErrorAsNtStatus(); @@ -1264,8 +1263,7 @@ NTSTATUS PhExecuteRunAsCommand( if (NT_SUCCESS(status)) break; - interval.QuadPart = -50 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); + PhDelayExecution(50); } while (--attempts != 0); PhDereferenceObject(portName); diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index eb4d2ab41396..38b80d3fd070 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -36,7 +36,6 @@ VOID PhAddDefaultSettings( 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""); From 082904182dadaa811ea70f38fbf8c92fec834d28 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 17 Feb 2018 03:56:23 +1100 Subject: [PATCH 818/839] Fix runas dialog default username --- ProcessHacker/runas.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 74fd7ec72b4c..31b6ed6a3409 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -795,12 +795,24 @@ INT_PTR CALLBACK PhpRunAsDlgProc( if (!context->ProcessId) { - SetWindowText( - context->UserComboBoxWindowHandle, - PH_AUTO_T(PH_STRING, PhGetStringSetting(L"RunAsUserName"))->Buffer - ); + 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 From 0d4344c3944ee6bf30b52520a6c208b7fcd6250e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 19 Feb 2018 11:56:53 +1100 Subject: [PATCH 819/839] Tidy up main provider initialization --- ProcessHacker/mainwnd.c | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 057818e6457f..d2740e6a341f 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -108,10 +108,6 @@ BOOLEAN PhMainWndInitialization( if (PhGetIntegerSetting(L"FirstRun")) PhSetIntegerSetting(L"FirstRun", FALSE); - // Initialize the main providers. - - PhMwpInitializeProviders(); - // Initialize the window. if ((windowAtom = PhMwpInitializeWindowClass()) == INVALID_ATOM) @@ -184,9 +180,8 @@ BOOLEAN PhMainWndInitialization( PhMwpLoadSettings(); PhLogInitialization(); - // Start the main providers. - PhStartProviderThread(&PhPrimaryProviderThread); - PhStartProviderThread(&PhSecondaryProviderThread); + // Initialize the main providers. + PhMwpInitializeProviders(); // Queue delayed init functions. PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhMwpLoadStage1Worker, NULL); @@ -352,22 +347,15 @@ VOID PhMwpInitializeProviders( VOID ) { - ULONG interval; - - interval = PhGetIntegerSetting(L"UpdateInterval"); - - if (interval == 0) + if (PhCsUpdateInterval == 0) { - interval = PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM; - PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, interval); + PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM); } // See PhMwpLoadStage1Worker for more details. - if (interval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) - interval = 1000; - PhInitializeProviderThread(&PhPrimaryProviderThread, interval); - PhInitializeProviderThread(&PhSecondaryProviderThread, interval); + PhInitializeProviderThread(&PhPrimaryProviderThread, PhCsUpdateInterval); + PhInitializeProviderThread(&PhSecondaryProviderThread, PhCsUpdateInterval); PhRegisterProvider(&PhPrimaryProviderThread, PhProcessProviderUpdate, NULL, &PhMwpProcessProviderRegistration); PhRegisterProvider(&PhPrimaryProviderThread, PhServiceProviderUpdate, NULL, &PhMwpServiceProviderRegistration); @@ -375,6 +363,9 @@ VOID PhMwpInitializeProviders( PhSetEnabledProvider(&PhMwpProcessProviderRegistration, TRUE); PhSetEnabledProvider(&PhMwpServiceProviderRegistration, TRUE); + + PhStartProviderThread(&PhPrimaryProviderThread); + PhStartProviderThread(&PhSecondaryProviderThread); } VOID PhMwpApplyUpdateInterval( @@ -489,16 +480,9 @@ NTSTATUS PhMwpLoadStage1Worker( { // 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 either make things appear more quickly or delay the . - - if (PhCsUpdateInterval >= PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) - { - PhDelayExecution(PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1); - } - - PhMwpApplyUpdateInterval(PhCsUpdateInterval); + // starts up to either make things appear more quickly. - if (PhCsUpdateInterval >= PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) + if (PhCsUpdateInterval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) { PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); From 9bc7f86a6fde4468ef02aefaab88a4d82c29608e Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 19 Feb 2018 12:00:17 +1100 Subject: [PATCH 820/839] Update ShowContinueStatus with task dialog support, Fix string format crash --- phlib/util.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/phlib/util.c b/phlib/util.c index 4c45de2b944b..474be742bff5 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -418,10 +418,9 @@ INT PhShowMessage2( if (!message) return -1; - config.hwndParent = hWnd; - config.hInstance = PhInstanceHandle; config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); config.dwCommonButtons = Buttons; + config.hwndParent = hWnd; config.pszWindowTitle = PhApplicationName; config.pszMainIcon = Icon; config.pszMainInstruction = Title; @@ -540,30 +539,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; } From e9cd2587a4b3a1e08df2c0f1120181524fd6c81b Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 02:42:17 +1100 Subject: [PATCH 821/839] Improve network address comparison --- ProcessHacker/netlist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index 8845df4fcb40..cf1983b12102 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -371,7 +371,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(LocalAddress) { - sortResult = PhCompareStringZ(networkItem1->LocalAddressString, networkItem2->LocalAddressString, TRUE); + sortResult = PhCompareStringZ(networkItem1->LocalAddressString, networkItem2->LocalAddressString, FALSE); } END_SORT_FUNCTION @@ -389,7 +389,7 @@ END_SORT_FUNCTION BEGIN_SORT_FUNCTION(RemoteAddress) { - sortResult = PhCompareStringZ(networkItem1->RemoteAddressString, networkItem2->RemoteAddressString, TRUE); + sortResult = PhCompareStringZ(networkItem1->RemoteAddressString, networkItem2->RemoteAddressString, FALSE); } END_SORT_FUNCTION From 9be955e7757146599e4e7a72864efe4a5dc76697 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 02:45:37 +1100 Subject: [PATCH 822/839] Add permissons button to token tab --- ProcessHacker/ProcessHacker.rc | 1 + ProcessHacker/prpgtok.c | 1 + ProcessHacker/tokprp.c | 49 ++++++++++++++++++---------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 78a1bb4076aa..933c45a7bfab 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -724,6 +724,7 @@ BEGIN 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 END IDD_HIDDENPROCESSES DIALOGEX 0, 0, 337, 221 diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index 5b46413e304a..060811d57673 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -76,6 +76,7 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( 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_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); diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index 9f8d04df62b7..bd25d2346d1f 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -891,6 +891,31 @@ INT_PTR CALLBACK PhpTokenPageProc( PhCopyListView(tokenPageContext->ListViewHandle); } break; + case IDC_PERMISSIONS: + { + PH_STD_OBJECT_SECURITY stdObjectSecurity; + PPH_ACCESS_ENTRY accessEntries; + ULONG numberOfAccessEntries; + + stdObjectSecurity.OpenObject = tokenPageContext->OpenObject; + stdObjectSecurity.ObjectType = L"Token"; + stdObjectSecurity.Context = tokenPageContext->Context; + + if (PhGetAccessEntries(L"Token", &accessEntries, &numberOfAccessEntries)) + { + PhEditSecurity( + hwndDlg, + L"Token", + PhStdGetObjectSecurity, + PhStdSetObjectSecurity, + &stdObjectSecurity, + accessEntries, + numberOfAccessEntries + ); + PhFree(accessEntries); + } + } + break; case IDC_INTEGRITY: { NTSTATUS status; @@ -1117,12 +1142,9 @@ VOID PhpShowTokenAdvancedProperties( ) { PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[6]; + HPROPSHEETPAGE pages[5]; PROPSHEETPAGE page; ULONG numberOfPages; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | @@ -1193,25 +1215,6 @@ VOID PhpShowTokenAdvancedProperties( 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); } From 1ee22b9895b6eb8a10ec6e1b45d8317c55a8ab1e Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 12:58:04 +1100 Subject: [PATCH 823/839] Add RS4 information classes --- phnt/include/ntexapi.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index f39ecbd000a0..47dbf28604e4 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1394,7 +1394,13 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemSecureDumpEncryptionInformation, SystemWriteConstraintInformation, // SYSTEM_WRITE_CONSTRAINT_INFORMATION SystemKernelVaShadowInformation, // SYSTEM_KERNEL_VA_SHADOW_INFORMATION - SystemSpeculationControlInformation = 201, // SYSTEM_SPECULATION_CONTROL_INFORMATION + SystemHypervisorSharedPageInformation, // REDSTONE4 + SystemFirmwareBootPerformanceInformation, + SystemCodeIntegrityVerificationInformation, + SystemFirmwarePartitionInformation, // 200 + SystemSpeculationControlInformation, // SYSTEM_SPECULATION_CONTROL_INFORMATION // (CVE-2017-5715) REDSTONE3 and above. + SystemDmaGuardPolicyInformation, + SystemEnclaveLaunchControlInformation, MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; From 7ce10e3aab6a00ddff13e250367f772b926275e4 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 12:58:40 +1100 Subject: [PATCH 824/839] Fix typo --- phlib/mapexlf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/mapexlf.c b/phlib/mapexlf.c index 796d517f9e82..a4c8f8a36cd5 100644 --- a/phlib/mapexlf.c +++ b/phlib/mapexlf.c @@ -137,7 +137,7 @@ PVOID PhGetMappedWslImageSectionData( { section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); - if (strcmp(Name, PTR_ADD_OFFSET(stringTable, section->sh_name)) == 0) + if (PhEqualBytesZ(Name, PTR_ADD_OFFSET(stringTable, section->sh_name), FALSE)) { return PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); } From adef72b934dd7ec49346a719b72fc0689af8823a Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 13:50:53 +1100 Subject: [PATCH 825/839] Fix missing macro parameter --- phlib/include/phutil.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 8fd56b6ea7bf..6b09b3289c9e 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -238,9 +238,9 @@ PhShowMessage2( ... ); -#define PhShowError2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, Format, __VA_ARGS__) -#define PhShowWarning2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_WARNING_ICON, Format, __VA_ARGS__) -#define PhShowInformation2(hWnd, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_INFORMATION_ICON, Format, __VA_ARGS__) +#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 From 05233ba1aec64d28d7518a4d440b439dc5039d15 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 14:03:10 +1100 Subject: [PATCH 826/839] Fix error dialog crash --- ProcessHacker/plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 8554c80d545d..0c2e4c82d47d 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -302,6 +302,7 @@ VOID PhLoadPlugins( PhShowError2( NULL, L"Unable to load the following plugin(s)", + L"%s", sb.String->Buffer ); From f5d2df8caeb8e7549e0aa2314db67934a1426104 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 14:13:10 +1100 Subject: [PATCH 827/839] Fix PhShowError2 crash --- phlib/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phlib/util.c b/phlib/util.c index 474be742bff5..af696f8c731e 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -506,7 +506,7 @@ VOID PhShowStatus( if (Message) { - PhShowError2(hWnd, Message, statusMessage->Buffer); + PhShowError2(hWnd, Message, L"%s", statusMessage->Buffer); } else { From 1693d0ffb6588185dc37876b877a02d19ecde974 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 14:49:44 +1100 Subject: [PATCH 828/839] Fix crash from commit 4bcaaa0b --- phlib/util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/phlib/util.c b/phlib/util.c index af696f8c731e..c630c86dcf05 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -5006,7 +5006,6 @@ BOOLEAN PhParseCommandLineFuzzy( } } - PhDereferenceObject(filePathSr); PhFree(temp.Buffer); *FileName = *CommandLine; From c2108377e53a89978d89bdaf9298607ed26f61c0 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 14:51:57 +1100 Subject: [PATCH 829/839] ExtendedTools: Fix disk tab filenames when running under wow64 --- plugins/ExtendedTools/etwmon.c | 32 ++++++++++++++++++++++++++------ plugins/ExtendedTools/etwmon.h | 6 ++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/plugins/ExtendedTools/etwmon.c b/plugins/ExtendedTools/etwmon.c index 7c4823ae21ba..c9787ceeb6a2 100644 --- a/plugins/ExtendedTools/etwmon.c +++ b/plugins/ExtendedTools/etwmon.c @@ -289,10 +289,20 @@ VOID NTAPI EtpEtwEventCallback( if (fileEvent.Type != -1) { - FileIo_Name *data = EventRecord->UserData; + 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); + fileEvent.FileObject = (PVOID)data->FileObject; + PhInitializeStringRef(&fileEvent.FileName, data->FileName); + } EtDiskProcessFileEvent(&fileEvent); } @@ -521,10 +531,20 @@ VOID NTAPI EtpRundownEtwEventCallback( if (fileEvent.Type != -1) { - FileIo_Name *data = EventRecord->UserData; + 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); + fileEvent.FileObject = (PVOID)data->FileObject; + PhInitializeStringRef(&fileEvent.FileName, data->FileName); + } EtDiskProcessFileEvent(&fileEvent); } diff --git a/plugins/ExtendedTools/etwmon.h b/plugins/ExtendedTools/etwmon.h index 85f3e399bb98..68aef52ff55c 100644 --- a/plugins/ExtendedTools/etwmon.h +++ b/plugins/ExtendedTools/etwmon.h @@ -22,6 +22,12 @@ typedef struct WCHAR FileName[1]; } FileIo_Name; +typedef struct +{ + ULONGLONG FileObject; + WCHAR FileName[1]; +} FileIo_Name_Wow64; + typedef struct { ULONG PID; From c85265c1f05bac2a1b516071b90b87003229ac75 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 24 Feb 2018 21:06:20 +1100 Subject: [PATCH 830/839] Fix extended statistics version check --- ProcessHacker/prpgstat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index e2cb541e8c58..61c7b7b75a58 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -118,7 +118,7 @@ VOID PhpUpdateStatisticsAddListViewGroups( PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, L"GDI handles", NULL); PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, L"USER handles", NULL); - if (WindowsVersion >= WINDOWS_10_RS3 || !PhIsExecutingInWow64()) + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) { PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, L"ContextSwitches", NULL); PhAddListViewGroupItem(ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_DISKENERGY, L"DiskEnergy", NULL); @@ -238,7 +238,7 @@ VOID PhpUpdateProcessStatistics( PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, 1, PhGetStringOrEmpty(userHandles)); } - if (WindowsVersion >= WINDOWS_10_RS3 || !PhIsExecutingInWow64()) + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) { PVOID processes; PSYSTEM_PROCESS_INFORMATION processInfo; From 1c4fc097ebde036f7f7ea6aa7e11a7307dcab045 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 15:12:15 +1100 Subject: [PATCH 831/839] Fix missing object name for named job objects (Thanks Alex!) --- phlib/hndlinfo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 9b1bafe98f92..a6d7bc20cfae 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -699,6 +699,10 @@ NTSTATUS PhpGetBestObjectName( if (!NT_SUCCESS(status)) goto CleanupExit; + // dmex: Don't do anything when we already have a valid job object name. + if (!PhIsNullOrEmptyString(ObjectName)) + goto CleanupExit; + if (handleGetClientIdName && NT_SUCCESS(PhGetJobProcessIdList(dupHandle, &processIdList))) { PH_STRING_BUILDER sb; From 035a10e9307bed0ecfe830c091cb58da57f3736b Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 17:09:02 +1100 Subject: [PATCH 832/839] Fix handle leak --- phlib/hndlinfo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index a6d7bc20cfae..ffc797c112bf 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -686,6 +686,10 @@ NTSTATUS PhpGetBestObjectName( 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, @@ -699,10 +703,6 @@ NTSTATUS PhpGetBestObjectName( if (!NT_SUCCESS(status)) goto CleanupExit; - // dmex: Don't do anything when we already have a valid job object name. - if (!PhIsNullOrEmptyString(ObjectName)) - goto CleanupExit; - if (handleGetClientIdName && NT_SUCCESS(PhGetJobProcessIdList(dupHandle, &processIdList))) { PH_STRING_BUILDER sb; From 66e43f03d0608df70fdac5104a1b41ff1c8bbb04 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 18:50:39 +1100 Subject: [PATCH 833/839] Add some wrapper functions --- phlib/include/phutil.h | 35 ++- phlib/util.c | 536 +++++++++++++++++++++++++++++++++-------- 2 files changed, 460 insertions(+), 111 deletions(-) diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 6b09b3289c9e..6296d8c2b814 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -597,20 +597,11 @@ PhGetSystemRoot( _Out_ PPH_STRINGREF SystemRoot ); -PHLIBAPI -PLDR_DATA_TABLE_ENTRY -NTAPI -PhFindLoaderEntry( - _In_opt_ PVOID DllBase, - _In_opt_ PPH_STRINGREF FullDllName, - _In_opt_ PPH_STRINGREF BaseDllName - ); - PHLIBAPI PPH_STRING NTAPI PhGetDllFileName( - _In_ PVOID DllHandle, + _In_ PVOID DllBase, _Out_opt_ PULONG IndexOfFileName ); @@ -1145,6 +1136,30 @@ PhExtractIcon( _In_ 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 +PhGetLoaderEntryDllBase( + _In_opt_ PWSTR DllName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhLoadPluginImage( + _In_ PPH_STRING FileName, + _Out_opt_ PVOID *BaseAddress + ); + #ifdef __cplusplus } #endif diff --git a/phlib/util.c b/phlib/util.c index c630c86dcf05..71d7ca6b29da 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -2052,106 +2052,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. */ @@ -5374,4 +5274,438 @@ BOOLEAN PhExtractIcon( return FALSE; return PrivateExtractIconExW(FileName, 0, IconLarge, IconSmall, 1) > 0; -} \ No newline at end of file +} + +/** + * 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); + PhDereferenceObject(fileName); + fileName = newFileName; + + if (IndexOfFileName) + { + indexOfFileName = PhFindLastCharInString(fileName, 0, '\\'); + + if (indexOfFileName != -1) + indexOfFileName++; + else + indexOfFileName = 0; + + *IndexOfFileName = (ULONG)indexOfFileName; + } + + return fileName; +} + +PVOID PhGetLoaderEntryDllBase( + _In_opt_ 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; +} + +NTSTATUS PhGetLoaderEntryImageNtHeaders( + _In_ PVOID BaseAddress, + _Out_ PIMAGE_NT_HEADERS *ImageNtHeaders + ) +{ + PIMAGE_NT_HEADERS ntHeader; + PIMAGE_DOS_HEADER dosHeader; + 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_FAIL_CHECK; // 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_ PVOID *ImageDirectoryEntry, + _Out_opt_ SIZE_T *ImageDirectoryLength + ) +{ + IMAGE_DATA_DIRECTORY directory; + + directory = ImageNtHeader->OptionalHeader.DataDirectory[ImageDirectoryIndex]; + + if (directory.VirtualAddress == 0 || directory.Size == 0) + return STATUS_INVALID_FILE_FOR_SECTION; + + *ImageDirectoryEntry = PTR_ADD_OFFSET(BaseAddress, directory.VirtualAddress); + if (ImageDirectoryLength) *ImageDirectoryLength = directory.Size; + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetLoaderEntryImageSection( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ PIMAGE_IMPORT_DESCRIPTOR ImageDirectory, + _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)ImageDirectory >= (ULONG_PTR)PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress)) && + ((ULONG_PTR)ImageDirectory < (ULONG_PTR)PTR_ADD_OFFSET(PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData)) + ) + { + directorySectionLength = sectionHeader->Misc.VirtualSize; + directorySectionHeader = PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress); + break; + } + } + + if (directorySectionHeader && directorySectionLength) + { + *ImageSection = directorySectionHeader; + *ImageSectionLength = directorySectionLength; + return STATUS_SUCCESS; + } + + return STATUS_SECTION_NOT_IMAGE; +} + +static NTSTATUS PhpFixupLoaderEntryImageImports( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader + ) +{ + NTSTATUS status; + SIZE_T importDirectorySize; + ULONG importDirectoryLength; + PIMAGE_IMPORT_DESCRIPTOR importDirectory; + PIMAGE_SECTION_HEADER importDirectorySection; + + status = PhGetLoaderEntryImageDirectory( + BaseAddress, + ImageNtHeader, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &importDirectory, + NULL + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetLoaderEntryImageSection( + BaseAddress, + ImageNtHeader, + importDirectory, + &importDirectorySection, + &importDirectorySize + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySection, + &importDirectorySize, + PAGE_READWRITE, + &importDirectoryLength + ); + + 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 = NtCurrentPeb()->ImageBaseAddress; + } + else + { + PPH_STRING importNameSr; + UNICODE_STRING dllNameUs; + + importNameSr = PhZeroExtendToUtf16(importName); + PhStringRefToUnicodeString(&importNameSr->sr, &dllNameUs); + + if (!NT_SUCCESS(status = LdrGetDllHandle(NULL, NULL, &dllNameUs, &importBaseAddress))) + { + 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 = PhGetProcedureAddress(importBaseAddress, NULL, procedureOrdinal); + + if (!procedureAddress) + { + status = STATUS_ORDINAL_NOT_FOUND; + PhShowError(NULL, L"Error locating ordinal: %u\r\nModule: %hs", procedureOrdinal, importName); + 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 = PhGetProcedureAddress(importBaseAddress, importByName->Name, importByName->Hint); + + if (!procedureAddress) + { + status = STATUS_PROCEDURE_NOT_FOUND; + PhShowError(NULL, L"Error locating procedure: %hs\r\nModule: %hs", importByName->Name, importName); + goto CleanupExit; + } + + importThunk->u1.Function = (ULONG_PTR)procedureAddress; + } + } + } + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySection, + &importDirectorySize, + importDirectoryLength, + &importDirectoryLength + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + +CleanupExit: + return status; +} + +// dmex: This function and the other LoaderEntryImage functions don't belong in this file +// and should be moved into mapimg.c at some stage. +// +// We use this function to load plugins since we can 'fixup' the import table at runtime which is required when +// users have renamed the main executable to avoid malware, spyware and other software that targets Process Hacker. +// This function can only fixup images that have static imports from processhacker.exe, +// plugins that use LoadLibrary/GetProcAddress will continue to fail when the main executable is renamed. +// Note: This functionality is a WIP and not be used for anything other than plugins. +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 fileNameUs; + + imageType = IMAGE_FILE_EXECUTABLE_IMAGE; + PhStringRefToUnicodeString(&FileName->sr, &fileNameUs); + + status = LdrLoadDll( + NULL, + &imageType, + &fileNameUs, + &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 = 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; +} From 9d8dc67667eb2674cb80030e6c50ed37ba9b7c1d Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:02:03 +1100 Subject: [PATCH 834/839] Add UNICODE_NULL macro --- ProcessHacker/mainwnd.c | 6 +++--- ProcessHacker/runas.c | 35 ++++++++++++++++++----------------- ProcessHacker/sessprp.c | 2 +- phlib/basesup.c | 8 ++++---- phlib/format.c | 2 +- phlib/util.c | 28 ++++++++++++++-------------- 6 files changed, 41 insertions(+), 40 deletions(-) diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index d2740e6a341f..d03d2cc0718b 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -3310,11 +3310,11 @@ VOID PhMwpUpdateUsersMenu( &returnLength )) { - winStationInfo.Domain[0] = 0; - winStationInfo.UserName[0] = 0; + winStationInfo.Domain[0] = UNICODE_NULL; + winStationInfo.UserName[0] = UNICODE_NULL; } - if (winStationInfo.Domain[0] == 0 || winStationInfo.UserName[0] == 0) + if (winStationInfo.Domain[0] == UNICODE_NULL || winStationInfo.UserName[0] == UNICODE_NULL) { // Probably the Services or RDP-Tcp session. continue; diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index 31b6ed6a3409..15ed2f65e5cb 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -564,13 +564,13 @@ static VOID PhpAddSessionsToComboBox( &returnLength )) { - winStationInfo.Domain[0] = 0; - winStationInfo.UserName[0] = 0; + winStationInfo.Domain[0] = UNICODE_NULL; + winStationInfo.UserName[0] = UNICODE_NULL; } if ( - winStationInfo.UserName[0] != 0 && - sessions[i].WinStationName[0] != 0 + winStationInfo.UserName[0] != UNICODE_NULL && + sessions[i].WinStationName[0] != UNICODE_NULL ) { menuString = PhaFormatString(L"%u: %s (%s\\%s)", @@ -580,7 +580,7 @@ static VOID PhpAddSessionsToComboBox( winStationInfo.UserName ); } - else if (winStationInfo.UserName[0] != 0) + else if (winStationInfo.UserName[0] != UNICODE_NULL) { menuString = PhaFormatString(L"%u: %s\\%s", sessions[i].SessionId, @@ -588,7 +588,7 @@ static VOID PhpAddSessionsToComboBox( winStationInfo.UserName ); } - else if (sessions[i].WinStationName[0] != 0) + else if (sessions[i].WinStationName[0] != UNICODE_NULL) { menuString = PhaFormatString(L"%u: %s", sessions[i].SessionId, @@ -600,18 +600,19 @@ static VOID PhpAddSessionsToComboBox( menuString = PhaFormatString(L"%u", sessions[i].SessionId); } + { + PPH_RUNAS_SESSION_ITEM entry; - PPH_RUNAS_SESSION_ITEM entry; - - entry = PhAllocate(sizeof(PH_RUNAS_SESSION_ITEM)); - entry->SessionId = sessions[i].SessionId; - entry->SessionName = menuString; + entry = PhAllocate(sizeof(PH_RUNAS_SESSION_ITEM)); + entry->SessionId = sessions[i].SessionId; + entry->SessionName = menuString; - INT itemIndex = ComboBox_AddString(ComboBoxHandle, menuString->Buffer); + INT itemIndex = ComboBox_AddString(ComboBoxHandle, menuString->Buffer); - if (itemIndex != CB_ERR) - { - ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + if (itemIndex != CB_ERR) + { + ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + } } } @@ -631,7 +632,7 @@ static BOOL CALLBACK EnumDesktopsCallback( context->CurrentWinStaName->Buffer, L"\\", DesktopName - )); + )); return TRUE; } @@ -1366,7 +1367,7 @@ NTSTATUS PhExecuteRunAsCommand3( parameters.CreateSuspendedProcess = CreateSuspendedProcess; // Try to use an existing instance of the service if possible. - if (RunAsOldServiceName[0] != 0) + if (RunAsOldServiceName[0] != UNICODE_NULL) { PhAcquireQueuedLockExclusive(&RunAsOldServiceLock); diff --git a/ProcessHacker/sessprp.c b/ProcessHacker/sessprp.c index 91e06a80cdee..ffdfb14f1a4c 100644 --- a/ProcessHacker/sessprp.c +++ b/ProcessHacker/sessprp.c @@ -170,7 +170,7 @@ INT_PTR CALLBACK PhpSessionPropertiesDlgProc( PhDereferenceObject(time); } - if (haveClientInfo && clientInfo.ClientName[0] != 0) + if (haveClientInfo && clientInfo.ClientName[0] != UNICODE_NULL) { WCHAR addressString[65]; diff --git a/phlib/basesup.c b/phlib/basesup.c index 35364e49c0af..c14f780e7291 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -2146,7 +2146,7 @@ PPH_STRING PhCreateStringEx( assert(!(Length & 1)); string->Length = Length; string->Buffer = string->Data; - *(PWCHAR)PTR_ADD_OFFSET(string->Buffer, Length) = 0; + *(PWCHAR)PTR_ADD_OFFSET(string->Buffer, Length) = UNICODE_NULL; if (Buffer) { @@ -2433,7 +2433,7 @@ PPH_BYTES PhCreateBytesEx( bytes->Length = Length; bytes->Buffer = bytes->Data; - bytes->Buffer[Length] = 0; + bytes->Buffer[Length] = ANSI_NULL; if (Buffer) { @@ -3474,7 +3474,7 @@ FORCEINLINE VOID PhpWriteNullTerminatorStringBuilder( ) { assert(!(StringBuilder->String->Length & 1)); - *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = 0; + *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = UNICODE_NULL; } /** @@ -3781,7 +3781,7 @@ VOID PhInitializeBytesBuilder( BytesBuilder->AllocatedLength = InitialCapacity; BytesBuilder->Bytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength); BytesBuilder->Bytes->Length = 0; - BytesBuilder->Bytes->Buffer[0] = 0; + BytesBuilder->Bytes->Buffer[0] = ANSI_NULL; } /** diff --git a/phlib/format.c b/phlib/format.c index b45fd38439c4..bd273a8a1981 100644 --- a/phlib/format.c +++ b/phlib/format.c @@ -202,7 +202,7 @@ PPH_STRING PhFormat( string->Length = usedLength; // Null-terminate the string. - string->Buffer[usedLength / sizeof(WCHAR)] = 0; + string->Buffer[usedLength / sizeof(WCHAR)] = UNICODE_NULL; return string; } diff --git a/phlib/util.c b/phlib/util.c index 71d7ca6b29da..2f96846f4347 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -827,7 +827,7 @@ VOID PhGenerateRandomAlphaString( Buffer[i] = 'A' + (RtlRandomEx(&seed) % 26); } - Buffer[Count - 1] = 0; + Buffer[Count - 1] = UNICODE_NULL; } /** @@ -1106,7 +1106,7 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( t = *A; - if (t == 0) + if (t == UNICODE_NULL) { if (MatchIfPrefix) return 0; @@ -1134,7 +1134,7 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( t = *A; - if (t == 0) + if (t == UNICODE_NULL) { if (MatchIfPrefix) return 0; @@ -1169,7 +1169,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)) { @@ -1198,7 +1198,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)) { @@ -1230,7 +1230,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)) { @@ -1426,13 +1426,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); @@ -1446,7 +1446,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)) { @@ -1944,7 +1944,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 / 2] = UNICODE_NULL; // make sure there is a null terminator return string; } @@ -2119,7 +2119,7 @@ PPH_STRING PhGetKnownLocation( SIZE_T appendPathLength; if (AppendPath) - appendPathLength = PhCountStringZ(AppendPath) * 2; + appendPathLength = PhCountStringZ(AppendPath) * sizeof(WCHAR); else appendPathLength = 0; @@ -2137,7 +2137,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 + 2); // +2 for null terminator path->Length += appendPathLength; } @@ -3611,7 +3611,7 @@ OPENFILENAME *PhpCreateOpenFileName( ofn->Flags = OFN_ENABLEHOOK | OFN_EXPLORER; ofn->lpfnHook = PhpOpenFileNameHookProc; - ofn->lpstrFile[0] = 0; + ofn->lpstrFile[0] = UNICODE_NULL; return ofn; } @@ -4863,7 +4863,7 @@ BOOLEAN PhParseCommandLineFuzzy( temp.Buffer = PhAllocate(commandLine.Length + sizeof(WCHAR)); 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; From 4824a014a70d6803b6d26a3bb3c0bcc31502f9dc Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:18:53 +1100 Subject: [PATCH 835/839] Move symbol settings to general options tab --- ProcessHacker/ProcessHacker.rc | 55 ++++++++++++------------- ProcessHacker/options.c | 74 ++++++++-------------------------- ProcessHacker/resource.h | 3 +- 3 files changed, 43 insertions(+), 89 deletions(-) diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 933c45a7bfab..f595f2b01605 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1131,18 +1131,20 @@ BEGIN 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,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 - PUSHBUTTON "Font...",IDC_FONT,179,53,49,14 - PUSHBUTTON "Make default...",IDC_REPLACETASKMANAGER,179,69,72,14 - LTEXT "Graph history length:",IDC_STATIC,106,39,69,8 - EDITTEXT IDC_SAMPLECOUNT,180,38,48,12,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "Automatic",IDC_SAMPLECOUNTAUTOMATIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,233,39,48,10 - CONTROL "",IDC_SETTINGS,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,301,125 - LTEXT "Application font:",IDC_STATIC,121,55,54,8 - RTEXT "Process Hacker is the default Task Manager:",IDC_DEFSTATE,7,71,166,8 + 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 @@ -1264,16 +1266,6 @@ BEGIN 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 "Search path:",IDC_STATIC,7,8,42,8 - EDITTEXT IDC_DBGHELPSEARCHPATH,66,7,177,12,ES_AUTOHSCROLL - CONTROL "Undecorate symbols",IDC_UNDECORATESYMBOLS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,22,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 @@ -2157,14 +2149,6 @@ BEGIN BOTTOMMARGIN, 296 END - IDD_OPTSYMBOLS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 68 - END - IDD_MEMEDIT, DIALOG BEGIN LEFTMARGIN, 7 @@ -2455,6 +2439,17 @@ IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_OPTGENERAL AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 0a0f0968930f..60119f6dfc1b 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -52,13 +52,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( _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, @@ -333,7 +326,6 @@ INT_PTR CALLBACK PhOptionsDialogProc( section = PhOptionsCreateSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); PhOptionsCreateSectionAdvanced(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); - PhOptionsCreateSection(L"Symbols", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTSYMBOLS), PhpOptionsSymbolsDlgProc, NULL); PhOptionsCreateSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); PhOptionsCreateSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); @@ -954,6 +946,7 @@ typedef enum _PHP_OPTIONS_INDEX 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_NETWORK_RESOLVE, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, @@ -962,7 +955,7 @@ typedef enum _PHP_OPTIONS_INDEX 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_INDEX_SHOW_ADVANCED_OPTIONS } PHP_OPTIONS_GENERAL_INDEX; VOID PhpSetListViewItemState( @@ -997,6 +990,7 @@ static VOID PhpAdvancedPageLoad( 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_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_CYCLE_CPU_USAGE, L"Enable cycle-based CPU usage", NULL); @@ -1010,18 +1004,19 @@ static VOID PhpAdvancedPageLoad( 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_ENABLE_WARNINGS, L"EnableWarnings"); - SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_PLUGINS, L"EnablePlugins"); 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_STAGE2, L"EnableStage2"); + 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_NETWORK_RESOLVE, L"EnableNetworkResolve"); - SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, L"PropagateCpuUsage"); SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"EnableInstantTooltips"); SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"EnableStage2"); 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) { @@ -1060,6 +1055,12 @@ static VOID PhpAdvancedPageSave( PhSetIntegerSetting(L"MaxSizeUnit", PhMaxSizeUnit = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT))); PhSetIntegerSetting(L"IconProcesses", GetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, NULL, FALSE)); + 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"); @@ -1067,6 +1068,7 @@ static VOID PhpAdvancedPageSave( 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_NETWORK_RESOLVE, L"EnableNetworkResolve"); SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"EnableInstantTooltips"); SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); @@ -1130,6 +1132,7 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( 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); @@ -1148,8 +1151,8 @@ INT_PTR CALLBACK PhpOptionsGeneralDlgProc( SetDlgItemText(hwndDlg, IDC_SEARCHENGINE, PhaGetStringSetting(L"SearchEngine")->Buffer); SetDlgItemText(hwndDlg, IDC_PEVIEWER, PhaGetStringSetting(L"ProgramInspectExecutables")->Buffer); - SetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, PhGetIntegerSetting(L"IconProcesses"), FALSE); + SetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); ReadCurrentUserRun(); @@ -1599,49 +1602,6 @@ INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( return FALSE; } -INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - static PH_LAYOUT_MANAGER LayoutManager; - - switch (uMsg) - { - case WM_INITDIALOG: - { - SetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); - SetDlgItemCheckForSetting(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); - - PhInitializeLayoutManager(&LayoutManager, hwndDlg); - PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPSEARCHPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&LayoutManager); - } - break; - case WM_DESTROY: - { - if (!PhEqualString(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH), PhaGetStringSetting(L"DbgHelpSearchPath"), TRUE)) - { - RestartRequired = TRUE; - PhSetStringSetting2(L"DbgHelpSearchPath", &(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH)->sr)); - } - - SetSettingForDlgItemCheck(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); - - PhDeleteLayoutManager(&LayoutManager); - } - break; - } - - return FALSE; -} - typedef struct _COLOR_ITEM { PWSTR SettingName; diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index e82c9362d713..aca3b7524fab 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -77,7 +77,6 @@ #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 @@ -744,7 +743,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 247 +#define _APS_NEXT_RESOURCE_VALUE 249 #define _APS_NEXT_COMMAND_VALUE 40297 #define _APS_NEXT_CONTROL_VALUE 1406 #define _APS_NEXT_SYMED_VALUE 170 From b088c8f91ad64085bede9d308d6dbc5ebc5cf130 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:21:55 +1100 Subject: [PATCH 836/839] Fix issues loading plugins when main executable renamed --- ProcessHacker/plugin.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 0c2e4c82d47d..3531ba60d976 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -338,6 +338,7 @@ BOOLEAN PhLoadPlugin( _In_ PPH_STRING FileName ) { + NTSTATUS status; PPH_STRING fileName; PPH_STRING errorMessage; PPHP_PLUGIN_LOAD_ERROR loadError; @@ -347,13 +348,15 @@ BOOLEAN PhLoadPlugin( if (!fileName) PhSetReference(&fileName, FileName); - if (LoadLibrary(fileName->Buffer)) + status = PhLoadPluginImage(fileName, NULL); + + if (NT_SUCCESS(status)) { PhDereferenceObject(fileName); return TRUE; } - errorMessage = PhGetWin32Message(GetLastError()); + errorMessage = PhGetNtMessage(status); loadError = PhAllocate(sizeof(PHP_PLUGIN_LOAD_ERROR)); PhSetReference(&loadError->FileName, fileName); From baad8d3d634074823852f03fcc699f4138613b3a Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:22:11 +1100 Subject: [PATCH 837/839] Fix missing copyright --- ProcessHacker/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index 6a909d2a77d5..054f92f371f5 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -3,6 +3,7 @@ * main program * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * From a2f76581fdc554b73cef9ae7dd970fe49c6a42e7 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:24:43 +1100 Subject: [PATCH 838/839] Add inline NtDelayExecution wrapper --- ProcessHacker/actions.c | 4 +--- ProcessHacker/anawait.c | 7 ++----- ProcessHacker/hidnproc.c | 8 +++----- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 4551bb8a50d9..86cd9e93e1ad 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -452,7 +452,6 @@ BOOLEAN PhUiConnectToPhSvcEx( if (started) { ULONG attempts = 10; - LARGE_INTEGER interval; // Try to connect several times because the server may take // a while to initialize. @@ -463,8 +462,7 @@ BOOLEAN PhUiConnectToPhSvcEx( if (NT_SUCCESS(status)) break; - interval.QuadPart = -50 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); + PhDelayExecution(50); } while (--attempts != 0); // Increment the reference count even if we failed. diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index 93bb2d675458..354a61b29636 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -663,10 +663,8 @@ static BOOLEAN PhpWaitUntilThreadIsWaiting( PVOID processes; PSYSTEM_PROCESS_INFORMATION processInfo; ULONG i; - LARGE_INTEGER interval; - interval.QuadPart = -100 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); + PhDelayExecution(100); if (!NT_SUCCESS(PhEnumProcesses(&processes))) break; @@ -695,8 +693,7 @@ static BOOLEAN PhpWaitUntilThreadIsWaiting( if (isWaiting) break; - interval.QuadPart = -500 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); + PhDelayExecution(500); } return isWaiting; diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index e2398510b385..8bbd8a5bc327 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -318,12 +318,10 @@ static INT_PTR CALLBACK PhpHiddenProcessesDlgProc( if (refresh) { - LARGE_INTEGER interval; + // Sleep for a bit before continuing. It seems to help avoid BSODs. + + PhDelayExecution(250); - // 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); } } From dc7ba9ca9bdb5d50df983aba1cec52ede75cb6e1 Mon Sep 17 00:00:00 2001 From: dmex Date: Mon, 26 Feb 2018 19:48:03 +1100 Subject: [PATCH 839/839] Fix options window crash (Partial revert 6dfc9dd9) --- ProcessHacker/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 60119f6dfc1b..2cbfa8249f06 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -168,7 +168,7 @@ VOID PhShowOptionsDialog( DialogBox( PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTIONS), - !!PhGetIntegerSetting(L"ForceNoParent") ? NULL : ParentWindowHandle, + ParentWindowHandle, PhOptionsDialogProc );

7uv&-vhT>IfZ4%Z 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 diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 8d157c0a3cbc..405c429a6cb0 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -98,7 +98,6 @@ BOOLEAN SetupExtractBuild( } InterlockedExchange64(&ExtractTotalLength, totalLength); - SendMessage(Context, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) @@ -153,7 +152,6 @@ BOOLEAN SetupExtractBuild( goto CleanupExit; extractPath = PhConcatStrings(3, PhGetString(SetupInstallPath), L"\\", PhGetString(fileName)); - //OutputDebugString(PhFormatString(L"%s\r\n", extractPath->Buffer)->Buffer); if (fullSetupPath = PhGetFullPath(extractPath->Buffer, &indexOfFileName)) { @@ -211,7 +209,9 @@ BOOLEAN SetupExtractBuild( goto CleanupExit; currentLength += bufferLength; + InterlockedExchange64(&ExtractCurrentLength, currentLength); + SendMessage(Context, WM_UPDATE_SETUP, 0, (LPARAM)PhGetBaseName(extractPath)); NtClose(fileHandle); From f338e2b7abf9bdbc284ccf3a453a0cac542c0de7 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 10:08:18 +1000 Subject: [PATCH 114/839] BuildTools: Improve build version definitions, Remove legacy version resources --- ProcessHacker/ProcessHacker.rc | 114 ++++++------------ ProcessHacker/ProcessHacker.vcxproj | 24 ++-- ProcessHacker/ProcessHacker.vcxproj.filters | 5 +- ProcessHacker/about.c | 2 +- ProcessHacker/include/phappres.h | 21 ++-- ProcessHacker/include/phapprev.h | 6 - ProcessHacker/include/phapprev_in.h | 6 - ProcessHacker/version.rc | 35 ------ tools/CustomBuildTool/Source Files/Build.cs | 23 ++-- tools/CustomBuildTool/Source Files/Program.cs | 2 - .../bin/Release/CustomBuildTool.exe | Bin 160256 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes .../CustomSetupTool/include/setup.h | 3 +- tools/peview/peview.rc | 75 ++++++------ tools/peview/peview.vcxproj | 16 ++- tools/peview/peview.vcxproj.filters | 3 - tools/peview/version.rc | 35 ------ 17 files changed, 121 insertions(+), 249 deletions(-) delete mode 100644 ProcessHacker/include/phapprev.h delete mode 100644 ProcessHacker/include/phapprev_in.h delete mode 100644 ProcessHacker/version.rc delete mode 100644 tools/peview/version.rc diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 4bc346f7cf65..e9bcdd06a676 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -2527,93 +2527,51 @@ IDR_RT_MANIFEST RT_MANIFEST "ProcessHacker.manifest" ///////////////////////////////////////////////////////////////////////////// // -// AFX_DIALOG_LAYOUT +// PNG // -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 - -IDD_PROCHANDLES AFX_DIALOG_LAYOUT -BEGIN - 0 -END +IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" -IDD_OBJTOKEN AFX_DIALOG_LAYOUT -BEGIN - 0 -END +IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" ///////////////////////////////////////////////////////////////////////////// // -// PNG +// Version // -IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" - -IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" +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 ///////////////////////////////////////////////////////////////////////////// diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index f212a670d21b..ddbc487bed92 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -85,7 +85,7 @@ Disabled ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_PHAPP_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -104,14 +104,12 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - Disabled ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_PHAPP_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -130,15 +128,13 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - MaxSpeed true ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) + _PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded false @@ -163,15 +159,13 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - MaxSpeed true ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions) + _PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true MultiThreaded false @@ -195,8 +189,6 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - @@ -385,8 +377,12 @@ - - + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 5517737409be..e4fac5fddc0e 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -576,9 +576,6 @@ Resources - - Resources - diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index 957c741c0b97..4a8b262e16f0 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -49,7 +49,7 @@ static INT_PTR CALLBACK PhpAboutDlgProc( PhCenterWindow(hwndDlg, GetParent(hwndDlg)); -#if (PHAPP_VERSION_REVISION != 0x0D06F00D) +#if (PHAPP_VERSION_REVISION != 0) appName = PhFormatString( L"Process Hacker %u.%u.%u", PHAPP_VERSION_MAJOR, diff --git a/ProcessHacker/include/phappres.h b/ProcessHacker/include/phappres.h index 3f8215df22e9..c0267bfd3ea9 100644 --- a/ProcessHacker/include/phappres.h +++ b/ProcessHacker/include/phappres.h @@ -1,14 +1,21 @@ -// 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" - +#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 #if (PHAPP_VERSION_BUILD == 0) #define TWO_DIGIT_VER 1 @@ -19,8 +26,6 @@ #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) @@ -29,6 +34,4 @@ #define PHAPP_VERSION_NUMBER PHAPP_VERSION_MAJOR,PHAPP_VERSION_MINOR,PHAPP_VERSION_BUILD,PHAPP_VERSION_REVISION -#endif // ISPP_INVOKED - #endif // PHAPPRES_H diff --git a/ProcessHacker/include/phapprev.h b/ProcessHacker/include/phapprev.h deleted file mode 100644 index cbb6d2096029..000000000000 --- a/ProcessHacker/include/phapprev.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION 0x0D06F00D - -#endif // PHAPPREV_H diff --git a/ProcessHacker/include/phapprev_in.h b/ProcessHacker/include/phapprev_in.h deleted file mode 100644 index c1e40313fd88..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/version.rc b/ProcessHacker/version.rc deleted file mode 100644 index 6caa53e40114..000000000000 --- a/ProcessHacker/version.rc +++ /dev/null @@ -1,35 +0,0 @@ -#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 diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index f0073edf10fd..d79db942fab8 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -12,8 +12,9 @@ public static class Build { private static DateTime TimeStart; private static bool BuildNightly = false; - private static string BuildBranch = "master"; + private static string BuildBranch; private static string BuildOutputFolder = "build"; + private static string BuildCommit; private static string BuildVersion; private static string BuildLongVersion; private static string BuildCount; @@ -231,12 +232,12 @@ public static string GetBuildLogString() public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) { - string currentBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); - string currentCommitTag = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD + BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(currentBranch, ConsoleColor.White); + Program.PrintColorMessage(BuildBranch, ConsoleColor.White); Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(currentCommitTag, ConsoleColor.White); + Program.PrintColorMessage(BuildCommit, ConsoleColor.White); string currentGitTag = Win32.ShellExecute(GitExePath, "describe --abbrev=0 --tags --always").Trim(); BuildRevision = Win32.ShellExecute(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\"").Trim(); @@ -1012,7 +1013,7 @@ public static bool AppveyorUploadBuildFiles() { try { - Win32.ShellExecute("appveyor", "UpdateBuild -Version \"1.0 -$version\" "); + Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); } catch (Exception ex) { @@ -1026,8 +1027,6 @@ public static bool AppveyorUploadBuildFiles() public static bool BuildSolution(string Solution, BuildFlags Flags) { - //string buildParams = "/p:DefineConstants=\"PH_BUILD_API=1;PHAPP_VERSION_REVISION=" + BuildRevision + "\" "; - if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) { Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); @@ -1038,8 +1037,8 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + - "/p:Platform=Win32 " + - //buildParams + + "/p:Platform=Win32 " + + "/p:ExternalCompilerOptions=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + Solution ); @@ -1060,8 +1059,8 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) MSBuildExePath, "/m /nologo /verbosity:quiet " + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + - "/p:Platform=x64 " + - //buildParams + + "/p:Platform=x64 " + + "/p:ExternalCompilerOptions=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + Solution ); diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 960cad65053f..65f25f628144 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -77,8 +77,6 @@ public static void Main(string[] args) BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildDebug | BuildFlags.BuildVerbose ); - - Build.ShowBuildStats(); } else if (ProgramArgs.ContainsKey("-cleansdk")) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index bf9224ba705bdcae546ae48bdc28a10b30dc3005..242914120060012e249192a8733fa077e6dd59d1 100644 GIT binary patch delta 12065 zcmbta33L=y*1oT*yLwI1Te_2Ur@NDo&}4zIChTDo0ztx(ux}DX6bZa2iZSVi01ASV zDFziq7Lfr(6r(H(P7p+K8Og{v3XH-43XX$13yiqTf8VXj0{?&hGv`ds$@jkR-uv!b z>eZ`PmBcPsVwWsyA5K4UE-(@FpRVZ;@_wo3w*D z)sb?LIU454R@O@q5d}u9AHn)tGlR7TVez4*mV6^yeIZL*r~0xXz!ddE!mkNU#wzAe ztBuLgkFj$yfU}l5bXCFpSvZUs+6r$>T+u@=4)uQJ}xnjIw9`2h)MBzh_}SQg7|~r4eb*eWxS4;T({sV>EteE>MoB} zz353$r+Q546CMw2JFn;*3;1wly7Luy8gCQqs8jY$_Z1x#OJXh7ks@QT z4yB{{!X+ZpeZjY(H)L&AyM(g&G+(XQ6N1faey?ac)2ALz@$_#_FTP`k~{W4=#~?!{I}gore^=MZq@mF#5H0(?Rhrd zC0{+}AC>z=J}lthAVk{ZWe;`XWXSKV8^Y_w{F3dS&gy_bQuK9n@skLZ1vuY!Ge3iz zuBK+-KcO~93eg!BJ8{)mWN{YWrwdjYh&;=}OWN?gZFujltuc))oQK+Dlf~U&fiE(y z3^TY5UbnIEDeRfk;52SvyFI&hR~LrT?Q6Q#r+ zDKRGV&2#1`Xr7{qf}7@y3}N-Rp#-T<7xhFaaqycMOn0$9XgmLhc<|7UIJfWyANTac z=Il2&H@K(TA)G6%>5fIVx+Gj0U5jyepO61SWfFH4MYUw->wlGe_bPXenKO0{DMZ}`E`vX@c03unUI60YsAT; zg&K45VH$Ds2^#V6c^XONF^zcnqZ;w?tr|({x^*TxMLu-n#%J9 ziFw78Wa`hWC?7~(A1|jeiO;7pnJ=X>g|DO1&$m+<;Crb|oS2Q}j4D>UNbPiVx=U)G3+AJa%8|5PJhenlfb zZXHamCh-)FB=c;Ir0_l(@$)en3GjIuN#)V|H5TNnH4@@mH4^5pY9x&x(?~l1L?ape zYmKzyKM4}UQ-vWrC`D%BErEA1xbuxL5uaP)N3%-YGXxgY1Tj$Jg@XD8^+lV{4HanH zS`!e)*7dqb7&Co*1(iws2`ZEMZYopwTU7e_Nh$;UODa?Ozo-my`zhNH6fzch9M2Bne889De8;u zAI+GGW~Z@Q6ns=M?q&<{I^<@Ubek8A>a<8oR|=Tb5)f_;5XMM3)~Z}6pb8-?=WeMw zpuP}EW&!o>$RIXVwRD&erQZCa;Qp>uJ%C{m+_rLRXJFHrdA_XLX-IxiwhHfq=*^4? zPejI;nhIn}wZO_+(Fne8S>pha5!@nkYH=TK+v>9-aH$B42~Px1!#QrFINo?Gm!Y}#wj1NOiTLFdFFX-E$MZZdrnk*h$;$K1P`z|5qwR{iC9JL6tylpou#VVvkPq^QzQ-J zYm(K=*}7219y&biCZhNz+o@uH{Zm7uhGShO*$sZ|Jm*oANnSq|r zKdbg*{a8D}uWCF~@PBALOMSKD#>AjhJrs@E>5^PSRx=0!H|n1NaYU z^m)Z~S6bgdT|n=ox$fF_YVZ8?L6kpkcyqgEZvICC}v6{@piRo!z;Mh<>tjAVdl3n&W!nB7@e@ z$A_zTb_uHCt{yG=ne#ManDBDRBw`*oL$4ixuJOI$}TxVU$eT$bH3CVqan zdag@I`sYpUWT{iSCaG0jBa(51y0Pmi$udG++^w&aIHF;Hx2Kt~miHJNjgG*ac$-_T zigt|ap>RWqer~dvwD)DnlWpQp3Trw>YH{{9mTXUUWCixiZqALED{+Ah|k8hisnkEaqNlp5R> zMU^RnkJosK;1e~DCZ%Qqa`lJ8tORV_@OgZe6gi8Xt(l~D?72%S?xvpU*=u~8m#YfJ zY1q*AJz*>v+Pobc(S)cRh<)J(7ST5H{Ujwg3(o4G}L zJl-4Fb8B8c>A&L@)U=lGXOZuYVUuwfd0}oF2@6i(y)8w4ja4cyAyi#PuqrVTQ7a%j zr52|~JpE%-mPek%8R2DEwRVbH-zV8bPo0`#bx$AfB*B$RoHHwZ@v6mBb=w_$3s$ZA z2ECav;SqOlPIkwt&1jB^Crfs@9Jzw-JewQ}cU0t1r(amMFFPn#C#h+Ddv>YBR{fO+ zF(P+_!k3f6i)qd6E75~TQ7Xkl&Ns30dn~-Xa-_PtZ$7I~5B2pYh^3Z&i(H9)RQSq~ z>Q{a9`j&(NGH_qpkL6L^#xus=!{^SQgU?5R2M3~UB-+ZxLeX0MN{Y6Op=HH`u`EUX zggUxkCpJf2((iqfnx??`1XUf|Kf|~%Y=r@8b^kwgd^T;1OMy3B0zYyYFy`Taz-8`C zS%Jsgg5OI#YA~d!D~fVM4~PEbR^W(3;0&+8U0xBq-y(3a+Ei3v6AkPQ_4HZcsOlc@ zRJtiF;vS>a7bvOKBT~D}0Vw%bVhI{!%li%G0?V%H?rFNkTgmO6GVycMjhiVH>)ucqf z8&Ktw8j;dgP1Jl*=tj;}*n|izAoUz36{?WbLTpf|UYajSQv*p&)O~|VP1m!Oka|@2 zO(nI}rq!EG!wrYEjz;Iucz3#m90m{4m|hBqF%`oS^8HMqqeyw+l2=5Ig5{KEFg797 zLlhbD2{jqk;p%X}?J3MJ)DwW;I{`Nx7!6ZlBgknEu;MdrG)#j{QF6M-SpnPIIB$oY z;DmpMhM1kuF|8Q!io?U4P?RL_+n~Vr@N20PE)bpy3BH(komub-R|(b{O_=Hw76)M} z<#~>HbOjZ^CVZK&5wSCTpEAna8QxA6xDQc*D^?Fvz!DgX{6flPGlS7-9$XMRFp|Ls zeu2+hgr&hJ@H6aad916nNhyzAk~b+m;Gk4t?g3i}*AhmfSSZSO@*Hz{Y>0d(;z+^@ z!j*(i5iTN}CyAIc!cJuQ2l1DPzeCtW77Ov-$eZ&Ix6Q}8Pq_qcFbI6k5EaGmQw4rt zpN}K>)*$-+q9QPta2VkOgtrl%CtOGPf>A`85xwxc0H?;ph=<#%G3W81z>|dM9fEhZ zSDVuy%~*{1n6WsDMSrZE291QG;WQ{CevJ6vh<`$uK$efOi)p}(Vr6vjL=kQFWq46; z#Wm3b;-r;kFFXZn5M!_&aTD8&*vMW+Jk1(0a;v`yu^l^Yj*1F@K}Q6>Gna;O9m0Aj z30=pT_!Rd-q2`1(8YQ?N&yhks=hzMstkqRC^l!5S3-Bx{e7V7mMkAcj)jq#sF~Ybk z%{R_(MU~rLQ$tYU<`w~bLBNW0Qx%H|9NC(4hfk`r-J6%11Nlj3Kf07XO9!R=w zHo?g}O}&jiGhFDXsZSDwignVI2eY70S7B0T@`f+>j0q*CO%8wK%{qLa69^xSlXpY!)Qf!@m;7TU>BZSIZOb05_E4VMxr= zFw0a+B7CE(zb0J;eC@!W7_1LlEESfN7WJ`dt_5FDbl;y%3oUrq)YZ=L{gzO(a)mz( zKZw$W4_STm>I=h?<$2+y>N~?al&!YVYC_*JW8ajv>gUw@HomW%>)WVYjHGo(^>(K` z-9|O0Y*mL8SEjCJtr36p>}V75mia}sso2lzeiUyd=6Bv>uL&iv?si!t2?lx(Vp*_kGFLgBt*HZ~xiR!Y%ZpEJd zOH;UYC7?QkF9KrPPWuzBBcTG{qQnT#`mRDLRNz~RQ0cgnjDeqYWk5Afzc6*Q0hYr_ zeCfi!aGJ*?1+C=}hc6{U{oU3Mm1nYeh@kwpEgNM(cP50l!}uGn?|A4&zB-r#U$F6@ zlG1kE2{%MefK7VjHt1}f0I%t474)!9ghRU8$@*9)!Ml^;PHeso-ebe8li`H!yvW8` zr`(WqDtxN@a@b7kR2Yr#Qlj2fHrg@`=91DnGyR5o)8Sq{vNPPRE*VwKI;iiDx-aF^ zFbgvm9hbHxOk_NNJ5RBh-E z=Olwv!7gLEJJH+0P{n?bjs_L*Krr+WI|t91R;zo)`pSNbnHRf`BSu_e81FfnkY5c5 z%g=!?*k#Q89pZS;b;P>@OzJGxC7Y!0VN=L1^^_k7R?wt4rOWV9f)DL?gjcJn<9yMf zQZB|k#Bzm&wS%7nv!!-WZI~?u5bu(@QJ?z|4u-nqEm8@^FP4_NzQEQ$N_a);L-Bnu z^K9fI!#Ip@lm^JX(~e<=6QTXM^NMW6vS?+B?p+b=K2FBDrqID@)8%=vE%1nZ4>q$| zeh`~^OU8_y>CxE|1Z8g!_TL5PIIoa6!I|c*wHPxR|8`-$eY%a@2T0 zj>8RjuFUWeyihu1de68}l0v^T9+uZR&KR9o>(53D-Z@_zk4NRF0^b>%!( ze9BNSaf5+1$-9${N*adRls(cSZ<5j^KZBMIXz8eQMpWRkY;g8azL)no`YA{Ci@U;h zmoh;9BD6~RjQ!oZ2~o0bQg%XD!&by+aMQ5JpV4IQ==>zLHR@I}yTItelMntT%C*+aD0 zo7g<~RYJC@v!UAF)wEdpA*ll9;ffq=GQ%>Miuf?xX0pKMC<+T~gE@#hU>-Vll4Tbx zME)Z2mtm2~g^bG}lM?wF9bte3eI`$+sV_}OBBTV!?m(4-UXQI2^nCNIB zdfVe}KgZMoj=)E89r0Md*4hzI*H0q)VLxI997fC`>9pNtMidY3jwn6X+Hc*TWoR6D}cr9)vqB(viVle&)d5cr>m~{+PIm_^d4O2;q4`*&r-42oIYCKS#*S zf~OG3K#2gilEhG*Tp3~4G?958l2nP_(BwRwch43)pIYMTmc)|gMXYIlQaRS4H zMT9d5V{vK}9h*rs5}qZ5c&b2HL^y*mM!1=<{8s{6g7(X^% zHHMVI$_Qn>vP0RYTvV8;ohcC*q~NU-03RNvjv1<~0GWvb=eq>n#o3}d#1Ykjx+TXa{vYb`p*c{ zpYYS^&9>FH|7BCzG3AAgF}9(Ict}>nP;+psGZF74*f#r7%owT9O#;Ef@?yEQRS%j+w^b)>@1;Zbdx? zbvdeLi>L{m(s4ij|7s0{GOSYqgQ+DjrE3E*-H&g*_{k{tTo^x$i*A>KdU|)#e>;ZG z|9@}(k1?ntmP~FK`O@3*4dFL0#Jd3Y-TIryH(%c~a>wLzf2>NHU9WmR+#nlGQbWUs zKgKn@eA;8qZT_Ei30FY?KmR|C|8@TKMSwA52anx8*Oav-BktCxcP(AH{oBV*H+*;A y%p7O+e>WG)<~s7n_k^V+=^KNZe(@W1#)YD~_%HXxf61<|l34sujum%WA*aQLr1O!aj6banm!jg0l zMNvSplu^Jzwi$E~QT#*@9T0RBnNbF2+`s`r2S&#c^&vJ%k>%M&of9?i-+ApJR>;28USWA`zV;c*%8kl;k|F4o zzzp9`!pctr2x~KdvimB4(}I}_v{6!^!UoZ1LE$ddNVm2IqtSwxhZv3>;WZ^;R^n1scLmKO4j=z;{{cL(?mA%u4ZjsoY8=-RlzE9rG^)h7huKq!L}&c zS4!q6nxH=DpdPhaF}kp_C0jI{k5l57BL!IGc37oUQaG&4zv}s1kIlkKwgoOtCgoDYdmEE<~g4-B}6N6SOE^6R?sqJ zkpXQp-I}OP^QJ`bo-pQ<6|^M8i^Og|ImEm+-i9O1iP^Ldydhj{-+I$Pu1 zRVlJJauLuX5;ra+D$+LIMN}khyoab&tMb)jhHbth(n+S>mRg76P^usCG~w0M8tEEn z)oFv+9_`_@d0B&9s&9fvb*bu_IbEMpz3U@*QEbs-{#_L2^RY!RU#m&a*A}KnW9~gH z;KSADDKSF@u6(>OF4;HLSM`{<>d#=P6d8k+`O8RYQg9W>wB-4IG=*$!>P?~SKHb+s zxA`$N$*VBEluj7whM9^~9w~zXgB+#vHy=o=VJK(vC;W_q24C; zhL)~fr8}iaD-Yx*Jc&MfBH6jEvvynHrdjhlt$$GP%s_J&H@=Iq3B_jze!z3}xpIIUi-u zQpjj?Pe(nt!#xJ&=u+)?My6vDTDRn+p`pH1I~2;)LP1yNGBjEnD=~4T0-?o?1LJ$q zsNYfcZrbQzmSaY@>kMISQ_vHmnGsi&CPJ**8|KEN@_%4OmrAL_;>FTbD+XwBS$M~M zp(rS8n-~-wGlrsL%2343S(G?wae1}tp5R@>G-I3X}d$__Bpll$IEa|yJo$FBXL=7oX`ts)hD!9 z_ZudZ4B<%J>^NMG_}OuA(V|{^U3cx_aC)R)xp7PD3yyz{+U2;h(Ljav$8c1d(G80N zEhlqSY<30R>Bj5s*Qn@zv2kamLG8R%P=Sl*+sI)^5*gO&Z!;Tt$&N>f`HsYd>aX^!VS8LK~gk*Z&6z*9-o6TalRfE7(G- zEZC9W+UD%k*av9Xl9S3WP?^R}eS}WuK`Q;c2bBRnmdXsiLX@q^m?0Ew+cTUbpO2rX zGL>JUGL2jM>bU|``gsYJ0bWIA2CwT2F>EZzXOkghaAj80PcOhIBfAunI3n|%C1ouu-OI!WWZ zb&}5C(21Xaq>})@tdk7x?I)%rMNCOf+mu9vEyu@uQJKoeQklkQQ<=^eQ|afiRYU@O zBb6EaB`Smb7?n6Br*vZD-{{27rT#QZ2T#>W0x!@>BJZPPGvgZL8YJXr!v5gQJKL%qcX_9 zqY^KKGLTkl$i`C#ij*NckLtw1`{*QrkJU*cpRJQ5zEmep{+Lc&{5Lvr^8-5Z@DFs7 z%sngcXtyO1{r ziJl&eG^8EMdw`{BHIX&hlhN!lFA)VV8-h=!`Qj;*tr|8j8nquI=>ZC;X%7f@1k?=C zO7gvaG5u=J&+0;~-*hKJ1Yeeg9z|N>*QVzWV3W1&`4jq4hkjP@>8vy@7D4!QDr}jG zb#sb*xh+$W{H$yd-lgcxX%(IbKDP>+aWR;bCG}dLf>11#cmrZiQEIND{H**|R9Pmf zc$8-0a48YoP#3mLqoDTGTSeTxlv;QqxGgU9n8hx|+jt+ETc%$hzfHv7Pw~PN!Cg~f zWB{I63Y$eIL`9!o@z(1rZWk3Fpo+p1!Glp@1dl0&5u2!-riBW#SU~GvSTR^6iezFu zrF5&2*;uXUlg|>}>d#*6pucf68cs<|#H!~L@ft`=#JxaU;--ht=degig4&_N+#XZJ z6yOvx7v(K3qv(>)6vq5p1r6|1-6%5cTKj=IMIo`-idW$kklAC*LeLFvG z`jOu~M7z|vZ{CM^WpPEgq(}=6zjZK%wFux_#W40^>S!KvZG1`2B(nckyWQy(>q{wO z1>$(5IEBHo8e2ot#pm zvCKYMl0*~iS(}; z%~jT0x>mDiRM|!*tlxC6?K5L2=ENmyvnl$VhA#7rcWt@PbQgVUHW5dvjOUtIm zIfO-vm8YlO;J^(kE__9JUn-yNpNWfx9w0N2{H)-o9Hr?K%#zbOQ!A?|kzO07&8p~X z8i`)*iHeE%)x=*S!#g;mxLk4h;lsry$zoTgG>ahQHw%s*M+8Tc(mYD%Xi}Q-h7@)* zDb099MeY?hsnU#x0}^Odn(-)vBDoQbN;95(M3u>cr`73VxDXR`K3?$g+M3GzBM`_3DEm~zn;~K_}+KNg@e~cGS*Lt>dBHQd3&Z4O( z%rBw$1H99v$P-wl@d85AMFgADDk5406r{J{=R5JdXr;0)@)S-9?>QWg4#Lp*53EoC z;K3`0GYS!L<@6Vqc+|=I^X)6jno=rx3((mU>nV*mwKn=_)p85!xEIZ4Y@q3D^yah* zkGSIt3yPbbL38UAZA0%2=F|4|uIxGv``~XpfPv9r3csBcUQb)Jx3mP8j!97(@rv4f z6B{pK;pO9oYn}R(6bhFd(a_ZRgMj}=!1T|F^kShF43ZnCh%q(}*J6E&dJhQ$WaE?Y zWh`IC7eaRJLOy5StR@^R;5H6+;Cjg(R}0CH;Ac&=We*-#Q;lUO)El*neLAz5T3X-t z)CV#Z7@zcj_Eq0(^Ng?!$}~^a>Ed5yZgwm1np@!e?kiyh9t;S)&yyo7@Q6q7JBi0k zSHfDbUz9D?hV{!}cW86^mD@$4=R%b}8@#IhuHS~5YoXH~tVSu$Q)OGSNa3~!{5M(7 zq>7lMYK|O)OKMq47AP5IDM7e9vn(YMzHyhOD3GQ-(!VU`gA6=eJ7Gk!-!mH0AqdNu z6HbCqVZ2F~v1CYPy2=IoJ`7)|e$R2ph0dhzbqLj6D5n#?$PkgeQEkR~x;e$~0aPWV zhNZ`=iMlTa-N^Y6HX%aGNo~iZLRFAjfDH=OL-(cXsz0fThOe5`R3po9Qfmy~6jEF4 zdc7Gm+~ly{(by~+@3S5u2g6bt(+dGHrXjG5e3vM6B&lS$;1!W0;eJX}jZFyk07XW8 zLfs6HPe7sM)A1>qhT^U4RWRvZ1_qX4O3uKjGS(A*28b&oHO8AaKXjU zV2cZ~GKV34>%y0>dlQ4raY$v{gF!lr$ zy9o~>c7aRjqb*(Ftqg&C5f%8x=4A?41LKgNPoH98Ffh}LOJN5_GC1ZJ_?%T(_V@&T zj9sj2EtNJab*&fV%}RGTAkDONhs}hK62=e}<=dD%$5PiiP@ao8g0O*bCE*6by9nn> zB4!+6XR`c2`~~8FAUsYMEAgJlu>s0I)IJ~UZg2}+ZxXnj@Xr}BA&xob;|Ts`5`BMO z5f~*LLb#alR>E_Hj}!jREFvw4UiedBAvGpOJk;KdIo}NmJV|)YDR>u0vn3NU&BG8M zA?$1R##kmC#1xs-a3+i+{x&v{3EvU_D`65@PGA=^ft$se$WLB|Xm_l@i!w6kW!)h` zdf4KH4e$tJD?Ev~iETkV$X-JHl)Z_OTl^;wJF(MvITECQT4G*&oPTE-6~+|^>!2$1 z6VAj(_y`ngQfQM|f_t)cwcWV`BzV+NO(ABLU|Npuiv~BD&2Y?6d;J!x8LDxQA+n71 zJ5UAkbTtr_0;BL9B7AdV85XMwfNvKecld-_XsE|YoiWs6OsYaJ{I3KNxj6MFiwZ}I zboCbcEO4?|SAR_s3ct-#WHM$!pP|B}j+N*>i!Uae&ven{PhO!+U3K-aRj5}>b@hr} zs0rP4Rp{Jgw!ql#x|&YS;}*kw@Qxv`Bu%usAv{cku7mrNW`hS_Hq;R7RBH;1 z!q=ei{WJ9n;MocPT47z-YMp6KZ&xSO+pYd~^|{()#qB5liO8qJi>;w{btv3o4MpHC zN~({Z)*Z-Bee}8lc-uTJe4jR>CV$)-E3Gh0@wT~ldc68ih8E}hyX(m~6~#zei&Sq{ z`mf{E!Srp~xthj|HLN4z!{q1UBHpyTpfwKlvsbl;hn8oYU@;RQpzX>y`^)le@bH$W*I zG}If(PE_w2s?^hE@<`ovI$o`x8d!RLmo@HkO@WvEG>2HYupZ>ZVm>kdB| zY9RW`!88Kr0ajQ>p|2bg3^m5rfLnnSLrwEJQ3VY(89Q1DxrUmJ9gS5&XG8XPHsG$I z+)zVtgVF=440XGu0eZp^L)~q0qKd!wdckP&t%cQYhqV_xN@^V}Og?4p4GZzm0k9U1 z1U|R+fvcp}!8Xe`*1jgVz){4k=@G+_( z5XN(hP!p`9VHj*QRGDiB42Mp*?G-tzRMj>dekQfTau%1va8Pg$EN1O(Tn@t_mS{+m ztpO`~4Hb0>6*QE%zQZBcP`{zbVneyS!q?qUcTv4Qh8jX@kfDAgHPTQ6a6Ju&x|kt{ zI~>^4X}ZG4nG01lo(RM!KK1)-Bj6aRb#TUa1xCR!JdcUdXW`Rj3^d>oM<^4jvBrg2 zP7lR8=!K^i0L?NcHEgScI}P={Jr~u|oAHb(oWIzkDDN|za(D-fzwY{uhmGW04zu7a z8xPk=>Cd4F*F{bM6^}sx%V8Uo+9p7Tp&o(?+eFAQ)U&LQ?Pe&M1h-M>Q8v^z2`UWd z7p%@U`MRW2pvv$SvU=MT*k`EAtk!x994DoBX6kkIrow3>vI{h7p;0v~Lz^?|?(}0} z7G`iQD1{|}kQA0-wMn<^`EgXbl=e_~I4qC~a7VgGx*Vv37h!Fh6Y)WIHf&GYQ)Am!%H()bM zaUO-W(t@yPBdpFtOjW-_OLO=NY=*sfGp?1EI287xRP9jE`vUGF*Gfl|u3&ot_er8p z`=p3F!rsOHDL2ErkZF%H(Y3DZSxiyG4oM=BgWg{6O52hqvEw2Xj>FR#)7Zz-5>pwR zl}yr1b`jIfMQ^_8PIgr~5>y}=+@Y21ENoZTXg7`Z#SUTSMeg?yBW^K_qs|k^F9(F> zKY_FCB4+*$aeVSmh_?r1sf)Zk%_?02EtDu#%8P>yG$|hGBD|lJhW5$fHEc8WCqUTA z;LFo$q$t)}$)Xg~3H}q96_Yx_64NXxfOv=0jY4}7R>ShNtVseHJ_Pe$vZQ zFN*Jljm<)iDWtI&|EAPW?wt7!W;hbsj}K#!twt7InQVAh1j}WdeTuBeoG#CWZGqKt zY#}zYMP7={?3M2$_bMZ(#`mTCIK1cynl>7iyzoHNHiNGYE;KErapW^`$wte6aR`js z6+-bw5F_461K3HCqvbw$(br1<@^3U*<^DEYBsl(11MFkntuL77V4^%&fT+N9d0y~U z(^*Lg&14JMU&05GA9TKjxFPUYlZZKGnl9G|KR3;hMf@DOB`F25Uszze&55`R`6Bku z(6^>V>^1LIjO-b7l4T8B#EQeem=?(~tJ;93f8okl#^zdG=9QQ_)x4ICw*(Qvl82aW zDK;--L0lip*uLO?Sj(nc%FJu!G0rNq?@tPhqzh9Dpyv zv!uEH34H>;PV%N71rB=6Te-St?0;67esNCR|FRaSM2rZ3^u)GhDnc zA--w1Cox_ZpKK)M|KzWjZdOl~0-0O5CKdIWGSiP5EdlQMw>1 za8Wk7DwIp|UT2kZ#JK7!>~|>rfV{z0ikd|$DerdvN&_OX=Af5w#2X!;)Up!1rtNU}Or^_bDvFYb4w zeAC~8sp?5+Wqak5kjPfjU2uZUg~yVjY8TTIN13`vx|-Smb8)E-QZ2a4oQilKG^keC zh};U#z#PPFFb{DDG@1f~Mf_EG0Qo-R2VfP( zyiWWeti{M9l=>Y?eGF4u;R8%+#g8Y=h^Hv>48@$Mmr^QQX0`zvu(Ep) zJ2BDs2%Cc#W%CeAnCNde^ycBqXO5Z=hv9faF&+yZvlZj+d;_8%_9JG)VZ=hhQuqXU zPdI}(fGoq{2jsP6pF~&>c6%|zZig}~nxMaZ06dHh)L;W&B+Nm)ny?blmiR1UIPoxf z-$mX%@f@Bo=cpHv4^5Pr;8u1RrYB~zt+2|T&FbL^dp=t3N*us`2fw!uK+D(m8nmoP ztViy3)U!Ppxt!I<(6OAvR&-Q3wh}*#{5Hp7;^&Y*?l?zWmg?aRhb)Q8*~l+AvWX8s z?oJp$ydHV4gnHu3k{I@($ZD>d0t{m5cq;|Kha0xHO?TP= za#94&a|`U{5jdA{EMbF>EXe{#ItA{s2>imb2=S;x;5Rm5Nu~^XwtZyjlb(c^d!Q4O!rzMp)0;z)KctCadlwbB~t32CGBytH2`mM6&f z$xq3<EX{Twnxj}KL87gD7V6`&*mN^bisG=MXpHn#zp2l4wW0T;J zmVZl5Y=nA6#?gaGfbmzSR*D(9&Nf0ljh|p|w5_uLzqWf*ZnV{@C-GbGjdjaJT;lbO zXrJCvRB=lBH;j?5L5uuvcu!WDZ2C7;ni80>&oa5#3gjzHd)Wi{4tYTNlqIWwXJc>> zd-_bh6x`FZC8LB5(qBAtK`nIe;Ck@`ZbX%{tK6(8X?V~@fH>;{CF>%kG?_W#6o%x>mr;{zw zwqI?d0Rjx*6n;oNbq-)mZFTMQU7OYwO)nd;&0F^;SI@q?_w4?{!kl5_2iI5udLaMR hi&;{t6qU8lzx;Z6;)T757ub(0rQxM}UDeWM_CMfUyN&<= diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index c9b211b16d3120a19e8d603bc851328efeb954d4..fcd1d1fea1e2a11a6ecf117d3056ba78e778ba97 100644 GIT binary patch delta 14140 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 AO8@`> diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index bca1fad59bc9..f703b2f2a180 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -43,8 +43,7 @@ #include "resource.h" #include -#include "..\..\..\ProcessHacker\include\phappres.h" -#include "..\..\..\ProcessHacker\include\phapprev.h" +#include "..\..\ProcessHacker\include\phappres.h" // Win32 PropertySheet Control IDs #define IDD_PROPSHEET_ID 1006 // ID of the propsheet dialog template in comctl32.dll diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 4a1db839071e..55f303c4af91 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -223,42 +223,6 @@ BEGIN END -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_PESYMBOLS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PELOADCONFIG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PEIMPORTS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PEEXPORTS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PECFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_LIBEXPORTS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - ///////////////////////////////////////////////////////////////////////////// // // Bitmap @@ -278,6 +242,45 @@ 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 + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 1ce6cc5d5354..7457b997abb7 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -85,7 +85,7 @@ Disabled ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -110,7 +110,7 @@ Disabled ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) EnableFastChecks MultiThreadedDebug Level3 @@ -136,7 +136,7 @@ MaxSpeed true ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) MultiThreaded true Level3 @@ -167,7 +167,7 @@ MaxSpeed true ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) MultiThreaded true Level3 @@ -217,8 +217,12 @@ - - + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index 95f381deb897..d81436dc0011 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -82,9 +82,6 @@ Resource Files - - Resource Files - diff --git a/tools/peview/version.rc b/tools/peview/version.rc deleted file mode 100644 index 1e412bb16d5e..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 From 2ecc25aa9dfe0eb2a6792df014028b1e8c47aaf5 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 10:21:16 +1000 Subject: [PATCH 115/839] BuildTools: Update binaries --- build/plugins.s | Bin 0 -> 112 bytes .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 build/plugins.s diff --git a/build/plugins.s b/build/plugins.s new file mode 100644 index 0000000000000000000000000000000000000000..cf600c72ece7f007e4c7fea3f91d26a642a64f07 GIT binary patch literal 112 zcmV-$0FVE19B3RV%BJN2*Xa7QP$(n4dkQvD{M6^>5V73a3XNE>>QL;PM18Z`(*d1_ zq0rOx<9QyGEJ+6XGfmWn_M>jSfOpSkV}m=Z%76D7^z7aM*)fO5h?2mxZle=VJVP?q S%2OMjKC?GHLq`A(iP=RzO*!5G literal 0 HcmV?d00001 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 242914120060012e249192a8733fa077e6dd59d1..7f7c097739684e258963a386f59e66dad60e2d35 100644 GIT binary patch delta 91 zcmZp8!P)SFb3zBx)E67O?9K_Kc`p|zG%xp9;9A-v_IZqiFf`VOw6JB0wpT%&?WtJ?TD6f3?wdM^^w{LjL7|;g*mE|L~ delta 91 zcmZp8!P)SFb3zBxg@+rv?9K^n_gg-F!LNFP2E1xcSDpHDWp!9?C>A009#a v%P=x9%wc9^nDdl@At=}-cv&X9+CpVszd37~I+wlgzummy>Glmz83XzNI?*Qe diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index fcd1d1fea1e2a11a6ecf117d3056ba78e778ba97..bc235b89c2dcbb1092f5428f2f5908a299532850 100644 GIT binary patch delta 2264 zcmaKuT}TvB6vzLw&bn%DVX_*sX7p)>8I=!NltL7}i1bp?Lq7SUzVsj_(il=v_E0Rc zB9in{521(~TC@fRp?wfyABJTFT2x3_6c$v|IlFU5@7TTPW#)f>_kZp=ckT?+5;t4o zW}lg{s{Ld5m;s>r)A;#LOz!W@Umgk;w7ralGiKNMCpuH>>teyJ%lb`&X2rtqK7_21 za+;qAHeL+^2OJH^Y=+pRg56sV+*YuL73!REi(nhCoCtZrgIT~8fjq^$BKAz-yO|C6 ze8MQ_>o{%;KS%eBh24@Dy6G8W{R-F}D3?4I-Gp{C*1v!>F3ql%=Z=|H7|wG!K_V8g`f zXa|xZ?@Vzf@Kqp;1k1%e7a05gXQhuGO?#SAGUvQ0pP!&00p zUzqg~t8vD>ET<6IpkTW#g4-0ZmKbenBHW`-iWfXdEG>!PWlb928IRBHpOaqD7%zK_ z&f1A$IyAm+jqgSwEM2EnCzc@uQk4Djqfx2#QuAq z;Wv%(mu5j@^TG>5FR~k4=ssfO&gm4aif1s)Dh5umpb(JnnO!8do&|-M^%AR1q6CXJ zrx;CiYAZ{C=E!hkb~)lIoGvElJ&#)O|@kl+>7{CM5M% zQtu@-OKOzsG`WXpm*2>v&PuieFQV7{Mu>#D1E^03`dmck{KkNIqU2!L=+1@jH+_`c zU^Yceyx?9oLoCCY?PXsyz8_mO@Heq~Cuxwb(W-!vNB;*RxTwL#fN{xG{;v#%GmV+7 Ef63z5d;kCd delta 2440 zcmaKuT}V_x6vzLw&bphqhJ_lkeUN2pm{BQYk@!;dp!o4n(L+6ilzJh0kP_)yQeXuY zBI|=BzSu)4=C)d-MJ<6@g!Dm?8HLtENLeHneQf8hckF|61UIw7!uqVAe%~#xd?NnpOmkT37f80cxyXRNz!9zjc`h`9G`jK#T(EsjZ zl3uCN{Agnc@ykA7hZumJjT3tH^v(zY>d{RoXuv>=?5`c z5g*Zx`-P@ElFD)e`vi2b)5Q8EtS}ZxmIS(+%5X-)^ieOcN3v+{0-#vJ8i;L|uzSR+ zsRA?2UYYE8;HyBW^w}Os@cW;|#{FvcvMl{>wU}na{x==iW)8SuWg(uw7N{{A8|H;!t%?{GHQG3)3d}AT!rD<{UJ0vJcqbj6<^6=UMum9MAzFiJ zQFv_%?@9)AX@*@2u~)I-fWmt)$20RcO8QtKKA$7bJJfnGA|497J3ch(tA;6d$?^1)?sD3L+rFd2fLwQw-xNZg6R(x za9F`!D%e{E`(S68wz5qS95l;qFKM|JXmztxPq~(}5qJ@3ce5n=;F|y~`r)D(lWx|l zw^(jc9v|F1`$s7rd6vV? Date: Thu, 18 May 2017 10:34:19 +1000 Subject: [PATCH 116/839] BuildTools: Fix resource definitions, Bump version --- ProcessHacker/ProcessHacker.vcxproj | 5 +---- ProcessHacker/include/phappres.h | 2 +- tools/CustomBuildTool/Source Files/Build.cs | 4 ++-- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes tools/peview/peview.vcxproj | 5 +---- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index ddbc487bed92..1869be83ebda 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -378,10 +378,7 @@ - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) diff --git a/ProcessHacker/include/phappres.h b/ProcessHacker/include/phappres.h index c0267bfd3ea9..566ab7e62649 100644 --- a/ProcessHacker/include/phappres.h +++ b/ProcessHacker/include/phappres.h @@ -6,7 +6,7 @@ #endif #ifndef PHAPP_VERSION_MINOR -#define PHAPP_VERSION_MINOR 0 +#define PHAPP_VERSION_MINOR 1 #endif #ifndef PHAPP_VERSION_BUILD diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index d79db942fab8..97fd86fb868d 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -248,8 +248,8 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (string.IsNullOrEmpty(BuildCount)) BuildCount = "0"; - BuildVersion = "3.0." + BuildRevision; - BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; + BuildVersion = "3.1." + BuildRevision; + BuildLongVersion = "3.1." + BuildCount + "." + BuildRevision; if (ShowBuildInfo) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 7f7c097739684e258963a386f59e66dad60e2d35..ed3777f571bd6771a2d2729f905bd1c11732ad64 100644 GIT binary patch delta 98 zcmZp8!P)SFb3zAG>g$bN^E(&~H*0mya}^N($DO}-ftdShj<6FNW(OW^zHx4im`=)T znMej8U;<(pMh1pC%!~|ko-!~51-k?rES0uBZ9c_eX1KJ-#iBPs%^RL>-|&<%pbr3X CwI#s- delta 98 zcmZp8!P)SFb3zBx)E67O=65g}Y}V?W=PHoqyC)7JY`@A3U&!jczLOP7Q-!h)6`b!!GPL=25Iq60^*?uk$MoZo)l56QiT=@fkcXz zmPq5)MnSxY1+fj0ZKz^;uSt=pXF;%fF%(ggcu0(ICbPPol6RST@Au6&^B&1YGjcQ| zr)0JL(l_5Z0swtaJC5cK&juf!?Qp%jRT$EJ>b`bYa&jXUE$;SA1^+x7EM_`ycU}DX zBD6rBSbJ1uzJF!s4c&g=Jdr1q*BA7W-uaa3&!HJy8S&?I->0wc_-WNf?H}6#PR2v6 z+TM~gKB#IZ*uv2GjyiHtT?UhBy5XVCl)O~GRB{d&H2^Aj-U=W||7*sMvPCz*Ba_R$ zvKN(_uk6J`Mo@ma6W|p-tD7cWy8u=iIBMatw;3R?2OxCcPCUvu?uO`2%QJJmP`)eE zOTrcLlFwLn<0Y)Y-jhq+(OFb8vV?V$I z*NSWkSx*C&M0U+!7Q8SKwvg$5?5!TO!E7(UTb|>nH?aqO09a#Tg<35Ul!;fubr{!y zT8$(2!&nrwX0{}-M2h>YZS{F0Xw5!(fhD??`g@MxD!k{5pf&pxkgf6p>uZMlENu0W z4+320S{u&5>~j-Y(!}PP$XujW8#@GWhL^V)dRz&NW>=Bn--JaA=6uW8M`XshMYfJi zZE$YGd@drG7R^v#6=X(lc7a_D0C@O?n^`OyMdsjaJH-AqA%O(Tv(02wZuo&)_O+08 F$6vX{;Q;^u delta 1350 zcmZ{kO=uHQ5Xa|jH=j0@7^5ZRL!^faY6Z4Kat z$a&e_KC&1*L;&E(x6=76yiaCBZGKNA^F|B0H@C07u=mVQ*HBXX+jDa`aOY#syKD0! z_YygB%h};}zRlYEdS8`@(qHvm-XGH9TRVyT{ppjO*4uSEuS6;~8xYjif}g(n!!vR0 z!i=`N;Q{F8KEO;|*{~Fl@YuN&h&5&otZvK{MO3ALEJH8Mu>4Y8CAUK5t`9(xmNUhT z7LWS@uA5Y@RM;rNN}#)Olw%=)N1_sk7FSvu2?IR-Zv$OdQk6hV*P|N&61>J}hdu#a zA3%1X2~&zVkgjXx%0-#JQL1WvqtDnFm*Cbo*dJuCjdoaGtg`jWD^$+e@I{4)Wxa$Q zV4YbdUSleUYt~t!E`WVaRL&yXWnxOcuJVE%+F~s%;i3=wsg4HUNh*J?T^T z$YhN&H|bW7@(bhRf-~>F@YIJQd)}P=OmLQ66rS2?5W%qyce%O)AaB>dWH z&PD<;z6c(4czHWO9}nS2FpRQ(WD`6>*{Ge2;`5){0dSCqSMie`v}6{U_`$VW*au_| z9<#CqWbP*Hhq(^>8%7)cHbaBdhp8eHyLl|^cobkgk6Bq7nU}LRQ=J47OkH)5lXBC4 L$Ejc|S@8Y?`w!wL diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 7457b997abb7..dd34e986d496 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -218,10 +218,7 @@ - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) From 4ccf1f85717aa1c77978d9a819d6f60ca1713c1f Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 10:55:25 +1000 Subject: [PATCH 117/839] Update solution resource includes --- ProcessHacker.sln | 19 ------------------- ProcessHacker/ProcessHacker.vcxproj | 1 + tools/peview/peview.vcxproj | 1 + 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/ProcessHacker.sln b/ProcessHacker.sln index c2fa6bcc9b31..4e87a04af677 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26430.6 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} @@ -12,12 +10,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProcessHacker", "ProcessHac 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("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomSignTool", "tools\CustomSignTool\CustomSignTool.vcxproj", "{E8CD0A41-1537-4EA6-98AC-E80CD59C478E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -42,10 +36,6 @@ Global {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 @@ -54,17 +44,8 @@ Global {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 - {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} - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} = {2758DC86-368B-430C-9D29-F1EF20032A71} - EndGlobalSection EndGlobal diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 1869be83ebda..1c52fe1b7730 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -379,6 +379,7 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index dd34e986d496..616289ae31be 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -219,6 +219,7 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) From 793f1e5128d99834a2c0c6195e9ba85145973191 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 10:59:55 +1000 Subject: [PATCH 118/839] Update appveryor config --- appveyor.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1eb83fda0bb2..d8da52ff95a5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,5 @@ -# Build image. -image: Visual Studio 2017 - # Version format. -version: "3.0.{build}" +version: "3.1.{build}" # Only build the master branch. branches: @@ -15,8 +12,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_depth: 1 +shallow_clone: true clone_folder: C:\projects\processhacker # Immediately finish build if something fails. @@ -28,13 +27,4 @@ test: off # Run custom build script. build_script: -- cmd: tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -appveyor - -# 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 \ No newline at end of file From 6f42ddcd38f919c3a5ade3d1a6c0d51e9cb0d44b Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 11:23:26 +1000 Subject: [PATCH 119/839] Fix build --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d8da52ff95a5..8ff4ed33c3c6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,8 +14,6 @@ pull_requests: skip_tags: true # Set the clone directory on the buildbot. -clone_depth: 1 -shallow_clone: true clone_folder: C:\projects\processhacker # Immediately finish build if something fails. From ab295b20b10854310d643061edc9187b3e0c5c88 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 11:33:28 +1000 Subject: [PATCH 120/839] Fix appx builds --- .../CustomBuildTool/Properties/Resources.resx | 2 +- tools/CustomBuildTool/Source Files/Build.cs | 21 ++++++++---------- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tools/CustomBuildTool/Properties/Resources.resx b/tools/CustomBuildTool/Properties/Resources.resx index 083dd1a65c62..646f2393cb4d 100644 --- a/tools/CustomBuildTool/Properties/Resources.resx +++ b/tools/CustomBuildTool/Properties/Resources.resx @@ -139,7 +139,7 @@ <Resource Language="en-US" /> </Resources> <Dependencies> - <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.15063.0" /> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.10240.0" MaxVersionTested="10.0.15063.0" /> </Dependencies> <Capabilities> <rescap:Capability Name="runFullTrust"/> diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 97fd86fb868d..e28f5d2e6679 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -254,22 +254,19 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (ShowBuildInfo) { Program.PrintColorMessage("Version: ", ConsoleColor.Cyan, false); - Program.PrintColorMessage(BuildLongVersion + Environment.NewLine, ConsoleColor.White); + Program.PrintColorMessage(BuildVersion + Environment.NewLine, ConsoleColor.White); - if (ShowLogInfo) + if (!BuildNightly && ShowLogInfo) { - if (BuildNightly) - BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); - else - { - 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:\"[%cd] %an %s\" --abbrev-commit"); - 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(yellow)%h%Creset)\" --abbrev-commit"); - } + 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(yellow)%h%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); } } } diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index ed3777f571bd6771a2d2729f905bd1c11732ad64..08ce7bfcb34d89826a121b6c5048bbcddc46aad4 100644 GIT binary patch delta 7410 zcmbuEdwi7Dna9t($=oNIc{BHWE+m;G6LP;_NVrbIL@tp_3W!K5wptP&p+H$OSSrfJ zVmyE?LP0((E20%T7Hvy~D%w`Xt=l3lTF^x;c6D7AS4)c0=bZDtlL`CzZ2xHfnDhHS z&vP#C^PY3w^S)Vs$+-TK@u3H#vsZ^VhoteD!%xgDecE$BtYd4qqI*`EY-eGzpEK4vSo@2F$|E1mJBW=WZaJkVb*no=(dj#Vl!Z^(&S8O za)#!3a)-84QyWb{seufJ&8|=+fnh^BLNX)c2{A?T@h5C`D|QL(uCFwii=#a>PhTdr z8|Y2?JUXZ^msT0+UcJ*hY$SvG3w=2xQjImY&cVL-7y99_k)GBEI)8;;IDo#mkLLL3WWyxgY34N22Mq!JG&9N#W_rXB)V;_wGy7at z$gpsS(mZQmk_8*^(m`2A^Nnt4j)k@u!wHIUks=&!xf!@FMqLESG&9%2e37*p&Qb@x z6cy}E;YEgJ*{XLfDtJ-FvTuUjI@%Z4#bhDttRcn))3O+DGaK)bBv_Y=;_G-;~HzRXO9lgD;{?#6!>r;if@9C4re(MY^tfIHRw(cKbsbA_mB16jm+ysO~7?2b_q|V zS-Iv`?r%o_Kuan}!5}dWu@S>ZtR^lH9pS>wYdU*3MIG2^=&~`htD-1=&NZ~uDO-_M~BZH0;l6Z#qiBxWsqjQI{fF+ zq!*i;MY#p8QW1UW|3*Jb^Q?{-k)pZqQZ7z|l00mXFUV!6;IxWmA>lXYkMZ}Yc;0J) zZTa-DwSK~HnJvgN&eTvVDrI6_PI;Ib^_W2}Q0vGv*Or@@fSj5Vu2OMvQ5&ooYd7$V zM*Gua?F25OfzLaAWCPoe|H{}ujtuGqPe2J@#~%ad^S&(Df@F*o;&8)sS%IIPv$<_g zqb)VZ;ZNhJMsC!!&}Nyh0wMl*#7v4>Pb;_GF9fV3*Ar23A*8V>Ni) zNV^eZQu}CQU^jYF7dm81Tg<;JqFQ^q#Z;_z@x5X?!_Ke&F1wd)2KU?RZ70#4st=zP z)6;ez^Bv78pcaQy8YxZ|I5ISK%@N#YCSu=3I`}r~p)A%Fe;!XltLl1SOc-6@tYeKW zuz2ZVXKCX}^rkxFQ(IkcH|{*_j;p?F5t`*1m)Bl`mDSBjT^SFcQ6%vB5<1&;n?|BP zb(Lo6N^!KyF_DF^u#|r6@<>?d+;^GnW!NsC<952vohP-G(S7cSa}Ame1KoHbM!hb= zmMLs0qknXdlb$N08J==hRC}&U7s_b4cM2<3cpDq!D6UvpzYB^{L|7Uk6|8CnB6KQF zaZ}k&RfLZL11yTti{6R~7UO>qPgR;i7YrlpFLV#-F{k?rJ#d1vcSy_G2QNiwr_Zik zgl3uEic@@ruY3GyBq=hRb;#m*|9D)=;KwC+4^3f{N*_h($3AcT3L3GWjIC}wi~0C( zq!t?}*Tal*GFYaC#aOTr+{sJt5Y9Fnkq=zsncY8R;2eOd;HEsp#UFBOCUe%ON{0>*bD;|^qYYhx1XuNyHQe!W<0;>!qvbH-%<^^JK5ub z+m5O>$-UU!@dTbwBfGJR#Q#(tg?Hf1a9kFM+?ApdhacFX;tleeT$vW7KUjG0dUrpr+#O@W5=mghtpQio6cp&{(E}a1sY@njOGhr z8-|JGA&h1WVKik3X*pY6l)&4PmNcWwZfe}il4g`myXkYYNGLbt zM$)olkuB-ju>k8))c$Kd-ID8z9Bfe5yvP~MYQ#=0H+Fh&BR!oPm0qdEqLdcq+1Pg6 zkk_6#jp3=S7&4laRbzscsx-p~OtRx2p4qtO$D45TsFiDO?Mt(A&8#}0%*-}Fi+JRh zml)uD3mG)~2j*iyxgY+ggh6O2q*kf2p7C6gc~jh;ejhcctPLEd-Z`+2tQN^5r7^c zg77US*1K3r3f1%`xYG@kmkAFX=E4gncnfICxb|DI& zScq}>fSO)Zl9+|KE`)iNU?r1&Cf#a^A7jDm0?al#6_dOLH@wJ851iqp7ygr%KDf?H zKgd&9-vKD#Wf1Ck8G{I#W+M!qo2Q&%cgdQPWuv7>)tVGd`-Fsk@FnM8*5I#r>;fG%f5rDUa2*RgK ztiux{rc65xQx@j3HhH|bSmWLr!+mHF_n~NkUe>N%ggzH+jnR=v*u5|1%N_OYizO>d zR!h>67=5Cwt>hUN^B~qYz|T?2rnMeyf*w6Qj2139OWSBRWN+e*r_`H?d@J z{J&|W*JJc@#r*6M*3!?|NQz-3M(0(QHdwk;lg$;u=dP|?QyRhVbh@I_p%``pkyOBR zw;BNT^tsBZQg_$GRc>i+7xh-<)~Y(+id6X0RC~bQU|e*0b%iw4MfX;R=(#Gh z`(P?^!Dz$JbWu%>lgiaW7Ft=I=R2u}HQk4g=F!MMu*eYIRP7LrPIterMn?Na!$w$G zi3oAh&Ki4GCVs3DNf-~AbV-d>)NE* z-N~7CyEV-EU-iB1$JtjOyy@&~3x5FC>!5SG@`O@e*`t!9R1a@5D}Qg}aTaEwi3Xqd z1_z!8He>3U_a_ZWO-}OmMvEkEtf7|H+J$R%a^|kIqe^){H>92LdbuI(tTK@s(oQQ? z+>mx&$>oLsUENwpOIo^=zUbpEw+Y!?J$j&wc8K6O=8r#CD>+xDSTxp*jSWh%IQ?^L zlk{+$7PjR}KabI_wnNoVVyJ)MIGcdeb?^cwc2teFNo)IYvgvdKa}nKr1DCOI!IN=1 zuDwrsIZhMpJwDPyNWq+5@=x3FV=&Hzf@yQQCS$pETk^wppCK_H-|h+7Nur@WS}i%H z(6fG>QA_Yunfq?`n~)rM$Y+sLkaQ%5Uql#vBd9a#$zMtYJ?>_77oJdA6#5Rf zsVB8%!uK7|h(S+|C-4gs3w;Tl2J#xd^)hF*Bo?_)^B&EVJ#=8c4hzKY%k_!n=P6KkzZCblEVp5x*=kYB#uz zCNgt^pjWXOGuhoC=sc$tos5P(k9hZJ&7_T`mu&P5dv(N2Vq@sY6z02^4EuxFift-k z{WBbIw2<{vh4Y$!qR~ozH;vIove~M`8+V{v_%vje(MCSzw3Ccjdo?nN<6AWw=wGb! zj5*|hLibtkAa>Fi7rvNrk}GxWR5};b8JYrC{$+eCw1g@g&OTPqydE% zps${+N+@^-I_t@Lg{GjhfjpqlO!PI7VTES9`$;4Dp+dL1ZAkkST8IN}B2OrE2M)A} zBo&%y>nF|R7YcRTY)Gei_*~d%xYN*2TF7gPbCtn{lzvZJ$rnIo81Pk_!)#B;G?)CZLRZY=kgh3o-CT;4xj;l(a}Sbv zN-GJmX6KOrcWxld$OX+jvX+xr-SfxHg!$xtCGr5NGR-G@6xvMcO?~8_6*{D8HGPd7 zQRo#-%(Q?!r_cwQn@tPVHaqxLEFvcrXPIV+X%T5!$VR$>e6E>gyoJo-BnGost;p*w zCX15JndMDM?dBII+jhP4czMS+R(9_ttC!gCyR1#V_4;yqW@UA4WwQG}nlqwjuLgJ? zC2Tu@CzzA?55|-46EeGZa_>{;9;*22sHyAwRXhLusQ1FvX}g>&k@M4kivYiJMPi*?0+HN^L@eO%p=-5JKgpm{N>sKTbkyitV0r@0&&XxcR60 ze0cZwJ?Gr*y!-CE^BB2m7`bZLvt7CSqtTsy#h5z!NF#(xS+W!qOWcJ&#?$hw&^?a==)X3=A< z@c2@td79?8f;%J?b4IRjLX0lKzHneLQj^;qV3%Z5b zjpnk1*#iS)4wX~Cv+xpTo{|){0*9B%m=(T*Uf7G5Pzbw(!-Qw!c#|;G6!TW0f+guO zo^Tmqz{H=mx_LxDU%Q9AZ}VU1-G*ggG$SlD^SyeXb{lDOcx@-8S#6LS(57JnE}mlW zMV)DsE;TL6E(d1ZEv}&o9C&0S^k4v`##n5UdIstO*py@zTAB}{89pGNm|8(P;yS!Y zJVqLW+C?AC20Wn z$MHq-eiFwr8{&4n$lz0i!R#2y`xU#(F!`RrI*fyT*{!kYi^K)?7(R#!&cre7Kztl8 zvZq^w>zpc1c(V+&0FIfu%Fdz@%&u5q9V*xu$L|ro8N&&eiK_jB*~M3yeHFr9iA%w# z4OP+V@M_^URmgO+p}}f$h`QQP4eEI1n!*b7#~0jvetZCo+Ch9cwG)2ofDP#eYq3Ts zmq)X+d0v`d0XKgm+Zla^DyFL|)e(j($e}#y+5(Dal2569pSA*8jIoIN$q}#*Ip#Y* z#aH1~eGwxF9mZawFqC8#n#?kw4%OgnLg9$xu#I+?7pNlbE|{xg3@Wxmua6CB<(?Fq zl07V=A3|4hblo3mE(KBlSJVJ{1Y*-@#fanty?`9^U7zAx5>S0OMWMx99#66gEv&*{ zhuf(-Md6t;#WPfsrfAZ|ruc9W@1>lgS-oqqDgFdqQ=-3?^A*udi3DLgdf<9aJysEW zN;nsJ4_*Wo`DMVFm;r>}lBfD7TxRIZ<2<8k&Mva~=q8aGuBQpfh5Az5Ln`4h!MV8S z9M@ZdQDu5kj_qMN*@y+|C)@QR322AOc3hKa5KC7V+KTCdW5g49(MneGkwbXV3!ZSN z!4f0`n(+O_7rSlz7Z$$-&=!A+;K%v=KP@%>FVHxgLMv{?G@L5E@j?xFN!s%JtiH&1 z3zF;p6OycymJ+KpMvr6cJyD}M%`aN3mwbtVbkmFx6OeX<3K1Y7@t17Y;tH*nSSEw`4f{sZc?^)x#Cd+VBS&EozTFYZu8-gX9BjpKgH9W2!xEN6_*;(Y%20%VK?Krj}?zRcaN_eODj>z5;yfxSH62c>uHc$q_P*SQIg18efX}iCazvFdA zUq=_5!;9ED&3d?mTHGjUfZND=@zM{{f&IQNyc1~#s4t5>2nJ|R(ub4u?D9m+@wgTn z1hiDp*3!-UeU8Qs)Sf{+f*XsYRxR`jRtx8phL@W7CkucqES9%<(2%6V6J(0sxTL2xfgLzR{hjOSgK?5?ysJb2w@2FwqVAJq}iF_kGfcu zV^~>kKVpBhCj0KGndAdoEp~67+I@2^e>oUYc2{B%=4m+=+DHRAjnO?Ap4hHWB8eq# zgqk{4n&5eobXLUUj?m&@^FfkTXkqovPQ(gLRxR|9nYJyne7P4F>fu>Dltev)eY6ri z@G_G8xe$oISxS?-;0sYYA+t%gl`Bd&R7HjGK&vRdaF-~3FeFMpY!PL^0Ao@Z;h+>I zcwGuJ{91}MxGY6F+>#;#(&tit0Sn|xVTBqgGGVb0(LfgTNs|pWN|6mCQrO{fDI9P> z3MU+v!UeyQ!VMRs@W3Zhc)>DH^y-5W5+im>Nq#K7^Mum@J)(>{VL%8MY!Rg!o)D!6 z4vNwXhehdww?*lPE26}d+?2uy`uU;2j7&!54(gY z4FuqTGzH-$DRSVX6uEF-iafX`#dNsM%L@vlwgqfxGyXhHQJ{vThomEx;(jz8*z6@+ zVkvGSBQEQ0qIAMuQM%x#qIAOvQF`E!Ungc$Og+I(W@N_q;NnDidO92 z3GLG4f;CdOVUrXd*eQh-mr%-PW2>Ivn=Vr{%bllN`yBD54jgdT^ zHT9uY=(7VELp^95{03bBF9kLG8NSM71gwwO3MOi;Zy9$Mep6BIYvJE3YDjy4Vs>Ly zJ#22_mx|oV11v`J-k296ZQs)w=h@qgN#Qdw`FW+xcguKC0j>%W-Xu zMb$on*+~RGiGk&^)i(Rz$ATiu6A?!-B9Im^g}$xM1zWV0hw^?I51t96LnG*qXu^ph z|2tn@9@P9P(L(|MeR-3T+Qxkq7ZpPr|E!`x$!;63seDR9*7H?e(P$gCgiC-v3yJq_ zl@?Ok)pwbC9*rorunxXYR`C;6#F>*UVOuz~4M$_vM?(6+ov5Zpw`;w1HUBZxbNSY@MNyns8Vfa?!3>g%Lclnz7 zd~TiHB6^uUCOfOB=@s5YmysqJj&qf7?h?U&h_h_&b8K!%F?H~>^|i{f4xZMKtKd>D zZ8&5Z?4XzU5n8BiT6m;`Uuy^|gB{%5xLn!M!N1!U;X>432@7V$rwn#wMz^omDoNM+r4CLv$9$A}cc9|1%QkhE+~NYXt% ztwF~ucnl)n^A4i#sPu%O|5oWiY*WYDi)7@3&RcpNI~T=IUR3cdbn4k<{1{JkK7(ik zzm5~xuo?8J6clu+MEdlCeukgjDe|_RB5h@o9y1V~!Vm4_J8UA_fFG`jiY!m3>REpk zez+oBE4qKaMpB90f}uox#zQUMY0>$(R#@bXmkzzsWR_R4sKPzV|RXS#~Al13IhNUX>Q0vzqN!X({@u*&&rW9sR6^ z{amFr4hz!jDy_tU*0K{SZNP!nvePQfv-Gn%_FI)&Ef%Cp9bzqP)BlyepUq~MRp$eG z3sUkut!Ez#-zeLiX*ATc$P&uh7`xwg(a^xmokXMTP49b#Mm8#Fj6J8nZkWSfRefIu z2H1Sogg<^M^6kt&8=BenF0mHI*vEk{3=3GWo1A0pL!`y*Rh9Y-cd|BCfj@4^S7|-S z+Sw67qpV4nYHVj?_`alD_`ZIZrky>b(n&MXL6uUB{aEj(Dn+bBhmmmp+w^pQ+u1SI zc~FF&RLN>5-x-xQiHhe{S}bTnrCWlos5BqfQ#-q%QoGrR1N~GIJ|!7QmwIv3QB20~ zJ=w+%*56077-R1_ZnCAUAAi;m<>IRCWVcmHLF!g-%vSNR^{{hyQ6$%QI{n5TmPY@l zhV%#1bR?TfUz);5UX?O}2U)M$N>o0xy{tkwx3aIZcQw6?3zAQF-=vk$$9|wjKF3Op zee7kGcCsqta&}auLz;TyUF?)fztSu=u3*1b=|fGoab>K{CNYau?1Jhn(yTSEVt1~j zS!`uDHFp}m#_ka$2eUd>QPf+_{$~91JBn+Sk$>co9qY1Iuza?j^|Mthh-xhxV0HXI zb_8@m7DC>@8+I0qhj*N4wlc>Trop58?Tdb*?>QB!I{v} z|HvzHuHZPlc<6Zw|6BH{4;kz1YVIoek*DEw^%G6s@AcSz1J^sp&t2AQEbpoRM@u8W lh5E^#=PFLk_3_jz#hR_@S6@lLs=2j8F;~7~ZB}k-{ueHzhaLa` diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 00c761c02ccc38c87155b28f1e5a63649c6f227b..6dc929151821cb21836568e5c81657a06f51ad72 100644 GIT binary patch delta 8845 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 Date: Thu, 18 May 2017 11:57:38 +1000 Subject: [PATCH 121/839] Fix Appveyor build image --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 8ff4ed33c3c6..c97fce5fff29 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,6 @@ +# Build image. +image: Visual Studio 2017 + # Version format. version: "3.1.{build}" From fb64f5c9061bbb67e096e2189837e96833141908 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 12:08:33 +1000 Subject: [PATCH 122/839] BuildTools: Update binary --- tools/CustomBuildTool/Source Files/Build.cs | 23 +++--------------- tools/CustomBuildTool/Source Files/Program.cs | 1 + .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160256 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index e28f5d2e6679..bf8d9f3ab76f 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -874,10 +874,6 @@ public static bool BuildUpdateSignature() public static void WebServiceUpdateConfig() { - string buildPostString; - string buildPosturl; - string buildPostApiKey; - if (string.IsNullOrEmpty(BuildSetupHash)) return; if (string.IsNullOrEmpty(BuildBinHash)) @@ -887,7 +883,7 @@ public static void WebServiceUpdateConfig() if (string.IsNullOrEmpty(BuildMessage)) return; - buildPostString = Json.Serialize(new BuildUpdateRequest + string buildPostString = Json.Serialize(new BuildUpdateRequest { Updated = TimeStart.ToString("o"), Version = BuildVersion, @@ -904,8 +900,8 @@ public static void WebServiceUpdateConfig() if (string.IsNullOrEmpty(buildPostString)) return; - buildPosturl = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%").Replace("%APPVEYOR_BUILD_API%", string.Empty); - buildPostApiKey = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_KEY%").Replace("%APPVEYOR_BUILD_KEY%", string.Empty); + string buildPosturl = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%").Replace("%APPVEYOR_BUILD_API%", string.Empty); + string buildPostApiKey = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_KEY%").Replace("%APPVEYOR_BUILD_KEY%", string.Empty); if (string.IsNullOrEmpty(buildPosturl)) return; @@ -1006,19 +1002,6 @@ public static bool AppveyorUploadBuildFiles() } } - if (File.Exists(releaseFileArray[0])) - { - try - { - Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); - } - catch (Exception ex) - { - Program.PrintColorMessage("[WebServicePushArtifact] " + ex, ConsoleColor.Red); - return false; - } - } - return true; } diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 65f25f628144..09f0d6b1ff10 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -176,6 +176,7 @@ public static void Main(string[] args) if (!Build.CopyKProcessHacker(false)) return; + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) return; diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 08ce7bfcb34d89826a121b6c5048bbcddc46aad4..ed095f0207c5e65154ea749ffa73790d17ca4d0d 100644 GIT binary patch delta 8251 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ⅅljuiXUdF23ldH#yfZGH_Eyu9B0k8H1Bb} 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 W@t60+UuM^rN;&y^4kWRk*#7`7z<@~r diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 6dc929151821cb21836568e5c81657a06f51ad72..d828175e2ea5c88db29efd0da8cb60fbd4b673d2 100644 GIT binary patch delta 8249 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 delta 7988 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 Date: Thu, 18 May 2017 12:17:07 +1000 Subject: [PATCH 123/839] Add build debugging --- .../bin/Release/CustomBuildTool.exe | Bin 160256 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index ed095f0207c5e65154ea749ffa73790d17ca4d0d..403baffd8b299f948b8c40af4d1bc1b6ac289c8e 100644 GIT binary patch delta 5517 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 EUyg6le*gdg delta 5127 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 From cb7db95f93d5578f5bb61181d0b261d397a94b56 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 12:26:07 +1000 Subject: [PATCH 124/839] Fix appveyor build --- tools/CustomBuildTool/Source Files/Build.cs | 5 ++--- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index bf8d9f3ab76f..1f929a3ec506 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -880,9 +880,8 @@ public static void WebServiceUpdateConfig() return; if (string.IsNullOrEmpty(BuildSetupSig)) return; - if (string.IsNullOrEmpty(BuildMessage)) - return; + string appveyorMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); string buildPostString = Json.Serialize(new BuildUpdateRequest { Updated = TimeStart.ToString("o"), @@ -894,7 +893,7 @@ public static void WebServiceUpdateConfig() HashSetup = BuildSetupHash, HashBin = BuildBinHash, sig = BuildSetupSig, - Message = BuildMessage + Message = appveyorMessage }); if (string.IsNullOrEmpty(buildPostString)) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 403baffd8b299f948b8c40af4d1bc1b6ac289c8e..a922dfbf78d445fbde7a9cd7675ce0c457899a42 100644 GIT binary patch delta 8946 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 delta 9334 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 delta 6015 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 jch3w^E>+mDE Date: Thu, 18 May 2017 13:27:16 +1000 Subject: [PATCH 125/839] BuildTool: Update setup solution --- .../CustomSetupTool/CustomSetupTool.vcxproj | 19 ++++++------------- .../CustomSetupTool/resource.rc | 12 +++++++----- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 253d75de4c0e..ec82116e1157 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -57,7 +57,7 @@ false StdCall MultiThreadedDebug - _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true ProgramDatabase true @@ -69,11 +69,6 @@ Windows RequireAdministrator - - - - - @@ -84,7 +79,7 @@ true ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) StdCall - _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) MultiThreaded true AnySuitable @@ -102,11 +97,6 @@ true RequireAdministrator - - - - - @@ -128,7 +118,10 @@ - + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index cf68f769845f..887108157590 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -8,6 +8,7 @@ // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" +#include "../../../ProcessHacker/include/phappres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -179,8 +180,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 + FILEVERSION PHAPP_VERSION_NUMBER + PRODUCTVERSION PHAPP_VERSION_NUMBER FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -197,12 +198,12 @@ BEGIN BEGIN VALUE "CompanyName", "Process Hacker" VALUE "FileDescription", "Process Hacker Setup" - VALUE "FileVersion", "1.0" + VALUE "FileVersion", PHAPP_VERSION_STRING VALUE "InternalName", "Process Hacker Setup" - VALUE "LegalCopyright", "(C) Process Hacker Team. Licensed under the GNU GPL, v3." + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3. Copyright (C) 2017" VALUE "OriginalFilename", "ProcessHackerSetup.exe" VALUE "ProductName", "Process Hacker Setup" - VALUE "ProductVersion", "1.0" + VALUE "ProductVersion", PHAPP_VERSION_STRING END END BLOCK "VarFileInfo" @@ -254,6 +255,7 @@ END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" + "#include ""../../../include/phappres.h""\r\n" "\0" END From 43bca697d2082a6d367295b432bcf424357fc126 Mon Sep 17 00:00:00 2001 From: dmex Date: Thu, 18 May 2017 20:54:34 +1000 Subject: [PATCH 126/839] BuilldTools: Revert version test --- ProcessHacker/include/phappres.h | 2 +- tools/CustomBuildTool/Source Files/Build.cs | 4 ++-- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 73216 bytes 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProcessHacker/include/phappres.h b/ProcessHacker/include/phappres.h index 566ab7e62649..c0267bfd3ea9 100644 --- a/ProcessHacker/include/phappres.h +++ b/ProcessHacker/include/phappres.h @@ -6,7 +6,7 @@ #endif #ifndef PHAPP_VERSION_MINOR -#define PHAPP_VERSION_MINOR 1 +#define PHAPP_VERSION_MINOR 0 #endif #ifndef PHAPP_VERSION_BUILD diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 1f929a3ec506..677ae2aa0adf 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -248,8 +248,8 @@ public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, boo if (string.IsNullOrEmpty(BuildCount)) BuildCount = "0"; - BuildVersion = "3.1." + BuildRevision; - BuildLongVersion = "3.1." + BuildCount + "." + BuildRevision; + BuildVersion = "3.0." + BuildRevision; + BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; if (ShowBuildInfo) { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index a922dfbf78d445fbde7a9cd7675ce0c457899a42..06ce779430fce6fa5fada71005b8058b72fc6bd0 100644 GIT binary patch delta 98 zcmZp8!P)SFb3zA`Q|-ns@eW3V&5JsmTm`sHyh{TYUw55l{(Pg(KDngLU(T)(({Zeo zjbs1YRCynfcBCT)uN=)|z&;rd_?R zy2k|fk6)_*=w5WO*0J#Aypyf(lx+>g86lrLW6Y?DyCz(HdMT~?xw;^@+O;6i-(1&p zBiO7oI#b>D`HqL~`yo$OsiJC+d!~mfvaeis#Zz18dz}*v`Ch$G(%wY%YyD8rq8|1D zMT3|0*J_<)F9dca5x6C=^5Nn(FKdKH_36b9Ylk@_fa3;ng9(#$3vy??z*&LiCIRP7 zrWx()v+qm>zNp3>>tJq=12RoP<0(D&(Fe2{H@JZ|jt6er3^cdtEn7bc8e)q}PHo4Q zMt@X+x2$b45M(*tev&O0Sn(7fjRN|?=XR$6C)i*Htv>Hpa{aM>duVL$GF5AdO@2K~ z^BX|FUnj60YL!aM4)Ziy)Lyc7LGC8?SZ>5Lu6ABKP--A&m1OM#n?{997IZkr0UQ#T z_P`002pXN>0+tA@f^3$+n#d|x&H0KZCjeiEavsaO82a{mq)qddscFDW_7Dx+v`K8y zIb?I0+3?Vmw?%7BIjgm(cgU((E{>~gz8~0R7{4Uv2&Wpz1@DsO{MY=E&twi}D*O?4 zN;}ja`9Vg7j~%>e{gR+PT^#g~i8>r5$@#1~%`(yyNpQL~XvyVCa)pvyFrB_nPC;oF zNrFoy9hOLP<$TWgKI-eVSrV+|gKW@xu1b>IFU$qhgJhcVTG{2pOf++Z)f zoyq|AumSLvmR9rJ3zMVzqx^dF0y?% z-5;ko>pQC5E(HSvOR|9{_2Fu-rkB7+FPpK&om_cRBsNWAxe_ZFGL3enbv?S@%bZ^M`(E6S#f2fy~$uylo+Kvo?9#ZO*OX8r@?? pi-kLC$c(vp5bj{@Q%%AhpU6hD8@jbtIFvK0@c-FXUxM;>#2*O-OC|sS delta 2641 zcmaKuTS!zv7{~u7&$_F*mU+o)ElX1~?~0h+G*BqavJ4F$v>wuEix3i|%Q2(-OC_1z z2$K*Z!rH_HEhLB_>_G~lUWB3tAu6aaDYtKS=WIKJbLM6C`+oDE?=m~H+cu=P4e5P) zc>Lnfgxwl|^IH<#!}E`n-MzX#qcaU_S^QfnNx6CP()ou&__t*Q37vwQn4Z?ox|R@mrqO?-|uR=uX3eS?KK>jcWV z4Tx-%*n0)*jRc-5Sp7Wpp7#ub7g=QA5Ci5!0hdiAB{M>7T%qfV2CgeuZY*#sh#BVo z%r_bbeA7*jn9T4JATvm4u41l97cgi#q(plXfZ;hN4BoR}Z%Bjz-_a7+u(r`$rlDUS zTn?0Shs>sl?NG3a6+nvShqA6Y3Ano20KWg%Ifc9E`SEwb9HU>?Q-F1Rk$9j$39(I_1!9oR3TzM3 ziOlPLMy!SFLg4lCkEHW@3sQl7CNm^>gWyUNO6)nYoLL$0m6)BgzzrjtCHjjPHQqO5 zX3SU!ZE0$ulb9-CjD^l6>H_bBFViFo;c8LH>+x5ZXQ3;$(3Pf9e21bnn=ORfEdthB z=;~#iSwB0cMhoE~S;&)Sx)uxFaSPpPVupFG=(-06+w=v&u@JvYW>+(SR=!$hk(ofU zg5?oAATodbEZ}W|#T Date: Thu, 18 May 2017 20:55:41 +1000 Subject: [PATCH 127/839] Appveyor: revert version test --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c97fce5fff29..fcbc818fe0f8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ image: Visual Studio 2017 # Version format. -version: "3.1.{build}" +version: "3.0.{build}" # Only build the master branch. branches: From 574d2325061c4c801f7ca9313c8a3e1996729100 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 00:21:28 +1000 Subject: [PATCH 128/839] BuildTools: Cleanup solution --- tools/CustomBuildTool/CustomBuildTool.csproj | 5 +- tools/CustomBuildTool/Source Files/Build.cs | 47 ++- .../CustomBuildTool/Source Files/HeaderGen.cs | 24 +- tools/CustomBuildTool/Source Files/Json.cs | 35 -- .../Source Files/NativeMethods.cs | 126 ++---- tools/CustomBuildTool/Source Files/Nightly.cs | 38 -- tools/CustomBuildTool/Source Files/Program.cs | 24 +- tools/CustomBuildTool/Source Files/Utils.cs | 380 ++++++++++++++++++ tools/CustomBuildTool/Source Files/Verify.cs | 59 --- .../Source Files/VisualStudio.cs | 133 ------ tools/CustomBuildTool/Source Files/Zip.cs | 25 +- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 73216 -> 69120 bytes 13 files changed, 513 insertions(+), 383 deletions(-) delete mode 100644 tools/CustomBuildTool/Source Files/Json.cs delete mode 100644 tools/CustomBuildTool/Source Files/Nightly.cs create mode 100644 tools/CustomBuildTool/Source Files/Utils.cs delete mode 100644 tools/CustomBuildTool/Source Files/Verify.cs delete mode 100644 tools/CustomBuildTool/Source Files/VisualStudio.cs diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index 2e456e11dfed..83a5df2111b6 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -86,14 +86,11 @@ - - - - + diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 677ae2aa0adf..136c97c1f0a6 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -1,4 +1,26 @@ -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.IO; using System.Net.Http; using System.Security; @@ -11,7 +33,7 @@ namespace CustomBuildTool public static class Build { private static DateTime TimeStart; - private static bool BuildNightly = false; + private static bool BuildNightly; private static string BuildBranch; private static string BuildOutputFolder = "build"; private static string BuildCommit; @@ -232,8 +254,8 @@ public static string GetBuildLogString() public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) { - BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD"); - BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD"); // rev-parse HEAD + BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD").Trim(); // rev-parse HEAD Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildBranch, ConsoleColor.White); Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); @@ -681,7 +703,7 @@ public static bool BuildSetupExe() { Program.PrintColorMessage("Building build-setup.exe...", ConsoleColor.Cyan, true, BuildFlags.BuildVerbose); - if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit)) + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildVerbose)) return false; try @@ -951,6 +973,7 @@ public static bool AppveyorUploadBuildFiles() if (!BuildNightly) return false; + // Cleanup existing output files for (int i = 0; i < releaseFileArray.Length; i++) { if (File.Exists(releaseFileArray[i])) @@ -967,6 +990,7 @@ public static bool AppveyorUploadBuildFiles() } } + // Rename files with the current build version -3.1- for (int i = 0; i < buildFileArray.Length; i++) { if (File.Exists(buildFileArray[i])) @@ -983,6 +1007,7 @@ public static bool AppveyorUploadBuildFiles() } } + // Upload build files to Appveyor storage. for (int i = 0; i < releaseFileArray.Length; i++) { if (File.Exists(releaseFileArray[i])) @@ -1001,6 +1026,14 @@ public static bool AppveyorUploadBuildFiles() } } + try + { + // Update Appveyor build version string. + Win32.ShellExecute("appveyor", "UpdateBuild -Version \"" + BuildLongVersion + " (" + BuildCommit + ")\" "); + } + catch (Exception) + { } + return true; } @@ -1015,7 +1048,7 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) string error32 = Win32.ShellExecute( MSBuildExePath, "/m /nologo /verbosity:quiet " + - "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug " : "Release ") + "/p:Platform=Win32 " + "/p:ExternalCompilerOptions=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + Solution @@ -1037,7 +1070,7 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) string error64 = Win32.ShellExecute( MSBuildExePath, "/m /nologo /verbosity:quiet " + - "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release") + " " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug " : "Release ") + "/p:Platform=x64 " + "/p:ExternalCompilerOptions=\"PH_BUILD_API;PHAPP_VERSION_REVISION=\"" + BuildRevision + "\";PHAPP_VERSION_BUILD=\"" + BuildCount + "\"\" " + Solution diff --git a/tools/CustomBuildTool/Source Files/HeaderGen.cs b/tools/CustomBuildTool/Source Files/HeaderGen.cs index cd4c1dbe70a4..dd4f9b9e5b53 100644 --- a/tools/CustomBuildTool/Source Files/HeaderGen.cs +++ b/tools/CustomBuildTool/Source Files/HeaderGen.cs @@ -1,4 +1,26 @@ -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.IO; using System.Linq; using System.Collections.Generic; diff --git a/tools/CustomBuildTool/Source Files/Json.cs b/tools/CustomBuildTool/Source Files/Json.cs deleted file mode 100644 index ea01ac43c211..000000000000 --- a/tools/CustomBuildTool/Source Files/Json.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Serialization.Json; -using System.Text; -using System.Threading.Tasks; - -namespace CustomBuildTool -{ - 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); - } - } - } -} diff --git a/tools/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs index 6478cdda836d..30770f760157 100644 --- a/tools/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -1,112 +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 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 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 (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) - { - File.Copy(CurrentFile, NewFile, true); - } - } - - public const int SW_HIDE = 0; - public const int SW_SHOW = 5; - public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - public static readonly IntPtr STD_OUTPUT_HANDLE = new IntPtr(-11); - public static readonly IntPtr STD_INPUT_HANDLE = new IntPtr(-10); - public static readonly IntPtr STD_ERROR_HANDLE = new IntPtr(-12); - - [DllImport("kernel32.dll", ExactSpelling = true)] - public static extern IntPtr GetStdHandle(IntPtr 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(); - [DllImport("user32.dll", ExactSpelling = true)] - public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - } - - [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/Nightly.cs b/tools/CustomBuildTool/Source Files/Nightly.cs deleted file mode 100644 index a9452fb42f97..000000000000 --- a/tools/CustomBuildTool/Source Files/Nightly.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Runtime.Serialization; - -namespace CustomBuildTool -{ - [DataContract] - public class BuildUpdateRequest - { - [DataMember(Name = "updated")] - public string Updated { get; set; } - - [DataMember(Name = "version")] - public string Version { get; set; } - - [DataMember(Name = "size")] - public string FileLength { get; set; } - - [DataMember(Name = "forum_url")] - public string ForumUrl { get; set; } - - [DataMember(Name = "setup_url")] - public string Setupurl { get; set; } - - [DataMember(Name = "bin_url")] - public string Binurl { get; set; } - - [DataMember(Name = "hash_setup")] - public string HashSetup { get; set; } - - [DataMember(Name = "hash_bin")] - public string HashBin { get; set; } - - [DataMember(Name = "sig")] - public string sig { get; set; } - - [DataMember(Name = "message")] - public string Message { get; set; } - } -} diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs index 09f0d6b1ff10..f123314e63a1 100644 --- a/tools/CustomBuildTool/Source Files/Program.cs +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -1,4 +1,26 @@ -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.Collections.Generic; using System.Security.Principal; diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs new file mode 100644 index 000000000000..ab413c2166cf --- /dev/null +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -0,0 +1,380 @@ +/* + * 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 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 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 (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) + { + File.Copy(CurrentFile, NewFile, true); + } + } + + public const int SW_HIDE = 0; + public const int SW_SHOW = 5; + public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + public static readonly IntPtr STD_OUTPUT_HANDLE = new IntPtr(-11); + public static readonly IntPtr STD_INPUT_HANDLE = new IntPtr(-10); + public static readonly IntPtr STD_ERROR_HANDLE = new IntPtr(-12); + + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern IntPtr GetStdHandle(IntPtr 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(); + [DllImport("user32.dll", ExactSpelling = true)] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + } + + 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 (Rijndael rijndael = GetRijndael(secret)) + using (FileStream fileStream = File.OpenRead(fileName)) + using (FileStream fileStream2 = File.Create(outFileName)) + using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateEncryptor(), CryptoStreamMode.Write)) + { + fileStream.CopyTo(cryptoStream); + } + } + + public static void Decrypt(string FileName, string outFileName, string secret) + { + using (Rijndael rijndael = GetRijndael(secret)) + using (FileStream fileStream = File.OpenRead(FileName)) + using (FileStream fileStream2 = File.Create(outFileName)) + using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) + { + 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 stream = File.OpenRead(FileName)) + using (BufferedStream bufferedStream = new BufferedStream(stream, 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 vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\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 " + + "-requires Microsoft.Component.MSBuild " + + "-property installationPath " + ); + + if (string.IsNullOrEmpty(vswhereResult)) + return null; + + if (File.Exists(vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) + return vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; + + return null; + } + else + { + try + { + VisualStudioInstance instance = FindVisualStudioInstance(); + + if (instance != null) + { + if (File.Exists(instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) + return instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; + } + } + 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 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 = "updated")] + public string Updated { get; set; } + + [DataMember(Name = "version")] + public string Version { get; set; } + + [DataMember(Name = "size")] + public string FileLength { get; set; } + + [DataMember(Name = "forum_url")] + public string ForumUrl { get; set; } + + [DataMember(Name = "setup_url")] + public string Setupurl { get; set; } + + [DataMember(Name = "bin_url")] + public string Binurl { get; set; } + + [DataMember(Name = "hash_setup")] + public string HashSetup { get; set; } + + [DataMember(Name = "hash_bin")] + public string HashBin { get; set; } + + [DataMember(Name = "sig")] + public string sig { get; set; } + + [DataMember(Name = "message")] + public string Message { get; set; } + } + + [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/Verify.cs b/tools/CustomBuildTool/Source Files/Verify.cs deleted file mode 100644 index f652094cb16f..000000000000 --- a/tools/CustomBuildTool/Source Files/Verify.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; - -namespace CustomBuildTool -{ - 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 (Rijndael rijndael = GetRijndael(secret)) - using (FileStream fileStream = File.OpenRead(fileName)) - using (FileStream fileStream2 = File.Create(outFileName)) - using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateEncryptor(), CryptoStreamMode.Write)) - { - fileStream.CopyTo(cryptoStream); - } - } - - public static void Decrypt(string FileName, string outFileName, string secret) - { - using (Rijndael rijndael = GetRijndael(secret)) - using (FileStream fileStream = File.OpenRead(FileName)) - using (FileStream fileStream2 = File.Create(outFileName)) - using (CryptoStream cryptoStream = new CryptoStream(fileStream2, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) - { - 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 stream = File.OpenRead(FileName)) - using (BufferedStream bufferedStream = new BufferedStream(stream, 0x1000)) - { - SHA256Managed sha = new SHA256Managed(); - byte[] checksum = sha.ComputeHash(bufferedStream); - - return BitConverter.ToString(checksum).Replace("-", string.Empty); - } - } - } -} diff --git a/tools/CustomBuildTool/Source Files/VisualStudio.cs b/tools/CustomBuildTool/Source Files/VisualStudio.cs deleted file mode 100644 index adef9214779a..000000000000 --- a/tools/CustomBuildTool/Source Files/VisualStudio.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.IO; -using System.Linq; - -namespace CustomBuildTool -{ - [System.Security.SuppressUnmanagedCodeSecurity] - public static class VisualStudio - { - public static string GetMsbuildFilePath() - { - string vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\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 " + - "-requires Microsoft.Component.MSBuild " + - "-property installationPath " - ); - - if (string.IsNullOrEmpty(vswhereResult)) - return null; - - if (File.Exists(vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) - return vswhereResult + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; - - return null; - } - else - { - VisualStudioInstance instance = FindVisualStudioInstance(); - - if (instance != null) - { - if (File.Exists(instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe")) - return instance.Path + "\\MSBuild\\15.0\\Bin\\MSBuild.exe"; - } - - 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 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(); - } - } -} diff --git a/tools/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs index cddb9f950b88..826659de53f8 100644 --- a/tools/CustomBuildTool/Source Files/Zip.cs +++ b/tools/CustomBuildTool/Source Files/Zip.cs @@ -1,7 +1,28 @@ -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.IO; using System.IO.Compression; -using System.Text; namespace CustomBuildTool { diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 06ce779430fce6fa5fada71005b8058b72fc6bd0..9177a7a24ba8204a88c65a85ee637ab037780de5 100644 GIT binary patch delta 14947 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 delta 14585 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 XYR^a8Js(NmuEGDpw%u1RpOO9_;|W{U diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index e566f8f63d56e5b85f14f00d06efa403dae6cfe3..60f23aad14bf323c3aad4586f2eba905471898ba 100644 GIT binary patch literal 69120 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 literal 73216 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 M4II|M|EC)Ge^3x)Hvj+t From 5eb02dad517e4d515e58f9c9d2f310761f769291 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 00:30:16 +1000 Subject: [PATCH 129/839] Updater: Add Changelog window --- plugins/Updater/Updater.rc | 22 ++++++++++++++++ plugins/Updater/options.c | 53 ++++++++++++++++++++++++++++++++++++++ plugins/Updater/page3.c | 3 +-- plugins/Updater/page4.c | 3 +-- plugins/Updater/page5.c | 4 +-- plugins/Updater/resource.h | 13 ++++++---- plugins/Updater/updater.c | 42 +++++++++++++++--------------- plugins/Updater/updater.h | 10 +++++++ 8 files changed, 117 insertions(+), 33 deletions(-) diff --git a/plugins/Updater/Updater.rc b/plugins/Updater/Updater.rc index 3819a0a7ca80..42c50c87d468 100644 --- a/plugins/Updater/Updater.rc +++ b/plugins/Updater/Updater.rc @@ -99,6 +99,15 @@ BEGIN DEFPUSHBUTTON "Close",IDCANCEL,158,33,50,14 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -115,6 +124,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 47 END + + IDD_TEXT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 308 + TOPMARGIN, 1 + BOTTOMMARGIN, 173 + END END #endif // APSTUDIO_INVOKED @@ -129,6 +146,11 @@ BEGIN 0 END +IDD_TEXT AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index cbef57deb360..77c0c978bda6 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -69,4 +69,57 @@ VOID ShowOptionsDialog( Parent, OptionsDlgProc ); +} + +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; + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); + + SetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), PhGetString(context->BuildMessage)); + + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); + } + break; + case WM_DESTROY: + { + 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; } \ No newline at end of file diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index 740dae4589f1..c5fe5c051573 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -77,7 +77,7 @@ VOID ShowAvailableDialog( 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_EXPAND_FOOTER_AREA; + 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.cxWidth = 200; @@ -94,7 +94,6 @@ VOID ShowAvailableDialog( Context->RevisionVersion, PhGetStringOrEmpty(Context->Size) )->Buffer; - config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file diff --git a/plugins/Updater/page4.c b/plugins/Updater/page4.c index 237e49e5de0b..a8df5182aaa4 100644 --- a/plugins/Updater/page4.c +++ b/plugins/Updater/page4.c @@ -62,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_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR | TDF_EXPAND_FOOTER_AREA; + 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; @@ -76,7 +76,6 @@ VOID ShowProgressDialog( Context->RevisionVersion )->Buffer; config.pszContent = L"Downloaded: ~ of ~ (0%)\r\nSpeed: ~ KB/s"; - config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } \ No newline at end of file diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 36b9860d61b0..0b766debcfeb 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -118,7 +118,7 @@ VOID ShowUpdateInstallDialog( config.cButtons = ARRAYSIZE(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); @@ -158,7 +158,6 @@ VOID ShowLatestVersionDialog( Context->CurrentRevisionVersion, PhaFormatDateTime(&systemTime)->Buffer )->Buffer; - config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } @@ -186,7 +185,6 @@ VOID ShowNewerVersionDialog( Context->CurrentMinorVersion, Context->CurrentRevisionVersion )->Buffer; - config.pszExpandedInformation = PhGetString(Context->BuildMessage); SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } diff --git a/plugins/Updater/resource.h b/plugins/Updater/resource.h index 11f1b12fef3e..67f9ce64bd40 100644 --- a/plugins/Updater/resource.h +++ b/plugins/Updater/resource.h @@ -2,16 +2,19 @@ // Microsoft Visual C++ generated include file. // Used by Updater.rc // -#define IDD_OPTIONS 103 -#define IDC_AUTOCHECKBOX 1002 +#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 107 +#define _APS_NEXT_RESOURCE_VALUE 103 + #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1010 -#define _APS_NEXT_SYMED_VALUE 103 +#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 55c88a814a7f..8e71e80d5df9 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -63,12 +63,6 @@ VOID FreeUpdateContext( PhClearReference(&Context->SetupFileDownloadUrl); PhClearReference(&Context->BuildMessage); - if (Context->IconLargeHandle) - DestroyIcon(Context->IconLargeHandle); - - if (Context->IconSmallHandle) - DestroyIcon(Context->IconSmallHandle); - PhDereferenceObject(Context); } @@ -76,20 +70,8 @@ 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) - ); + Context->IconSmallHandle = UT_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = UT_LOAD_SHARED_ICON_LARGE(MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); @@ -101,7 +83,13 @@ VOID TaskDialogLinkClicked( { if (!PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) { - PhShellExecute(Context->DialogHandle, PhGetStringOrEmpty(Context->ReleaseNotesUrl), NULL); + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_TEXT), + Context->DialogHandle, + TextDlgProc, + (LPARAM)Context + ); } } @@ -441,6 +429,18 @@ BOOLEAN QueryUpdateData( Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "setup_url")); Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "message")); + PH_STRING_BUILDER sb; + PhInitializeStringBuilder(&sb, 0x100); + + for (size_t i = 0; i < Context->BuildMessage->Length; i++) + { + if (Context->BuildMessage->Data[i] == '\n') + PhAppendFormatStringBuilder(&sb, L"\r\n"); + else + PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); + } + PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); + CleanupJsonParser(jsonObject); if (PhIsNullOrEmptyString(Context->Signature)) diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index a6835fa9268b..ab8b850a635c 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -55,6 +55,9 @@ ((ULONGLONG)(build) << 16) | \ ((ULONGLONG)(revision) << 0)) +#define UT_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) +#define UT_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhLibImageBase, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) + #ifdef _DEBUG // Force update checks to succeed (most of the below flags require this to be defined). //#define FORCE_UPDATE_CHECK @@ -178,6 +181,13 @@ VOID ShowOptionsDialog( _In_opt_ HWND Parent ); +INT_PTR CALLBACK TextDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + // verify.c typedef struct _UPDATER_HASH_CONTEXT From d0d9c7d7c3d20d979463dc4ebce5d5344399bfd9 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 01:45:55 +1000 Subject: [PATCH 130/839] Updater: Add nightly build location --- plugins/Updater/updater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 8e71e80d5df9..6ade85239887 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -352,7 +352,7 @@ BOOLEAN QueryUpdateData( if (!(httpRequestHandle = WinHttpOpenRequest( httpConnectionHandle, NULL, - L"/processhacker/plugins/nightly.php", + L"/processhacker/nightly.php?phupdater", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, From be4ec6c094bed48834cd93b371d421749a042f95 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 01:51:16 +1000 Subject: [PATCH 131/839] Updater: Fix typo --- plugins/Updater/updater.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 6ade85239887..6cc3cdc1facd 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -309,6 +309,7 @@ BOOLEAN QueryUpdateData( HINTERNET httpRequestHandle = NULL; ULONG stringBufferLength = 0; PSTR stringBuffer = NULL; + PH_STRING_BUILDER sb; PVOID jsonObject = NULL; mxml_node_t* xmlNode = NULL; PPH_STRING versionHeader = UpdateVersionString(); @@ -429,13 +430,11 @@ BOOLEAN QueryUpdateData( Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "setup_url")); Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "message")); - PH_STRING_BUILDER sb; PhInitializeStringBuilder(&sb, 0x100); - - for (size_t i = 0; i < Context->BuildMessage->Length; i++) + for (SIZE_T i = 0; i < Context->BuildMessage->Length; i++) { if (Context->BuildMessage->Data[i] == '\n') - PhAppendFormatStringBuilder(&sb, L"\r\n"); + PhAppendStringBuilder2(&sb, L"\r\n"); else PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); } From 7cbed97282fbb0c635da7fe614dca545bf00dff7 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 18:27:30 +1000 Subject: [PATCH 132/839] Update solutions, Fix peview clr layout --- ProcessHacker/ProcessHacker.vcxproj | 21 +++++++++++++++++---- tools/peview/peview.rc | 3 +-- tools/peview/peview.vcxproj | 21 +++++++++++++++++---- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 1c52fe1b7730..60b6c8f4be98 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -104,6 +104,10 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -128,6 +132,10 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -159,6 +167,10 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -189,6 +201,10 @@ ProcessHacker.def aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -377,10 +393,7 @@ - - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - + diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 55f303c4af91..c9dd19af9d33 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -192,9 +192,8 @@ BEGIN 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 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,57,286,85 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,57,286,216 LTEXT "Sections:",IDC_STATIC,7,47,30,8 - CONTROL "",IDC_SECTION,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,145,286,128 END IDD_PELOADCONFIG DIALOGEX 0, 0, 300, 280 diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index 616289ae31be..a5a6ae577e12 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -105,6 +105,10 @@ MachineX86 6.01 + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + @@ -130,6 +134,10 @@ MachineX64 6.01 + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + @@ -161,6 +169,10 @@ true 6.01 + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + @@ -191,6 +203,10 @@ true 6.01 + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + @@ -217,10 +233,7 @@ - - _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - + From 5866671772ce702f459fdde3fac5e51b88b5c77d Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 19:56:54 +1000 Subject: [PATCH 133/839] Peview: Fix type filter, Fix crash. Update build tools. --- tools/CustomBuildTool/Source Files/Build.cs | 49 ++-- tools/CustomBuildTool/Source Files/Utils.cs | 5 +- .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes tools/peview/pdb.c | 276 ++++++------------ 5 files changed, 118 insertions(+), 212 deletions(-) diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 136c97c1f0a6..6242b16b4135 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -35,7 +35,7 @@ public static class Build private static DateTime TimeStart; private static bool BuildNightly; private static string BuildBranch; - private static string BuildOutputFolder = "build"; + private static string BuildOutputFolder; private static string BuildCommit; private static string BuildVersion; private static string BuildLongVersion; @@ -145,6 +145,7 @@ public static class Build public static bool InitializeBuildEnvironment(bool CheckDependencies) { TimeStart = DateTime.Now; + BuildOutputFolder = "build\\output"; MSBuildExePath = VisualStudio.GetMsbuildFilePath(); CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; GitExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles%\\Git\\cmd\\git.exe"); @@ -182,7 +183,7 @@ public static bool InitializeBuildEnvironment(bool CheckDependencies) if (!File.Exists(MSBuildExePath)) { - Program.PrintColorMessage("MsBuild not installed.\r\nExiting...\r\n", ConsoleColor.Red); + Program.PrintColorMessage("MsBuild not installed. Exiting.", ConsoleColor.Red); return false; } @@ -215,47 +216,29 @@ public static void CleanupBuildEnvironment() { string[] cleanupFileArray = { - BuildOutputFolder + "\\processhacker-build-setup.exe", - BuildOutputFolder + "\\processhacker-build-bin.zip", - BuildOutputFolder + "\\processhacker-build-src.zip", - BuildOutputFolder + "\\processhacker-build-sdk.zip", - BuildOutputFolder + "\\processhacker-build-pdb.zip", - BuildOutputFolder + "\\processhacker-build-checksums.txt", - BuildOutputFolder + "\\processhacker-build-package.appxbundle", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-setup.exe", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-bin.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-src.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-sdk.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-pdb.zip", - BuildOutputFolder + "\\processhacker-" + BuildVersion + "-checksums.txt" + BuildOutputFolder, + "bin", + "sdk", }; try { - if (Directory.Exists("build\\output")) - Directory.Delete("build\\output", true); - for (int i = 0; i < cleanupFileArray.Length; i++) { - if (File.Exists(cleanupFileArray[i])) - File.Delete(cleanupFileArray[i]); + if (Directory.Exists(cleanupFileArray[i])) + Directory.Delete(cleanupFileArray[i], true); } } catch (Exception ex) { - Program.PrintColorMessage("[CleanupBuildEnvironment] " + ex, ConsoleColor.Red); + Program.PrintColorMessage("[Cleanup] " + ex, ConsoleColor.Red); } } - 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 void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) { BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); - BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD").Trim(); // rev-parse HEAD + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse --short HEAD").Trim(); Program.PrintColorMessage("Branch: ", ConsoleColor.Cyan, false); Program.PrintColorMessage(BuildBranch, ConsoleColor.White); Program.PrintColorMessage("Commit: ", ConsoleColor.Cyan, false); @@ -486,6 +469,7 @@ public static bool CopyVersionHeader() 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) { @@ -894,6 +878,11 @@ public static bool BuildUpdateSignature() 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 void WebServiceUpdateConfig() { if (string.IsNullOrEmpty(BuildSetupHash)) @@ -903,7 +892,8 @@ public static void WebServiceUpdateConfig() if (string.IsNullOrEmpty(BuildSetupSig)) return; - string appveyorMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); + string buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --pretty=format:\"%h %an %s (%cr)\""); + string buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); string buildPostString = Json.Serialize(new BuildUpdateRequest { Updated = TimeStart.ToString("o"), @@ -915,7 +905,8 @@ public static void WebServiceUpdateConfig() HashSetup = BuildSetupHash, HashBin = BuildBinHash, sig = BuildSetupSig, - Message = appveyorMessage + Message = buildSummary, + Changelog = buildChangelog, }); if (string.IsNullOrEmpty(buildPostString)) diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index ab413c2166cf..99a974c973ec 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -79,9 +79,7 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) return; if (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) - { File.Copy(CurrentFile, NewFile, true); - } } public const int SW_HIDE = 0; @@ -345,6 +343,9 @@ public class BuildUpdateRequest [DataMember(Name = "message")] public string Message { get; set; } + + [DataMember(Name = "changelog")] + public string Changelog { get; set; } } [Flags] diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 9177a7a24ba8204a88c65a85ee637ab037780de5..b6f79ef199c0234d41ce3ff314eedc43bcf75b03 100644 GIT binary patch delta 18521 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 E8^~)Ff&c&j delta 18440 zcmbt*37k~bmHv6Js$Q*KUDZo1T~*!HKtuJ$4ub59vWbW+ilTtD0*W+Tj0#9sLmPt% zO7jr+1w6rQ)>3`a zt`Yyn;-5|qT04{I>-&gUdl8Y*YF6ug(z8MF|6nRkD=IzQBS%Y^9?#KIrgOE7vq-U2CGv%S&{}miu7h!D6*QjudZXF=K=*0w<{$Nb+HNyx zs~GFnk+(Cw2LqfKv!w{Xp}=J(CjwpuN0?zB~;H8hQA zK26$9@vyYEzg9F#i?ST+>7+I^6IBaV6|I{DRZXbH&K14ThMIIjO}5m8EzO;IO~lf? zmDfZqO_4p9tHRQx$K+KpOSLMmiCdaqZDQV5-v^x}+eh_f%tBfzfxW@wS}Nqjs^}6-6|7MGmKGK|d{kate?Fzbv&x+n5^;_I*|;6V{9I!0aN<_?OKP^uT~ zYp21wr6#J{MO^`P@4PyucCb3t2dY#LKpn+;+UY_JI>sfa_QI;(2BhX;wAEKki#-|I zMfHKGCS|!=%Jk+eJreb(9UgOKpu)5}Ye)4(^oBF_wOFL|uwY75#p+eg=JsZyS{{)C zk7;TQjH=CRE_Q~7WHRkz)xXG8tZY8XlnKkV8RbK5qUP_MiS#rWtGt;mw~DMNC~~1P z;ULdaqt}wVq5fUft^&2lmil8ZtEwndmYNGm-Jhc$Gp+Y$ttMNjDJJ>c)28C&#K$fn6q<)p7u^{L?fH+&dK#R?EpxTVMC;Fd-CFqbD zsNM~lOWi&E$64jdsUCAD(#0iQ1i&hB22(Rfzv2o!o}*u~wMS{zxfzn$pQDa4(2D?8 zsE)&U%9yG9+4COGt9o;L|?J>!*uW%ss2bBDlvd>R`h zzmXaM`AA^^x4GFH*Ox@i=e;rKC`=k>^FZ^ccff#0ku^6TTku5ZcTcwXGcz?@r#e@l zW|>WSScN&+SC;14msKr;xzPRH0{7BbN81u`$%my)WZoCl&kD36i&pHX!`?h7=aiud z@<@%5l(#DY57&o<|XymDQN6YD7V}((36EK#h6;J4F{eP0^SjS`FSzB#o zN88Qd+4_qtHT`vgR%FqNJzsw@SoAE^)T+O?DpO0ZGh6@XpmJIZbiv_=$47nkkKT7D z&4530=KdrmLv1yJ$pbnMl6o~u>noMmy`4JPCuZr9s88Jr;xcDdhs^8!iS$7>@p(_N z)G`=SW|ke#Sy;+jyeil^n}?;m#n*zFU#nsXO4U> zYZnZtM>}R9^)VDOFP$mHE7D(?$GXsB!{Wmrs!IVb;YTWp)XiPYtBVH(9)&VD?bRQ; zn0t%MM&%BB*4UO=N1nV^#&b{&n$_`am>Pnf$*uD-vrnM1;T5>$3!tvqOu3_BYA$M= z%QPVEf=e!gRY2~j*`Yppt<+~Q@@3CnEL&$Ubyk6z<&1hJL?u~<^0;2hR9r_0-w1wc z*_Uw7au&szQ{u;2B(u<5D>|EZ1k3e%x|)v$BMaVuPr>k#6*qTS?$D;ZmfK_<n#*hrgv_RrM8h8B$aSXrIu|K-3QslVd;e!81Ezgn zn8B)P?tI>%rg^Q@^puIf%VR_4e14(LJiRm?$q!ed@lKn$s5G9=&n+=#t`B+bQq;1w z2rWN#Pzv4iC5XEp?n_6YA|6AXp2Eg35^`5VT@AT~V_C)=|9%7-yLI8r+3UEB`P>Y&wh-W?jWx)y0)maxCI#=wG$N7}CRIz*HmW$Eo0 zgPJs=*wtp%jz);)>scu$>E;{6?&TO~uxSl*^$=W}ZxZpZ5DPAGT+imUr�)Cze}K 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 OqaN?Kdv8cPq5VH_`}I-) diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 60f23aad14bf323c3aad4586f2eba905471898ba..372d398db27ce85002681c32d8b6b0690eca97d9 100644 GIT binary patch delta 17059 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, + Index, + TI_GET_CHILDRENCOUNT, + &symbolCount + )) + { + return FALSE; + } + + if (symbolCount == 0) + return TRUE; + + length = sizeof(TI_FINDCHILDREN_PARAMS) + symbolCount * sizeof(ULONG); + symbols = _alloca(length); + memset(symbols, 0, length); + + symbols->Count = symbolCount; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, symbols)) + return FALSE; + + *Count = symbolCount; + *Params = symbols; + + return TRUE; +} + BOOLEAN SymbolInfo_UdtVariables( _Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, @@ -928,6 +968,7 @@ BOOLEAN SymbolInfo_UdtVariables( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -935,33 +976,16 @@ BOOLEAN SymbolInfo_UdtVariables( if (MaxVars <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - *Vars = 0; - return TRUE; // No children -> no member variables - } - - // Get the children - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - params->Count = childrenLength; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Vars = 0; - // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) { - pVars[*Vars] = params->ChildId[i]; + pVars[*Vars] = symbolParams->ChildId[i]; (*Vars)++; @@ -982,6 +1006,7 @@ BOOLEAN SymbolInfo_UdtFunctions( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -989,34 +1014,16 @@ BOOLEAN SymbolInfo_UdtFunctions( if (MaxFuncs <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - *Funcs = 0; // No children -> no member variables - return TRUE; - } - - // Get the children - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - - params->Count = childrenLength; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Funcs = 0; - // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagFunction)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagFunction)) { - pFuncs[*Funcs] = params->ChildId[i]; + pFuncs[*Funcs] = symbolParams->ChildId[i]; (*Funcs)++; @@ -1037,6 +1044,7 @@ BOOLEAN SymbolInfo_UdtBaseClasses( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -1044,34 +1052,16 @@ BOOLEAN SymbolInfo_UdtBaseClasses( if (MaxBases <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - *Bases = 0; // No children -> no member variables - return TRUE; - } - - // Get the children - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - - params->Count = childrenLength; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Bases = 0; - // Enumerate children, looking for base classes, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagBaseClass)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagBaseClass)) { - pBases[*Bases] = params->ChildId[i]; + pBases[*Bases] = symbolParams->ChildId[i]; (*Bases)++; @@ -1092,6 +1082,7 @@ BOOLEAN SymbolInfo_UdtUnionMembers( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -1099,34 +1090,16 @@ BOOLEAN SymbolInfo_UdtUnionMembers( if (MaxMembers <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - *Members = 0; - return TRUE; // No children -> no members - } - - // Get the children - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - - params->Count = childrenLength; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Members = 0; - // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) { - pMembers[*Members] = params->ChildId[i]; + pMembers[*Members] = symbolParams->ChildId[i]; (*Members)++; @@ -1147,6 +1120,7 @@ BOOLEAN SymbolInfo_FunctionArguments( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) return FALSE; @@ -1154,34 +1128,16 @@ BOOLEAN SymbolInfo_FunctionArguments( if (MaxArgs <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - *Args = 0; - return TRUE; // No children -> no member variables - } - - // Get the children - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - - params->Count = childrenLength; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Args = 0; - // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagFunctionArgType)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagFunctionArgType)) { - pArgs[*Args] = params->ChildId[i]; + pArgs[*Args] = symbolParams->ChildId[i]; (*Args)++; @@ -1202,6 +1158,7 @@ BOOLEAN SymbolInfo_Enumerators( ) { ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) return FALSE; @@ -1209,34 +1166,16 @@ BOOLEAN SymbolInfo_Enumerators( if (MaxEnums <= 0) return FALSE; - // Get the number of children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_CHILDRENCOUNT, &childrenLength)) - return FALSE; - - if (childrenLength == 0) - { - // No children -> no enumerators - *Enums = 0; - return TRUE; - } - - ULONG FindChildrenSize = sizeof(TI_FINDCHILDREN_PARAMS) + childrenLength * sizeof(ULONG); - TI_FINDCHILDREN_PARAMS* params = (TI_FINDCHILDREN_PARAMS*)_alloca(FindChildrenSize); - memset(params, 0, FindChildrenSize); - params->Count = childrenLength; - - // Get the children - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, params)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Enums = 0; - // Enumerate children, looking for enumerators, and copy their indexes. for (ULONG i = 0; i < childrenLength; i++) { - if (SymbolInfo_CheckTag(Context, params->ChildId[i], SymTagData)) + if (SymbolInfo_CheckTag(Context, symbolParams->ChildId[i], SymTagData)) { - pEnums[*Enums] = params->ChildId[i]; + pEnums[*Enums] = symbolParams->ChildId[i]; (*Enums)++; @@ -1311,21 +1250,19 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( { TypeInfo Info; - // Get the type information if (!SymbolInfo_DumpType(Obj, Index, &Info)) { return FALSE; } else { - // Is it a pointer ? ULONG numPointers = 0; if (Info.Tag == SymTagPointerType) { - // Yes, get the number of * to show ULONG typeIndex = 0; + // Yes, get the number of * to show if (!SymbolInfo_PointerType(Obj, Index, &typeIndex, &numPointers)) return FALSE; @@ -1345,12 +1282,10 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.sBaseTypeInfo.BaseType, Info.sBaseTypeInfo.Length)); PhAppendStringBuilder2(TypeName, L" "); break; - case SymTagTypedef: PhAppendStringBuilder2(TypeName, Info.sTypedefInfo.Name); PhAppendStringBuilder2(TypeName, L" "); break; - case SymTagUDT: { if (Info.UdtKind) @@ -1370,17 +1305,13 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( } } break; - case SymTagEnum: PhAppendStringBuilder2(TypeName, Info.sEnumInfo.Name); break; - case SymTagFunctionType: { if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.StaticFunction) - { PhAppendStringBuilder2(TypeName, L"static "); - } // return value if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) @@ -1395,15 +1326,15 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( { PhAddItemList(Obj->UdtList, UlongToPtr(Info.sFunctionTypeInfo.ClassIndex)); - /* - // It is not needed to print the class name here, because it is contained in the function name + /* + // 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 that it is a function + // Print name PhAppendStringBuilder2(TypeName, *VarName); // Print parameters @@ -1415,24 +1346,14 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( return FALSE; if (i < (Info.sFunctionTypeInfo.NumArgs - 1)) - { PhAppendStringBuilder2(TypeName, L", "); - } } PhAppendStringBuilder2(TypeName, L") "); // Print "this" adjustment value if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.ThisAdjust != 0) - { - WCHAR buffer[MAX_PATH + 1] = L""; - - _snwprintf(buffer, ARRAYSIZE(buffer), L"this+%u", Info.sFunctionTypeInfo.ThisAdjust); - - PhAppendStringBuilder2(TypeName, L": "); - PhAppendStringBuilder2(TypeName, buffer); - PhAppendStringBuilder2(TypeName, L" "); - } + PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.sFunctionTypeInfo.ThisAdjust); } break; @@ -1442,7 +1363,6 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( return FALSE; } break; - case SymTagArrayType: { // Print element type name @@ -1450,26 +1370,15 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( return FALSE; PhAppendStringBuilder2(TypeName, L" "); - //PhAppendStringBuilder2(TypeName, *VarName); // Print dimensions for (ULONG i = 0; i < Info.sArrayTypeInfo.NumDimensions; i++) - { - WCHAR buffer[MAX_PATH + 1] = L""; - - _snwprintf(buffer, ARRAYSIZE(buffer), L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); - - PhAppendStringBuilder2(TypeName, buffer); - } + PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); } break; default: { - WCHAR buffer[MAX_PATH + 1] = L""; - - _snwprintf(buffer, ARRAYSIZE(buffer), L"Unknown(%lu)", Info.Tag); - - PhAppendStringBuilder2(TypeName, buffer); + PhAppendFormatStringBuilder(TypeName, L"Unknown+%lu", Info.Tag); } break; } @@ -1575,7 +1484,7 @@ PWSTR SymbolInfo_TagStr( return L"Dimension"; } - return L"Unknown"; + return L"UNKNOWN"; } @@ -1587,7 +1496,7 @@ PWSTR SymbolInfo_BaseTypeStr( switch (Type) { case btNoType: - return L"NoType"; + return L""; case btVoid: return L"void"; case btChar: @@ -1666,7 +1575,7 @@ PWSTR SymbolInfo_BaseTypeStr( return L"HRESULT"; } - return L"Unknown"; + return L"UNKNOWN"; } @@ -1677,7 +1586,7 @@ PWSTR SymbolInfo_CallConvStr( switch (CallConv) { case CV_CALL_NEAR_C: - return L"NEAR_C"; + return L"__cdecl"; case CV_CALL_FAR_C: return L"FAR_C"; case CV_CALL_NEAR_PASCAL: @@ -1685,21 +1594,21 @@ PWSTR SymbolInfo_CallConvStr( case CV_CALL_FAR_PASCAL: return L"FAR_PASCAL"; case CV_CALL_NEAR_FAST: - return L"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"NEAR_STD"; + return L"__stdcall"; case CV_CALL_FAR_STD: return L"FAR_STD"; case CV_CALL_NEAR_SYS: - return L"NEAR_SYS"; + return L"__syscall"; case CV_CALL_FAR_SYS: return L"FAR_SYS"; case CV_CALL_THISCALL: - return L"THISCALL"; + return L"__thiscall"; case CV_CALL_MIPSCALL: return L"MIPSCALL"; case CV_CALL_GENERIC: @@ -1794,6 +1703,7 @@ VOID SymbolInfo_SymbolLocationStr( _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) { @@ -1948,11 +1858,12 @@ BOOL CALLBACK EnumCallbackProc( symDataKind = SymbolInfo_DataKindStr(dataKindType); - if ( - dataKindType == DataIsLocal || - dataKindType == DataIsParam - ) // || dataKindType == DataIsObjectPtr) + if (dataKindType == DataIsLocal || + dataKindType == DataIsParam || + dataKindType == DataIsObjectPtr) + { break; + } symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); @@ -1960,7 +1871,10 @@ BOOL CALLBACK EnumCallbackProc( switch (dataKindType) { case DataIsLocal: - symbol->Type = PV_SYMBOL_TYPE_LOCAL_VAR; + { + SymbolInfo->Address = SymbolInfo->ModBase + SymbolInfo->Address; + symbol->Type = PV_SYMBOL_TYPE_LOCAL_VAR; + } break; case DataIsStaticLocal: symbol->Type = PV_SYMBOL_TYPE_STATIC_LOCAL_VAR; From df225fb30d0788cdfc2773ae08dfb4975f29e8bd Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 22:07:38 +1000 Subject: [PATCH 134/839] Update gitignore, Add sdk build during debug, Fix build output path --- .gitignore | 7 +- ProcessHacker/ProcessHacker.vcxproj | 7 + tools/CustomBuildTool/Source Files/Build.cs | 151 ++++++++++-------- tools/CustomBuildTool/Source Files/Utils.cs | 2 + .../bin/Release/CustomBuildTool.exe | Bin 160768 -> 160768 bytes .../bin/Release/CustomBuildTool.pdb | Bin 69120 -> 69120 bytes 6 files changed, 97 insertions(+), 70 deletions(-) diff --git a/.gitignore b/.gitignore index d38240e8b4e3..325569e78592 100644 --- a/.gitignore +++ b/.gitignore @@ -74,12 +74,9 @@ Desktop.ini ## Project specific rules ########################## -build/*.exe -build/*.zip -build/*.h -ProcessHacker/sdk/phapppub.h +build/output/ plugins-extra/ -/sdk/ +sdk/ KProcessHacker/*.log KProcessHacker/*.err diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 60b6c8f4be98..5e3e2d24f2de 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -108,6 +108,9 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + + ..\build\build_sdk.cmd + @@ -136,6 +139,10 @@ _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) + + + ..\build\build_sdk.cmd + diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs index 6242b16b4135..8e010b9ace8b 100644 --- a/tools/CustomBuildTool/Source Files/Build.cs +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -1080,22 +1080,14 @@ public static bool BuildSolution(string Solution, BuildFlags Flags) #region Appx Package public static void BuildAppxPackage(BuildFlags Flags) { - string error; - string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); - Program.PrintColorMessage("Building processhacker-build-package.appxbundle...", ConsoleColor.Cyan); + string signToolExePath = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Windows Kits\\10\\bin\\x64\\SignTool.exe"); try { - if (!Directory.Exists("build\\output")) - Directory.CreateDirectory("build\\output"); - - if (File.Exists(BuildOutputFolder + "\\processhacker-build-package.appxbundle")) - File.Delete(BuildOutputFolder + "\\processhacker-build-package.appxbundle"); - - Win32.ImageResizeFile(44, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-44.png"); - Win32.ImageResizeFile(50, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-50.png"); - Win32.ImageResizeFile(150, "ProcessHacker\\resources\\ProcessHacker.png", "build\\output\\ProcessHacker-150.png"); + 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) { @@ -1103,15 +1095,15 @@ public static void BuildAppxPackage(BuildFlags Flags) string appxManifestString = Properties.Resources.AppxManifest; appxManifestString = appxManifestString.Replace("PH_APPX_ARCH", "x86"); appxManifestString = appxManifestString.Replace("PH_APPX_VERSION", BuildLongVersion); - File.WriteAllText("build\\output\\AppxManifest32.xml", appxManifestString); + File.WriteAllText(BuildOutputFolder + "\\AppxManifest32.xml", appxManifestString); // create the package mapping file StringBuilder packageMap32 = new StringBuilder(0x100); packageMap32.AppendLine("[Files]"); - packageMap32.AppendLine("\"build\\output\\AppxManifest32.xml\" \"AppxManifest.xml\""); - packageMap32.AppendLine("\"build\\output\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); - packageMap32.AppendLine("\"build\\output\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); - packageMap32.AppendLine("\"build\\output\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + 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); for (int i = 0; i < filesToAdd.Length; i++) @@ -1130,21 +1122,23 @@ public static void BuildAppxPackage(BuildFlags Flags) packageMap32.AppendLine("\"" + filePath + "\" \"" + filePath.Substring("bin\\Release32\\".Length) + "\""); } - File.WriteAllText("build\\output\\package32.map", packageMap32.ToString()); + File.WriteAllText(BuildOutputFolder + "\\package32.map", packageMap32.ToString()); // create the package - error = Win32.ShellExecute( + Win32.ShellExecute( MakeAppxExePath, - "pack /o /f build\\output\\package32.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" + "pack /o /f " + BuildOutputFolder + "\\package32.map /p " + + BuildOutputFolder + "\\processhacker-build-package-x32.appx" ); - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the package - error = Win32.ShellExecute( + Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx" + "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); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) @@ -1153,14 +1147,14 @@ public static void BuildAppxPackage(BuildFlags Flags) string appxManifestString = Properties.Resources.AppxManifest; appxManifestString = appxManifestString.Replace("PH_APPX_ARCH", "x64"); appxManifestString = appxManifestString.Replace("PH_APPX_VERSION", BuildLongVersion); - File.WriteAllText("build\\output\\AppxManifest64.xml", appxManifestString); + File.WriteAllText(BuildOutputFolder + "\\AppxManifest64.xml", appxManifestString); StringBuilder packageMap64 = new StringBuilder(0x100); packageMap64.AppendLine("[Files]"); - packageMap64.AppendLine("\"build\\output\\AppxManifest64.xml\" \"AppxManifest.xml\""); - packageMap64.AppendLine("\"build\\output\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); - packageMap64.AppendLine("\"build\\output\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); - packageMap64.AppendLine("\"build\\output\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + 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); for (int i = 0; i < filesToAdd.Length; i++) @@ -1179,47 +1173,74 @@ public static void BuildAppxPackage(BuildFlags Flags) packageMap64.AppendLine("\"" + filePath + "\" \"" + filePath.Substring("bin\\Release64\\".Length) + "\""); } - File.WriteAllText("build\\output\\package64.map", packageMap64.ToString()); + File.WriteAllText(BuildOutputFolder + "\\package64.map", packageMap64.ToString()); // create the package - error = Win32.ShellExecute( + Win32.ShellExecute( MakeAppxExePath, - "pack /o /f build\\output\\package64.map /p " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" + "pack /o /f " + BuildOutputFolder + "\\package64.map /p " + + BuildOutputFolder + "\\processhacker-build-package-x64.appx" ); - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the package - error = Win32.ShellExecute( + Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx" + "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); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } { // create the appx bundle map StringBuilder bundleMap = new StringBuilder(0x100); bundleMap.AppendLine("[Files]"); - bundleMap.AppendLine("\"" + BuildOutputFolder + "\\output\\processhacker-build-package-x32.appx\" \"processhacker-build-package-x32.appx\""); - bundleMap.AppendLine("\"" + BuildOutputFolder + "\\output\\processhacker-build-package-x64.appx\" \"processhacker-build-package-x64.appx\""); - File.WriteAllText("build\\output\\bundle.map", bundleMap.ToString()); + 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( + Win32.ShellExecute( MakeAppxExePath, - "bundle /f build\\output\\bundle.map /p " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + "bundle /f " + BuildOutputFolder + "\\bundle.map /p " + + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); // sign the appx bundle package - error = Win32.ShellExecute( + Win32.ShellExecute( signToolExePath, - "sign /v /fd SHA256 /a /f build\\processhacker-appx.pfx /td SHA256 " + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + BuildOutputFolder + "\\processhacker-build-package.appxbundle" ); - Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); + //Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray); } - Directory.Delete("build\\output", true); + try + { + 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" + }; + + for (int i = 0; i < cleanupAppxArray.Length; i++) + { + if (File.Exists(cleanupAppxArray[i])) + File.Delete(cleanupAppxArray[i]); + } + } + catch (Exception) { } } catch (Exception ex) { @@ -1236,12 +1257,18 @@ public static bool BuildAppxSignature() try { - if (File.Exists("build\\processhacker-appx.pvk")) - File.Delete("build\\processhacker-appx.pvk"); - if (File.Exists("build\\processhacker-appx.cer")) - File.Delete("build\\processhacker-appx.cer"); - if (File.Exists("build\\processhacker-appx.pfx")) - File.Delete("build\\processhacker-appx.pfx"); + string[] cleanupAppxArray = + { + BuildOutputFolder + "\\processhacker-appx.pvk", + BuildOutputFolder + "\\processhacker-appx.cer", + BuildOutputFolder + "\\processhacker-appx.pfx" + }; + + for (int i = 0; i < cleanupAppxArray.Length; i++) + { + if (File.Exists(cleanupAppxArray[i])) + File.Delete(cleanupAppxArray[i]); + } string output = Win32.ShellExecute(makeCertExePath, "/n " + @@ -1249,8 +1276,8 @@ public static bool BuildAppxSignature() "/r /h 0 " + "/eku \"1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13\" " + "/sv " + - "build\\processhacker-appx.pvk " + - "build\\processhacker-appx.cer " + BuildOutputFolder + "\\processhacker-appx.pvk " + + BuildOutputFolder + "\\processhacker-appx.cer " ); if (!string.IsNullOrEmpty(output) && !output.Equals("Succeeded", StringComparison.OrdinalIgnoreCase)) @@ -1260,9 +1287,9 @@ public static bool BuildAppxSignature() } output = Win32.ShellExecute(pvk2PfxExePath, - "/pvk build\\processhacker-appx.pvk " + - "/spc build\\processhacker-appx.cer " + - "/pfx build\\processhacker-appx.pfx " + "/pvk " + BuildOutputFolder + "\\processhacker-appx.pvk " + + "/spc " + BuildOutputFolder + "\\processhacker-appx.cer " + + "/pfx " + BuildOutputFolder + "\\processhacker-appx.pfx " ); if (!string.IsNullOrEmpty(output)) @@ -1272,7 +1299,7 @@ public static bool BuildAppxSignature() } output = Win32.ShellExecute(CertUtilExePath, - "-addStore TrustedPeople build\\processhacker-appx.cer" + "-addStore TrustedPeople " + BuildOutputFolder + "\\processhacker-appx.cer" ); if (!string.IsNullOrEmpty(output) && !output.Contains("command completed successfully")) @@ -1295,6 +1322,7 @@ public static bool CleanupAppxSignature() try { X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadWrite); foreach (X509Certificate2 c in store.Certificates) @@ -1305,13 +1333,6 @@ public static bool CleanupAppxSignature() store.Remove(c); } } - - if (File.Exists("build\\processhacker-appx.pvk")) - File.Delete("build\\processhacker-appx.pvk"); - if (File.Exists("build\\processhacker-appx.cer")) - File.Delete("build\\processhacker-appx.cer"); - if (File.Exists("build\\processhacker-appx.pfx")) - File.Delete("build\\processhacker-appx.pfx"); } catch (Exception ex) { diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs index 99a974c973ec..3f3c9a5008a2 100644 --- a/tools/CustomBuildTool/Source Files/Utils.cs +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -99,6 +99,8 @@ public static void CopyIfNewer(string CurrentFile, string NewFile) public static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll", ExactSpelling = true)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern int MessageBox(IntPtr hWnd, string m, string c, int type); } public static class Json where T : class diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index b6f79ef199c0234d41ce3ff314eedc43bcf75b03..fc9e59071d2563b2d76ea3a8c6eb0350d3a977b5 100644 GIT binary patch delta 12485 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 BXwd)w delta 12532 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 diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb index 372d398db27ce85002681c32d8b6b0690eca97d9..8a6ef390251d29c386caf969281fd9efd5b8d05d 100644 GIT binary patch delta 7357 zcma)>4P4Y!y2sD|IN(5ljJ%IHz@USU@;=P!gb3bBBcpuD)a$yw1SujZpb{c8pdzv> zA&+_$k)+gGQ$)tBHIeAEYnHB`w#{-ib#2Qwuh+X-rnc4R{+|B~uZ5rc*W>q`=Q+=L zdCvKt^ZZ9ggT~RIY0-FxHe4~xQiKo<_NLFW(+^Inxz+r)({Da<+!^jYS{pd+^N#-kaoPp6k zVcrcxT&7K)WKK;AT7+Vgrx5yJA$XKV5vdcjDK#c&U6>Gqq!6`kjR_isI>yB)lD5DKoZ<-Ueip`~Kjv_e{mJlh0A>jnDLU&aOm`9NEQv;&eF zcMSt&qy^CL#zlI+35_3&PU|T*O;4xNLNgOE{SH+A8`wDnA8t+v9iTo7^RnmXuUM9` zY*}$eLBagzSr{w^=#Hbg(Xbnd^73%HjwWyH@`+;Abj;`(s4xW46y7~jKa~Axx%n|V zVAbl!3`G&oS?<~WpiR%W!YOCG!EZMj(4Y#dA*vfcHRDr=&Y-;w5M6P%%U**MdA#=> zvL5vY(2wIoXkMF#FN{winP3<`l+5YfW9L2ux`kFOGIWm}n?&d^_GytUc24Z z+~}@N%K4B{s*6(5js;S#*{Nm!ltTUk(hOBjH`7giTTr}jb9z`mKoSz3Z!ohA&1$jBrLEcvx z;1l2ka4Gl@xDuQQmV%SOP2i(oBbW&~!N5kk5O(ndGG9ROB*=Sc3OEFu3R=PG z;6(5#FcZA_zdt#i0G~$rDR8z%tlJGPMEM^e*Za!NZ@_GneX+ocQ13Bt8Mq!ShP+1F zg;BV{2sjaZ1NQ5|cCa0MPa~?I#&$dhy?r(^JiO@D{gVvk3d{EgS#}{-~!DwwZb$%z=WRFW?b= ztG)#Jt@*s^tp!^KTE0Y7>MtM2-9a!SlJKWp=-qVPx4sd{naC`!Kfd378 zg5Q94FAuS996BEa!NZ^q>I2R1^5EW~{FqG@SA>ckR9DEHlfCs?{@DLaU{t+~S z?}5X?)1V3T6*J+D-y&^RVZH`OrPDfpf~Vt=-4UZkhe z#o=sQzqne?O(s)rn#@k7oLrmCOQx4{t+FtgE&wIT)VN1aqnAX=@?^?dk|wK?sRno< zna%>+lF2X6rrnk7o zrIenp@$D%sQR@yu-6*`GopsAqsXwH{5pQ%#t5m73kMsq#-GAl#E8A3k0Q5b_xJ%Vf zdd^?jqvnc`dri&leoo^TBSxw1^QyuV-plm%P&9y3Q0T$IAZ|BI1n(Be9vKQ^ZwUi9 z0gM3oYDE){IbamH7&L+_z-X`>90u|gI|AGSjszP(Gx#z%3Ve;79*fKy5X6Ch0OP@9 zU?TV@FbRAY9P9k^vZoc9kwB&bBO|lGDyJk+MM0Tdn?SPAq+grh+6la>n3vd%WP>Tx z6AF{$mIPW^nE!BwT1&y1>3Vqf)7|-e+^7f-aUBRpFjxTcNsD(+^#Z@x=_s$Hkwwwm zZB~&!3TK{!qccPnSXbQ5Ao@^G0q)-6+*q_z(Vpsj*E%kT(nBj}%1bHKxH4aUoKA(&_(vpd#Dhni3Nu zt7MRr3T4c6}7C4s{KxKwMWiuU$K`8XSZ2 zZ)ui2f~RS4=ttH?xAQid?}$!-?=QDm+xk-?*BUuC|uji%rYMuu%eB{%!d z25#0_P-Pkj3upZcQsKIdwPmQ}G;H=%t0L#<`A^GE|?1IbaG%4Ro11+ zv?MxNXOo3VB&kH!CQ%tBFMz9|TW>6Tv@&{3*b1B+vP;C_f3F1E+xqG#t6$d2kJQ0pvjEB3KSy z0=MxMdl{MS5d0mi2S1~)wmxmIO=8y~K!SuHJ{mt)b3$B0O&;=>Aj|&=^5R|xhl4l3 z5#axTX7Fn;7UZF#S@8|Z48?Im)tm44bjm4#Dx&vu}7#lW@Am6K@RJ7AyM#fT2VoOOP6=re7v}@N;5@IeI2gxK?Jge~6^90r~Gi0S|cEZL--y?*m6I+ao_F3f_3mpYMvd~QkFIvd_icS8*LOH;7 z3%vx~ve4yMtnwbZdDSWvtkkPEsZF73z(0jf0wF0Re{bYb<3JBf#}fC75!8_>Ld&t# zVAh&&cd{Qi2Ek<5=s)@x)%G%CZBHZ2nT;NSxMu|6@(x zy?SD8XQ;u$gV)$-av!6%UPi3#X~fQj5x%A!kz!ca0M7I>Vr|c$32VScQz4EPF; z!lLI2xO%V{shZ^(oKw%A7_1-wSr9<;u4_E_6aFlI4-WC*FVt0(cs;}}aC08SboxGu z2wQP5N(lA0YA6+P4)>$$VI6xtqpRFo$Nrz%k8THaCf-?i(B{xyjke>}uX7KEJEVCx z!=0fwCVP5)fC2L}#<}#?4#gg}VhV2hRF~{$W7`B>np-FPjGF_X4+prEeRaD=3GsYC zcEO`v=O(N1D_idg+n`+1kKtbER=afr`q=S3is(aq`s(<)T-uM$&Q~D<#a}RNEA+?i zUFp_oL|dUd3oo@5@?U7Yz|TLDxVXx%g$_JBOQ&`?xWk*ug>09$Ulhm zH~T&J(eaNZ{n_ykCZ~SUqe7J6&g-=EqOa}@^5B?DOIPoNNJJ)=@8~@^5^)I-b+CY= z43_{A1q(QWa0w7Cuz;fjmjICf3;5=D32@7^fNyn|05>=b_{Me#INpMQZ)2ALH!lnL zrgaH$tFnM^QB~j(f!shU|31Wnf5?jEoceRu_j-?6U8;G=1+mJ&A8pgz4&jf1&WBG! ze7xU;*(KHNYqa<|a^A$bn_aTo?p#o6*oq&J?>ruS`@B4h@#{;7yY=2Wd>vS+_1|WR c|0%ruZ&&-+U!QG^BH)&cmfzf;3HO^3k5C^6nzxj(%1!J4+)2dbuI_2ky4NTRApES_x`$zmlS%oD{RZ8I2vP`sOfm$_8Dz9xiv+FvCBN+ zi9s=eV}^K5yEJ6UvD<=>?a~Ngh!lc*X%O*xE)XJwN=`2*D_dFynxI4VIm!CnF+vE?Giqqep=cqRP`U;7qocfSM4}!)vW<$0$be=W z;tSA@_i2r^XH*!Kj*5@iW)R{Xv{HmRxNm0I`O}*@$%+oGBq{-F3;STd6(!qH;-0T0 zw-CrJ#0-aKGvan=wJ6E;7@-|Vb4H~{T!!W>Bne=*CWv6qG)%zE|^OcpadYIW9i@+165fH46SkAhTrzJ&p;9F$XI$Z z&tzT;iz*ah^+=?-Tik`;B0U!JZK|9O2&U|nf_%N6O zJ_3#d$AL4!0&o%dC|Ctf05^jZ!R_E<;LG47uvG|`n2ba_1cl&v@Con=I2HUGI1T(7 zoDTj7&H()}Ei=I|a27ZW{3SRX?ELzkn-}USuJ8nM;|E<*k+$mQPi@ngj$#1aVp z20^JttdW?d<;X{X&mo@yu0sA{uo|2Q)__yMTCfOQ1Fi(ug6qIKa0~c6*bLT#Z-DE; z6W|8$ci=|wk_(AVNL&XSz%M}_!EJCe^8Ww{Bgn&YG$FqZH*d=YF${#9@fI(!b? zkNjugYsh~G9zxz9C&Upj3~U4AK@aG{`ii4SV0y(da3OdChUm38h5SzN4D!dp-yr`! zcn-V_{vP}Ud=LB-`~dtr_#yZ;cpe-i8pI$`inHh^B0`J-=YVs-8jczGS%c_=a0=oq z#A3wRh(AMQqeX}-eQ_0Sd@^wqYlk2{)x-a14L|+Y8Y1x6M~Htz-0O=odus1)O)q{* zzj!LfH4Bf6_s~#!4inzzlOX4C5d#kfFM+&iJ_eJ(D}3gWnL;gEZ z_zAZTom9XeZ~zz#YQPXs3-V_22UEa6a0ECI93-Y=B{w3*tjCJwV|McUlU|zrb5}SF zyl0o9o@3l@1R^)gL66FGU??~k3nn z#o$n|0wZUK`rY3egm zxvrLHvID%Pcp@obO<3c?tbm8r$`zrdJqyEV(!vn7Yk3BaXkjed8mAfP=)zLzw2`?u zS3Y5*qGE@fWuv{t*>awZt^&n2YTjp{)J5^~SsOVQ<;n^hH2^LfeFW6mD0s0$w+UWI z9sZs_h0&Lb22ttaXt~u!8=%|Styz_P!*Na>MYLe3>2}Ip za*}&hU1A6wr10JuF203^p9=J{y2Kc4!eydcgdTIr!6<3O9j9B25kO2`C7R&gVvLAO z>rpyE4C^V4(OH*Tcyz3VXMvkb>Cs}eT-WZUAv|JEo=0+%jXIVZgL@amnAtuG_Pu+( z(<_dKI9ZHPtL2r&Oazu1Tn31>7+WkD3dVu#w~Zjr$WSmFgo!&JOaLDPO(35aiQqyo z30whU3%hGTGq?$~fLlRqWj;SdD#*tN{G|I0p7eAi`25HK`RZWbvwEKdUR4ZN>_+6X4(j}6cDdF{<;zMQ zv8kmLY*ugJ(Le4>mkJz4NSA@Qo{HsQB3KUcEbuuo2CSfz<%!&^bGad*3Q9eM)nEeX z#(zF)xiB|h>sh~ir=mO2z2Dk9qG@dTba^3@n#)V%K3!R7Lo|UeyYLvO@R8nO!X9^L1S9+2^@hPg%X$9~Pb+zm^J@ZQfjs zLLTE5ts<|dley6{=(=z9)R#Nj(iln08ujvSI@LAiN^1t4YjntY86>Gvx-+Pn?2I;Q zkq0wq?pCX8%b>=s2f4P~7Ywe`n2~|#bGvV*euK;FP~_i7J{kM~OaXBncRvJP0LO!W z03QW;Pw|-l1Nlkd6>tjp8CVQn1y_UDKwPlJpTQasHxc)X@cZIFkzj}XS8xY-oo>D` z&Bgmw!M~3n;lL;1bv32FXQ^Gj2@96r0(o(7gZw(u2@VIp1})$}z%-Vlt9J0eksk%# z0r~ajF8CbyPYnHa@PBbwx(^}2hIn#Ro&*QraMg5=l{q7W$BDpO zn#UE83S%p6-W4SEsi?p%V^U#UOgYWby24ant6NaPs%E_`Ohv0xWw~k;%vbsfKfL&$ zo@zEWQ^CtYvW_d58~81x?;>I#XJ0I#XX|FI9ywYd*(_zt`!dHw$oLh-cFJG9f7~TU)H$aK+E?>%a`ob zu-_q@?eqa~z)so&4*8~?@_}P^sy>h{Pub}>@LM}|LinMbEU!A`$95_LuG(oY@K-x^ zyqYa5i-RE+k_Dm5vzDb3$)+{VN zmjeujjUMS|#19@n&t4y6IMHq!0*2_Eb zWIpVa0~4tHaHVc!LNE2eR=pgVK*rWcIW2**TPx-I1Zr!wFgjXG(b*4(9f|7SzRFn3Z zF!5Bj??wZ|J+YVe`6*+m{Ys*NzuqY#BRd1ZHitig4&#fWSQ|_Cu9)m=akJ@B&t=E% zRUeIGxpm&W!HI{Uk$KB>%PDZ9ySTli23FK zB|X&{=ZU^OQ5z7=Zx{&Ac$R#-U2&}td2pmTUKyA4rP%6P@@nXEeG)0!Lc5i$<15i6 z_#Y1ab(?UHSm{;$i%tti&F2#b>FQUh1Lw_AR zt0fQ6x!5_jJV410Ub#;PC)pFYL4LN2e3JgUYtUhG)X-n2SaE5*uj6}?H*lr=dX(?W z`}xRV9lv08FH?WrVd(g6hlAD&^_T7VJ>&_WtiLWd9rwHkDfyZ``2mJ}F>RvuVtih> zqw(V#+($h*rw!L7YTp+Yz|B9=CpW84ZeE{Uai83?eR36ja;`qPx;{DnB5}XO1Hy0MLD^dl1gOT#5 znjfP4_)}0Cr_>KdKN$QT?NLoaDuPu;eg~T3Ti1a;GI$p7>e~sk3#!>SsPQsVfyAkf z9@&lVl;bv1-8*Ygzp7?r2@&O!_1EFcSlf>liNAt%{AkuZ@Gou0VwEI~GXJeSjdCX7 F{{a3D`?df8 From c8db767248cd2d6d68e527809e6801e7f6900115 Mon Sep 17 00:00:00 2001 From: dmex Date: Fri, 19 May 2017 22:14:51 +1000 Subject: [PATCH 135/839] peview: Add missing debug directory check --- tools/peview/peprp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 73ee2331031a..75ce0487a33f 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -174,6 +174,7 @@ VOID PvPeProperties( } // Symbols page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &entry)) && entry->VirtualAddress) { newPage = PvCreatePropPageContext( MAKEINTRESOURCE(IDD_PESYMBOLS), From 29ee346f5379a2b76799d4048cf100307f61e0d8 Mon Sep 17 00:00:00 2001 From: dmex Date: Sat, 20 May 2017 22:50:37 +1000 Subject: [PATCH 136/839] peview: Fix symbol tab crash --- tools/peview/pdb.c | 123 +++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c index 10d74158e9ff..7fac14523a91 100644 --- a/tools/peview/pdb.c +++ b/tools/peview/pdb.c @@ -920,43 +920,43 @@ BOOLEAN SymbolInfo_ArrayDims( } BOOLEAN PdbGetSymbolChildren( - _Inout_ PPDB_SYMBOL_CONTEXT Context, - _In_ ULONG Index, - _Inout_ ULONG* Count, - _Inout_ TI_FINDCHILDREN_PARAMS **Params - ) + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ ULONG* Count, + _Inout_ TI_FINDCHILDREN_PARAMS **Params + ) { - ULONG length; - ULONG symbolCount = 0; - TI_FINDCHILDREN_PARAMS* symbols; - - if (!SymGetTypeInfo_I( - NtCurrentProcess(), - Context->BaseAddress, - Index, - TI_GET_CHILDRENCOUNT, - &symbolCount - )) - { - return FALSE; - } - - if (symbolCount == 0) - return TRUE; - - length = sizeof(TI_FINDCHILDREN_PARAMS) + symbolCount * sizeof(ULONG); - symbols = _alloca(length); - memset(symbols, 0, length); - - symbols->Count = symbolCount; - - if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, symbols)) - return FALSE; - - *Count = symbolCount; - *Params = symbols; - - return TRUE; + ULONG length; + ULONG symbolCount = 0; + TI_FINDCHILDREN_PARAMS* symbols; + + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + Context->BaseAddress, + Index, + TI_GET_CHILDRENCOUNT, + &symbolCount + )) + { + return FALSE; + } + + if (symbolCount == 0) + return TRUE; + + length = sizeof(TI_FINDCHILDREN_PARAMS) + symbolCount * sizeof(ULONG); + symbols = _alloca(length); + memset(symbols, 0, length); + + symbols->Count = symbolCount; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_FINDCHILDREN, symbols)) + return FALSE; + + *Count = symbolCount; + *Params = symbols; + + return TRUE; } BOOLEAN SymbolInfo_UdtVariables( @@ -968,7 +968,7 @@ BOOLEAN SymbolInfo_UdtVariables( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -976,8 +976,8 @@ BOOLEAN SymbolInfo_UdtVariables( if (MaxVars <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Vars = 0; @@ -1006,7 +1006,7 @@ BOOLEAN SymbolInfo_UdtFunctions( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -1014,8 +1014,8 @@ BOOLEAN SymbolInfo_UdtFunctions( if (MaxFuncs <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Funcs = 0; @@ -1044,7 +1044,7 @@ BOOLEAN SymbolInfo_UdtBaseClasses( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -1052,8 +1052,8 @@ BOOLEAN SymbolInfo_UdtBaseClasses( if (MaxBases <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Bases = 0; @@ -1082,7 +1082,7 @@ BOOLEAN SymbolInfo_UdtUnionMembers( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagUDT)) return FALSE; @@ -1090,8 +1090,8 @@ BOOLEAN SymbolInfo_UdtUnionMembers( if (MaxMembers <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Members = 0; @@ -1120,7 +1120,7 @@ BOOLEAN SymbolInfo_FunctionArguments( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagFunctionType)) return FALSE; @@ -1128,8 +1128,8 @@ BOOLEAN SymbolInfo_FunctionArguments( if (MaxArgs <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Args = 0; @@ -1158,7 +1158,7 @@ BOOLEAN SymbolInfo_Enumerators( ) { ULONG childrenLength = 0; - TI_FINDCHILDREN_PARAMS* symbolParams; + TI_FINDCHILDREN_PARAMS* symbolParams; if (!SymbolInfo_CheckTag(Context, Index, SymTagEnum)) return FALSE; @@ -1166,8 +1166,8 @@ BOOLEAN SymbolInfo_Enumerators( if (MaxEnums <= 0) return FALSE; - if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) - return FALSE; + if (!PdbGetSymbolChildren(Context, Index, &childrenLength, &symbolParams)) + return FALSE; *Enums = 0; @@ -1262,7 +1262,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( { ULONG typeIndex = 0; - // Yes, get the number of * to show + // Yes, get the number of * to show if (!SymbolInfo_PointerType(Obj, Index, &typeIndex, &numPointers)) return FALSE; @@ -1327,7 +1327,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( PhAddItemList(Obj->UdtList, UlongToPtr(Info.sFunctionTypeInfo.ClassIndex)); /* - // It is not needed to print the class name here, because it is contained in the function name + // 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 += "::"); @@ -1353,7 +1353,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( // Print "this" adjustment value if (Info.sFunctionTypeInfo.MemberFunction && Info.sFunctionTypeInfo.ThisAdjust != 0) - PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.sFunctionTypeInfo.ThisAdjust); + PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.sFunctionTypeInfo.ThisAdjust); } break; @@ -1373,7 +1373,7 @@ BOOLEAN SymbolInfo_GetTypeNameHelper( // Print dimensions for (ULONG i = 0; i < Info.sArrayTypeInfo.NumDimensions; i++) - PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); + PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.sArrayTypeInfo.Dimensions[i]); } break; default: @@ -2299,8 +2299,11 @@ NTSTATUS PeDumpFileSymbols( PPH_STRING dbghelpPath; PPH_STRING symsrvPath; - dbghelpPath = PvFindDbghelpPath(FALSE); - symsrvPath = PvFindDbghelpPath(TRUE); + if (!(dbghelpPath = PvFindDbghelpPath(FALSE))) + return 1; + if (!(symsrvPath = PvFindDbghelpPath(TRUE))) + return 1; + dbghelpHandle = LoadLibrary(dbghelpPath->Buffer); symsrvHandle = LoadLibrary(symsrvPath->Buffer); From ee84c5e84c542707ce602ced6fdaff0f9e29a38a Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 21 May 2017 05:54:59 +1000 Subject: [PATCH 137/839] [Tools] Add delaylib --- .gitignore | 2 + tools/delaylib/bin/Debug32/delaylib.lib | Bin 0 -> 20588 bytes tools/delaylib/bin/Debug32/delaylib.pdb | Bin 0 -> 135168 bytes tools/delaylib/bin/Debug64/delaylib.lib | Bin 0 -> 21430 bytes tools/delaylib/bin/Debug64/delaylib.pdb | Bin 0 -> 135168 bytes tools/delaylib/bin/Release32/delaylib.lib | Bin 0 -> 226114 bytes tools/delaylib/bin/Release64/delaylib.lib | Bin 0 -> 225886 bytes tools/delaylib/delaylib.sln | 28 ++++ tools/delaylib/delaylib.vcxproj | 184 ++++++++++++++++++++++ tools/delaylib/delaylib.vcxproj.filters | 22 +++ tools/delaylib/main.c | 125 +++++++++++++++ 11 files changed, 361 insertions(+) create mode 100644 tools/delaylib/bin/Debug32/delaylib.lib create mode 100644 tools/delaylib/bin/Debug32/delaylib.pdb create mode 100644 tools/delaylib/bin/Debug64/delaylib.lib create mode 100644 tools/delaylib/bin/Debug64/delaylib.pdb create mode 100644 tools/delaylib/bin/Release32/delaylib.lib create mode 100644 tools/delaylib/bin/Release64/delaylib.lib create mode 100644 tools/delaylib/delaylib.sln create mode 100644 tools/delaylib/delaylib.vcxproj create mode 100644 tools/delaylib/delaylib.vcxproj.filters create mode 100644 tools/delaylib/main.c diff --git a/.gitignore b/.gitignore index 325569e78592..53afad1c8da7 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,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/tools/delaylib/bin/Debug32/delaylib.lib b/tools/delaylib/bin/Debug32/delaylib.lib new file mode 100644 index 0000000000000000000000000000000000000000..418c0549ec1d5c58d058709cb9f229f93ce16b8c GIT binary patch literal 20588 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 ip#?$+L(MY-$D`r_vg? zTZ&7Rn%|(*y(={2EEFB8xzoZ+*mxUI*>RbDr%S;rn%j%5{aL%E31j{*;QjpL8ZsG0 zi;8YoP=$9o;D52w?xo9?X5dxL?;bS$58z$dp>Z%()zOUkRnhwi;CbLgJQ{Btijn9& z4E4JMI3BOYYeX>;y_*2P9XOMH8V}t@r1uWsGl1jvYdmLu+j~>Rl!oz><1DvuY-mSw zTgQTdO1ueZ^Z>8h!leiWZv=W<|C96z!24`9y?o`khcV8*z`42$6Zv!2F8o)?oK1Y#8PCW8nSV!qw}w@Di1HzX8s>Rd_J(kNNyvo@6Ky z3U;oToh|nE^(}y3m|Y1^=lavdzV<~UG+k@v3z=MYgceZrb|-vQq#_|-*wgKg&u*bI zzJ>o?`1dQ98-+SW$F)LT?p7P>)C+2|YEn-^!|k#=lg{S~xlP5Ed9nE|k%3~SKeH17 zq{W--AIS8Xc|Dig;rf@URWcXOhZmg&eal;D`(?9$_%TA{Iaf_;-ar}DUP(75w89-l zYsBSJla$*%fO;MtDB|*Gi}@XDI$+xYyBA|bT*3ZN8#or?xK;TIB2zKp6^PLvm;ybI+eD8GU7Vr$*EHBEdQC^9%7iAdb9+Z&t5Cs!M`7M-j zlrN%0lpZ46*P#3@O6cRzc+CBiD9KmMacCjRPoW$_xe4W$QTC%efbtrYkD%O!@;Q_{ zQ2ryz>ruXn@&=S|VSdZ!D%GyWx!)GH`GWCi$g|EH33mlo8F8;C914d0MkMC*$AaNi zMlhW4$3h;j->1$&ryosX&exsV9?V7anOr_o+>zKZU>4L&G|mM@_^4~4?wzSZCLPRf z%H{i0Mfg1^|2TIe;!eGa=C+ynWWmgXN~V9XU!8}hMbfoYRYv#*| zbMOTqUZ=WpNPvrp{9vKz%P~D-NK+#k!AbSKH zg;MD3vxe{#3!}lt;P?gMCOeL^!IGOs;R#`T}1*_CtJp7C@WXH9O~lEvj1v#c>@+88pm$VO(_mbc>+ zInXQ{rKC*G6ldFQ{_SRZuxNJmr8bw=eSzKT-I4Ay$rGpJT)Qa>5!gBk5)4kH%Yq`bLoAqQujE7(c_FQ5t5os?j= zXRXoc3H#OreTe`(itf{J(muYpH)5pG(v@n(F?HgEUU3h z@YAfOsa8`-vNpwOR!fCosgNrz(@xXV)J$kDFy6(OSZHpe)f+yCG_$>CZ_O-PfqPdo zWtfN>UQa00>G7^MqOo8k7EG)&!jV|FC!{!6V%3j~6%317Ao7oMw?Xaels6JHdi=3? zFcLPpJy-H>5j@s7_XIF+oJ!=jn%Q`6FrPMI{~Fq4LE)-0g<>XMP*bgz308~cA9}^h zk8U-A+`y$gg)~?W(Zt~=k^CT;r9)(6o${Hrt&>%w`{Ur=+PJ63 zZ*-CK31lXX?iBzU-ek-djDcH?ZZGfsYrXzxg8Ys7!EGP1u|{`?bWhO9boo7rWXul- z-RNdo7!7uNR{4!YAQ@h5_&f>ElEu>Ymr#^wG++cg@j!z92yQ76TZg$eipY(T^a2s8 z(LDnzF=C03;ai8jL(nTE;7aJz=A->Av`0s7#~83~IKxN`QK`}WCMS^y#QYu~99SH* zf?>@7EKmkJXLMq+4C=c9XRCcJdwWBkcpN&{C@jYcWekrb6UbUb77Q#lmC~q_&}XsDp3Y#R+Y^NoYIM&hEEx`ZBR;3I=`9uC5kBB_6F6tM8qA%UuhNz4O{A*u? zIM?WYh77eASIbe(@6#Y`#Mi|W{_aE&u9owo{upcFhRYP_G{CtirACLLFu>XUk(5WOSDj5Tz5l3o6FH|;Li?1B1#6-g@_OhE?rArCg{3SQU#T zqj0T_?ru&y?uV%aA~F96Zh&jMfN_1tZ(-_arQSCY5EQHVOUQ5`~_7+fl~bd^6QJ`Z}SqK+9@iwFg~gB6M=UVRq$ zxh=gVm4`Od+5Ri71Wdmw=}*D~tnr{vXDH%bEgs?(3V*df7WRjXWE?W3=;LYuI#P{F zuFo2C6g{FbKLk=&B|ScbPt6riI3BdU5hW2JGfpN6s>Ab`ZXg(D$_C3L=eb!!MrRv? zf!2iLfL!SshA0l$B#eVeTXBYAi*9iQuUN3eprvMXCW9et6Ok`By0>wPI->df*ieQd zh#WqD+#3tRS;oW@%qEWZobgC9hFQ)*Pg;@f7)LQ8hRjb{W82g6A;@xYD&fDP4S{r& zXvGEn)RN^I&uYX|`W+GZ5h}k|`xLIC4w91yiD@T-$i5tF^7%WHtC0ORO2TBVqSnM1 z-I_sUl`m){{GE_}0)CaGmryAfd`BTY7dXt_%RE(eHm)7jr9gb2%&69Cr0NE`jG|~v z5V!%?%IblS=wUt?jrrq#Dh%hyJ#tCSYFEdIG?$#TgYO#^T{<24qa79ne6amCDlDNZbe@*}-TukQNvSeXaE8k?U$P z@pu3c9cqnaO+3WkW4cxv7wnEQo9RS`j$>Oz|F&3^OX3xod(dJ~$%nCKbS1;$uO-}1 z0l})&Lgs_65+AUuhZH$#weXWcq@5Ip5R2n-Xh9P$J2urb3Rz_~qCPWd@M%+r2MtUzvI>we|#DdsV zK=*OJX744~ol)IVUTWe^SWW>uFj4xKK^xx=SH~P?2&PQFEPJFmT}OO7ZE$2svX_8t zh|yI7F=w2~<}hROVF5_#)J#$@O6OvLq8c`G?mE$8lx_WtTHJWX%z?fXzE}0;@`2ub zco2tYnY7QusS{4IIFW1U6xP5;Qp09rB0dRBlsFG4|H$#A$x1i-$B{23WD~?~gcU0)_7QZ4mw~XhGHel;Z#>2+juat8k4~q~B7sZ_yENAre)l2!Y}3V(({XwFs+BiRe*Igix37cN@nk$K+0o+Io>X)G54RhY&f z5Ad8)L`4F+Kdpktw{{j-8#d@y&IW$8)Mt=@jPE{%v2*Z3ETp2c)IPs|s3N*=u6RJeaUqP`;56c3fUgRd_$05Lv9X@1yhi z2F76;6hK~rAl;el>Oyp-Mm>d=N=u}>I0sO$G%3eim>CYMB4NF|#x~m@67R`#Bpo1Y z)y(#`;z8hTd8Y8Z%!LPnb1k%gu|Q|qP!zU@_Icn z0`xmbz>$?mxAQ3MQM-{B6j3SO+A?|{#O!Uq{%R(Pvl&Ki+7hjG1Y@PbH&~L;0gG1C z0zZ-TB5{%Abp|IiTKFM>9*cnNG{w>{g2hC|2&sv5J0ULu2f0n!c0xxh8nQTOvkpm_ zF6opa2*atn3R+AeXsi4_OBv7vp|xB~zS}vRWU&)iZ8bVT0-51V&4kuPGbx!D1;wuttbHF=zVQ(78uSdiIA15!cmfCX#`jK}* zdbQ5uBp=p;bNTQi5`4ONGkWl$@0|)1>x`0ThtR*EH!jA22H8o#KN4|<5SD|^GDLFx zp0dU0BVYLi~BYaKYxuA)k%j;VZ`rR z#GN0<>{j2tjPMF zf-Z`QY`@N9!C7Mc2-Cu$h8#}Qw8*HnoGK4fVk4Z^GBk0;_CeVwOJ>ZV#f73`Ks0(e zXvW#0?1>1ti7ZCC9Hog;%*#1td%e!4hN9s)t{mgCA4W$kqN7R1Wf31EJIao=X8?%@ z3+1VJ*|`E$Z!Wu_j~;~y6QMl)tvpoVIvDIMO&}AU19Lf&;#UK=S_`Q-{6^-712MP( z>&sV}DfidWvh6jO!}BZWxPb9!w>7tlFG?tw9`+thFhtBgm<0n-}z8IkAVWKz5VsUk% zN0Y#T9x~O^VIW=Uat>jooweB>F_v<&qhip9+lR;Hn`rtwZiL8ct%?%dFaxDiO`63& zbMP83&283~tgfZQK>9pqL&3DcIf{T3*oAqk@DBna8gk z%49|RrK0yYGP#X|n>CZg`N?hGvgnRy8SmkP&f7V2O{-T@gv^70zYd)Z}10K{ks3cId|>3e#e9FD)oxM zzwr6ZD~E4=_3ks?x_ivqJ8wCHAJAZcW>?*Pnc1zM`S<11-<$59^39DuLVhmrBfq#Z z_T!n4?7J;G=Ga5cOOGkFPvBqs&0|*wzIW(PU2p%Pd-9(hJcJ)w3cR`f&=pIvW7Ge1 zBb>M#Au7>(Qbf%X+nEmIaDWx(3 zzvbwitIv9)r+?<>#sn@o@P1IKzZQ6|f7y3iT6))+g=4;Z{^!G>|9gS&oOIXszIV;^ z2JP1|C9JFY6QG%cHJ}W>N7*Go*VXbp4I%=?!V69_s{~re9w1pn;Twr+cozt zeR$saKf{kKzb^2|;}3*?dg5!r-#&Qf-Axb8xflE#LpqmCnDNT@h96jR;g^D^J$?Hz zxVq&6f9blHpFQ#J=ib>deBRWr@BJj;w+g&&d`t2Nhg|i?c7OBcnA*Kwspkbg{i*Bs zesKQU@7%im%ExYb@{#A1nu&MuYKp@ z@dM|+{@F|4`x~Y16!@MaZ|wN|OWk!3bRRO8PFd8V)YAg*yzbfYf3ow=-%bANZI{gT zy#Jn3{~_?P-+pG-g}b{RczVwJvv;mI^nLtpbsWdt`QrZg@dvJWwf@}Kx|Yq~KZM_Z z3jC{IzG(lV8Rz6)8~)(<<u5pYadr)A~NOde>{?Zy9KvAN`6_Z$l5?L7Pg; zQ!$(Kss5Ht_{n#nW#0B>OXjcNmdWI2 z(6)XMdHMP@eqYJ_yx;6k59}}s3%01<(Vw&#*un>FY-$5rviOb9wPq&Uhi<1ll|r#M zllw5^=gmzY#ke*Lw(*B!+07qj2GoVLeZ#J_=g>Dg1mbeX5lDX^$K63*%EK z1T)R$cYH*gvV{u8|B#k9u*Fe|U?vG_se77M)}Zg?$?lZvoH zhqm!2?0;;CiXP_nQ@0jtg|K2Az~<Ybv-#D=UaA3c8 zVB?V&>TyrEYE9~F2R6fjEp}kb9N5Fi)w*RJk6N`R{4iaqPpBzVu3mA^q31X6@A~d} zq2s$YxR)YL{x3dPXFny3$3Qo#w*q?-t<8Zwq1MJVfg69l0b~QiA3T8sSAFX_TyH-V z7~b2;huQ+em=lWjqk%oUTlvV_-}zcw0NyRl!yOHQeSE@J1?ry(3_lgv<7=H)YHX}& z>?k!(sA-Ir8k=hx*9P`Yk%5Lot@GYs?dHRSQ>79Z?%05TEXdco?AUJ3hDawgK7oN< z*S9WH#c`ax>rL}%U+X|X(DX9caXSa+GkaXD{fziNb$N_9Tv!U+!(bal^@Wc42!X@4>oe zftRyxuh4V2_t`S;y?F)K4hC4PyVb?}Wt@3O05_N6X7J33--Il|sMBOcmaKKZ_X5Mu zS=ypGH2gG)QF{aJ&uP$&N2&G3k&7`?Jk17+!?sN9ut9BgYPUJH`<>e3s73MLpq|1f zA7gJ&*y7-fk+uJg&vT@<4)o8K+B2xllp6QBv~}1we20cjLruf(LQVJJK6bj`{Svh< zsr?GI7OBziv`VdGjTsnjnA3(E;|Pe#L%2OC7gas@N*hHlI7|H~C<7DkYk=}_bF7r+Y1*~;md#52nc740`0hFrPPK{?Z05jE=LWA8xTiBPNI_+&e zh-R%h7`EljS%N?tdlU_{v1$}pON+Xpq5bfiW2lo`0>h)|+7Jko=^EEy99f7@t!wL0 zn}PoZRmA7HQoGBkeG9b}0{ad=Tcq{^KDEqkF<%0VKH|)Wiw~mCv+9V;*VpGfBK0Iw6E z$@@F>t=yMAdrQ*xMSeXBJ~pAmv58!MrJuRI66I<@LMX3B2^$@{9%UTmXHde-h7O?Q zv#wu7X`p-%WeVjZC^w>{+SAIl+Ovkd0OM)c46D|p<~uM-Rpa>_SipgmTgI+m!$p4Z z!~_s^%z_>gIvP;L1$2vnH_!-24CSV}c1(Oc)Y`IXPvgYL0XcD4JnE)Bp1L=uFhHR5 zradmF=BblDLMN~%&=}Ztv`y>qR1j^zpLREQqFh>~hc^LsKK{wC10_AIedh~xunkDH z7k$o5h`Iw!{M{7v4o|q@VQ@Y`XT33oPpb#euJunp8fG<>+!Yb*i}M-+H=(cUatrv| z1~tUF9!Gf*%C}I`KHo-}MfomDuH)ZPQl2yLujR>FlX}>JQQbAnRp(N51GvRS7inK} zP^lNr=~^T1IcB|bonNOW#!izyoTYtTfQs&+YwSTkQm-7t5TgMT_;_yln*?kUR=EWw zd)wDCRGRIZ{|9AV)fgO~|1QZYmP(DDZ@JXY?xtdz#0(l2?w~`Bddw_kBg64v*HR2wJ*?@^_w%^mV0IzAXyGS_;pCDXz%@#-p z-W4*iAk+I~s=>u1v|e>=GI7Av*0#SM?-3A4`Rjm4quzBx8690Mf7K4xU+?0}fX=GG zD}N0TmsIcCP|?nt@>lWD`_#L*`pKwGe{AhaVpQUc!F|2Uw!h4T>kjB7sC+*31of_a z@JWii#^A4#4iB8`UDP+${_vwq|EiV_d0p?~OMh77HLixvctGo2H{p|eO*8tYy1 ziQ0=(#(cTd%k5$xZL#$gV^JqdW4(F!-rs2{6MK%;OK>X2IzxKdV;#C_rn}T@rPWJt zRQbz~CQ7fv{KWrj!886&YbI{gTC11f5TLk|Yerdu+zm)u1>^}p9(5q)JY>&$*O~Yv z;r=gYZ!V4XDnYmer(#{y&=L>-K0WQ;Qm^-|UV>AxF6Mvru7mu<|KI-Vhwr1XuBlpO zB}ZhdQi}nh%%zt=t^$N}DM508>~|pL>!Ob-t!w=MF_;2b*XOLU1gBzMf^j_A0$pivCsWn8iZDzz441 zS-k{D_p)9w#f6R5%dJ_7=k|a@GHR{`VZBZ*$6dva(lGFvAS0H_6iom2w23!0;RPQW z$d?m^G8^+LymUxI_|tt0;;})!`TyLj14rKAjPP0~{F1|Kc(<78t??q>qSuqcm*@iH zy9n{h1ykR}w+t5XMItA=Dy}y>dFcf}0q^?YJ7tVC$>2Mo+}4b#S_iZI^0BpI{jKuO zwJMl}Sepx5*tHEKuC5iLX6A?Eo0GTW4Iv}FfRX*Nd_H9|Izkt|U^RVwzns0k5osxF z>LWMSilJ=oCuv*=X3Mc<*&89tXFYOj?UZZr?D>w+QvP0?5AmwV3LSE+P@uLh36xe= Ju0mRQ{x96H!7l&+ literal 0 HcmV?d00001 diff --git a/tools/delaylib/bin/Debug64/delaylib.pdb b/tools/delaylib/bin/Debug64/delaylib.pdb new file mode 100644 index 0000000000000000000000000000000000000000..4f5770e5944b0847f1b65f42e03d24b05b22a533 GIT binary patch literal 135168 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