TerminalShell: improve speed on Windows

[NtQueryInformationProcess](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess) is an internal NT syscall which may change in the future. However, unlike other NT syscalls NtQueryInformationProcess is well documented on MSDN, so I think it's safe to use.

Boost shell detection by 0.3s on my machine. Note querying PowerShell's version `pwsh --version` costs another 0.2s
This commit is contained in:
李通洲
2022-10-15 19:51:33 +08:00
parent 54e271c985
commit 5749a8f7c1
2 changed files with 75 additions and 17 deletions
+5
View File
@@ -64,6 +64,7 @@ cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR WIN32"
cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX OR WIN32" OFF)
cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF)
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND AND NOT ANDROID" OFF)
cmake_dependent_option(USE_WIN_FAST_PPID_DETECTION "Use internal NTAPI instead of querying WMI to get PPID" ON "WIN32" OFF)
option(BUILD_TESTS "Build tests" OFF) # Also create test executables
option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds
@@ -523,7 +524,11 @@ elseif(WIN32)
PRIVATE "opengl32"
PRIVATE "wbemuuid"
PRIVATE "ws2_32"
PRIVATE "ntdll"
)
if(USE_WIN_FAST_PPID_DETECTION)
target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_FAST_PPID_DETECTION)
endif()
endif()
target_include_directories(libfastfetch
@@ -3,20 +3,68 @@ extern "C" {
#include "common/processing.h"
#include "common/thread.h"
}
#include "util/windows/wmi.hpp"
#include <inttypes.h>
#include <processthreadsapi.h>
#include <wchar.h>
struct ProcessInfo
{
uint32_t pid;
FFstrbuf psName;
};
#include <chrono>
static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe)
#ifdef FF_USE_WIN_FAST_PPID_DETECTION
#include <winternl.h>
static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName)
{
HANDLE hProcess = pid == 0
? GetCurrentProcess()
: OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid);
if(ppid)
{
PROCESS_BASIC_INFORMATION info;
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
{
assert(size == sizeof(info));
*ppid = (uint32_t)info.InheritedFromUniqueProcessId;
}
else
{
CloseHandle(hProcess);
return false;
}
}
if(exe)
{
DWORD bufSize = exe->allocated;
if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize))
exe->length = bufSize;
else
{
CloseHandle(hProcess);
return false;
}
}
if(pname && exeName)
{
*exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1;
ffStrbufSetS(pname, *exeName);
}
CloseHandle(hProcess);
return true;
}
#else
#include "util/windows/wmi.hpp"
static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName)
{
if(pid == 0)
pid = GetCurrentProcessId();
wchar_t query[256] = {};
swprintf(query, 256, L"SELECT %ls %ls ParentProcessId FROM Win32_Process WHERE ProcessId = %" PRIu32,
pname ? L"Name," : L"",
@@ -49,20 +97,26 @@ static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrb
if(exe)
ffGetWmiObjString(pclsObj, L"ExecutablePath", exe);
if(exeName)
{
*exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1;
}
pclsObj->Release();
pEnumerator->Release();
return true;
}
#endif
extern "C" bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid)
{
uint32_t ppid;
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe))
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe, &result->shellExeName))
return 0;
result->shellExeName = result->shellExe.chars + ffStrbufLastIndexC(&result->shellExe, '\\') + 1;
ffStrbufSet(&result->shellPrettyName, &result->shellProcessName);
if(ffStrbufEndsWithIgnCaseS(&result->shellPrettyName, ".exe"))
@@ -113,9 +167,8 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid)
{
uint32_t ppid;
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe))
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName))
return 0;
result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\');
ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName);
if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe"))
@@ -135,7 +188,7 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid)
ffStrbufClear(&result->terminalProcessName);
ffStrbufClear(&result->terminalPrettyName);
ffStrbufClear(&result->terminalExe);
result->terminalExeName = nullptr;
result->terminalExeName = "";
return getTerminalInfo(result, ppid);
}
@@ -212,21 +265,21 @@ const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance)
ffStrbufInit(&result.shellProcessName);
ffStrbufInitA(&result.shellExe, 128);
result.shellExeName = result.shellExe.chars;
result.shellExeName = "";
ffStrbufInit(&result.shellPrettyName);
ffStrbufInit(&result.shellVersion);
ffStrbufInit(&result.terminalProcessName);
ffStrbufInitA(&result.terminalExe, 128);
result.terminalExeName = result.terminalExe.chars;
result.terminalExeName = "";
ffStrbufInit(&result.terminalPrettyName);
ffStrbufInit(&result.userShellExe);
result.userShellExeName = result.userShellExe.chars;
result.userShellExeName = "";
ffStrbufInit(&result.userShellVersion);
uint32_t ppid = GetCurrentProcessId();
if(!getProcessInfo(ppid, &ppid, nullptr, nullptr))
uint32_t ppid;
if(!getProcessInfo(0, &ppid, nullptr, nullptr, nullptr))
goto exit;
ppid = getShellInfo(&result, ppid);