如何在Windows上获得进程工作目录
How to get a process working dir on Windows?
如何使用本地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 ;
请注意,这种方法包含一个竞争,除非进程被挂起
详细解释Anton的回答,因为你不能像调用普通函数那样简单地调用NtQueryInformationProcess
,你必须通过GetModuleHandleW
调用Windows ntdll.dll,如下所示:
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", "rn");
} else {
printf("%s", cwd_from_pid(GetCurrentProcessId()).c_str());
printf("%s", "rn");
}
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上获得进程工作目录?
这不是指远程进程,所以对于当前进程:
NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath
如何在Windows上使用本机API获得进程工作目录(for另一个进程使用进程句柄或PID)?
这可以通过几个已知的和未知的(未记录的)方法和函数来实现。我将暂时不讨论未归档的方法,只提供已归档的函数。
- 按进程句柄:GetFinalPathNameByHandleA, GetProcessImageFileNameA, QueryFullProcessImageNameA
- 通过进程名:GetFullPathNameA
- 按进程PID: OpenProcess + GetModuleFileNameEx
- 将类型化数组写入子进程 stdin 无法正常工作
- 设置 ACL,指定允许在请求队列上接收 I/O 的工作进程
- 读取进程内存无法正常工作,使用 UTF16 字符串
- Dll进程挂钩不工作
- 如何使本机Unix/Linux守护进程在Android上工作
- CPP GDB 崩溃,没有核心和 GDB 附加到工作进程
- MPI 从进程在不再工作时挂起
- 杀死一个Linux进程,停止它在Oracle数据库中的查询工作吗?
- 停止QThread工作进程处理即将删除的资源的正确方法是什么
- 当用std::system启动进程时,我可以指定一个工作目录吗
- c++代码工作正常,但进程以termination结束,而不是返回0
- 如何获取绑定到我正在运行的控制台窗口的进程列表,这些进程也可以在 Windows 2000 中工作?
- 子进程中的Execl仅在特定情况下工作
- 如何在C/ c++中使三个进程工作
- 通知父进程恢复工作
- 进程外内存堆围绕32位地址空间工作
- Visual Studio.如何构建DLL(在命令行中),通过附加到进程来调试工作
- Execl与wget,子进程,为什么它不工作
- 捕获外部进程的工作目录[Qt/WinAPI]
- 如何在Windows上获得进程工作目录