ネイティブAPIを使用してWindowsでプロセス作業ディレクトリを取得するには(プロセスハンドルまたはPIDを使用する別のプロセスの場合)?プロセス関数とスレッド関数、PSAPI関数を見てきましたが、見つかりませんでした。多分WMI?
また、これらのトピックに関して、PSAPIはプロセスおよびスレッド関数とどのように関連していますか?時代遅れですか?
ネイティブAPIを使用してWindowsでプロセス作業ディレクトリを取得するには(プロセスハンドルまたはPIDを使用する別のプロセスの場合)?プロセス関数とスレッド関数、PSAPI関数を見てきましたが、見つかりませんでした。多分WMI?
また、これらのトピックに関して、PSAPIはプロセスおよびスレッド関数とどのように関連していますか?時代遅れですか?
このためには、PSAPIよりも重い砲が必要です。方法は次のとおりです(x86を想定、エラー処理は省略)。
ProcessBasicInformation pbi ;
RTL_USER_PROCESS_PARAMETERS upp ;
PEB peb ;
DWORD len ;
HANDLE handle = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid) ;
NtQueryInformationProcess (handle, 0 /*ProcessBasicInformation*/, &pbi,
sizeof (ProcessBasicInformation), &len) ;
ReadProcessMemory (handle, pbi.PebBaseAddress, &peb, sizeof (PEB), &len) ;
ReadProcessMemory (handle, peb.ProcessParameters, &upp, sizeof (RTL_USER_PROCESS_PARAMETERS), &len) ;
WCHAR path = new WCHAR[upp.CurrentDirectoryPath.Length / 2 + 1] ;
ReadProcessMemory (handle, upp.CurrentDirectoryPath.Buffer, path, upp.CurrentDirectoryPath.Length, &len) ;
// null-terminate
path[upp.CurrentDirectoryPath.Length / 2] = 0 ;
プロセスが中断されない限り、このアプローチには競争が含まれることに注意してください。
アントンの答えを拡張するには、通常の関数のように単純に呼び出すことはできないため、次のようNtQueryInformationProcess
にWindowsのntdll.dllを呼び出す必要があります。GetModuleHandleW
getcwd.cpp
#include <string>
#include <vector>
#include <cwchar>
#include <windows.h>
#include <winternl.h>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
// define process_t type
typedef DWORD process_t;
// #define instead of typedef to override
#define RTL_DRIVE_LETTER_CURDIR struct {\
WORD Flags;\
WORD Length;\
ULONG TimeStamp;\
STRING DosPath;\
}\
// #define instead of typedef to override
#define RTL_USER_PROCESS_PARAMETERS struct {\
ULONG MaximumLength;\
ULONG Length;\
ULONG Flags;\
ULONG DebugFlags;\
PVOID ConsoleHandle;\
ULONG ConsoleFlags;\
PVOID StdInputHandle;\
PVOID StdOutputHandle;\
PVOID StdErrorHandle;\
UNICODE_STRING CurrentDirectoryPath;\
PVOID CurrentDirectoryHandle;\
UNICODE_STRING DllPath;\
UNICODE_STRING ImagePathName;\
UNICODE_STRING CommandLine;\
PVOID Environment;\
ULONG StartingPositionLeft;\
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
UNICODE_STRING WindowTitle;\
UNICODE_STRING DesktopName;\
UNICODE_STRING ShellInfo;\
UNICODE_STRING RuntimeData;\
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
}\
// shortens a wide string to a narrow string
static inline string shorten(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string { buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
// checks whether process handle is 32-bit or not
static inline bool IsX86Process(HANDLE process) {
BOOL isWow = true;
SYSTEM_INFO systemInfo = { 0 };
GetNativeSystemInfo(&systemInfo);
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
return isWow;
IsWow64Process(process, &isWow);
return isWow;
}
// helper to open processes based on pid with full debug privileges
static inline HANDLE OpenProcessWithDebugPrivilege(process_t pid) {
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
}
// helper to get wide character string of pids cwd based on handle
static inline wchar_t *GetCurrentWorkingDirectoryW(HANDLE proc) {
PEB peb;
SIZE_T nRead;
ULONG res_len = 0;
PROCESS_BASIC_INFORMATION pbi;
RTL_USER_PROCESS_PARAMETERS upp;
HMODULE p_ntdll = GetModuleHandleW(L"ntdll.dll");
typedef NTSTATUS (__stdcall *tfn_qip)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
tfn_qip pfn_qip = tfn_qip(GetProcAddress(p_ntdll, "NtQueryInformationProcess"));
NTSTATUS status = pfn_qip(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &res_len);
if (status) { return NULL; }
ReadProcessMemory(proc, pbi.PebBaseAddress, &peb, sizeof(peb), &nRead);
if (!nRead) { return NULL; }
ReadProcessMemory(proc, peb.ProcessParameters, &upp, sizeof(upp), &nRead);
if (!nRead) { return NULL; }
PVOID buffer = upp.CurrentDirectoryPath.Buffer;
USHORT length = upp.CurrentDirectoryPath.Length;
wchar_t *res = new wchar_t[length / 2 + 1];
ReadProcessMemory(proc, buffer, res, length, &nRead);
if (!nRead) { return NULL; }
res[length / 2] = 0;
return res;
}
// get cwd of pid as a narrow string
string cwd_from_pid(process_t pid) {
string cwd;
// open process of pid using full debug privilege
HANDLE proc = OpenProcessWithDebugPrivilege(pid);
wchar_t *wcwd = NULL;
if (IsX86Process(GetCurrentProcess())) {
if (IsX86Process(proc)) {
wcwd = GetCurrentWorkingDirectoryW(proc);
}
} else {
if (!IsX86Process(proc)) {
wcwd = GetCurrentWorkingDirectoryW(proc);
}
}
if (wcwd != NULL) {
// converts to UTF-8
cwd = shorten(wcwd);
// free memory
delete[] wcwd;
}
// adds trailing slash if one doesn't yet exist or leave empty
return (cwd.back() == '\\' || cwd.empty()) ? cwd : cwd + "\\";
// return cwd; // or get the directories completely unmodified
}
// test function (can be omitted)
int main(int argc, char **argv) {
if (argc == 2) {
printf("%s", cwd_from_pid(stoul(string(argv[1]), nullptr, 10)).c_str());
printf("%s", "\r\n");
} else {
printf("%s", cwd_from_pid(GetCurrentProcessId()).c_str());
printf("%s", "\r\n");
}
return 0;
}
buildx86.sh
cd "${0%/*}"
g++ getcwd.cpp -o getcwd.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m32
buildx64.sh
cd "${0%/*}"
g++ getcwd.cpp -o getcwd.exe -std=c++17 -static-libgcc -static-libstdc++ -static -m64
注意してください。これはプライベートAPIを使用しており、警告なしに変更される可能性があるため、事前の通知やドキュメントなしで動作を停止します。呼び出し元のプロセス/exeは、このアプローチが機能するためのターゲットと同じアーキテクチャである必要があります。それ以外の場合は、空の文字列を返します。
CreateProcess()からの印刷出力の読み取り方法を知っている場合は、ターゲット実行可能ファイルのアーキテクチャに基づいて、適切なアーキテクチャのCLI実行可能ファイルを起動できます。これは、プロジェクトのビルドに複数の実行可能ファイルを使用することを意味します。これは時間がかかりますが、ユースケースによっては、それでも許容できる場合があります。明らかに、このために頻繁に(あまり頻繁ではないが)新しいプロセスを作成しない限り、プログラムの速度が低下しすぎないようにするのは理想的ではありません。
これは良い質問ですが、これらすべての答えを非常に多くのコードで見ると、心が痛むことになります。現在の作業ディレクトリを取得する「ネイティブ」な方法が必要です。解決しました。
現在のプロセスを開いてメモリを読み取ることは、「ネイティブ」ではありません。
WindowsプロセスはPEBに多くの情報を保持しており、それを取得するためにそれほど多くのコードは必要ありません。実際、これは次のように単純です。
NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath
"."
常に現在のディレクトリです。私はそれがうまくいくと思います。