可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I got the following code to get path/filename by handle:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowThreadProcessId(IntPtr handle, out uint processId); public static string GetProcessPath(IntPtr hwnd) { uint pid = 0; GetWindowThreadProcessId(hwnd, out pid); Process proc = Process.GetProcessById((int)pid); return proc.MainModule.FileName.ToString(); }
it works perfect in 32 bit but I get error in 64bit > "Only part of the ReadProcessMemory or WriteProcessMemory request was completed." the project is compiled as x86 (platform target x86).
How can I fix it?
~Thanks Ron
回答1:
It appears from your question that you've currently compiled your program as a 32-bit application. However, the process you're trying to query (assuming you're running on a 64-bit version of Windows) is undoubtedly a 64-bit one. That kind of thing isn't allowed. Although you can run 32-bit applications on 64-bit versions of Windows, they run under the dedicated Windows on Windows (WOW64) subsystem. That's why you're getting a Win32Exception
claiming that "Only part of a ReadProcessMemory or WriteProcessMemory request was completed". I agree it's not the most descriptive of error messages if you don't already know how Windows manages 32-bit and 64-bit processes, but armed with this knowledge it makes at least a little more sense.
The solution is to compile your application as a 64-bit application (x64), or for "Any CPU". Everything should work as expected after that. If possible, I suggest using "Any CPU", which will allow the application to run in 32-bit mode on 32-bit OSes and 64-bit on 64-bit OSes. Which is really the ideal set of circumstances, assuming that:
- You've written your P/Invoke definitions correctly (i.e., using
IntPtr
where appropriate, instead of Integer
). - You're not relying on 3rd-party DLLs (to which you don't have the source code) that were compiled as 32-bit.
回答2:
I'm not aware of a known problem with this, it smells environmental. The error is very low level, probably the wow64 emulation layer. I can only suggest you punt and use a different way to get the same info. You can use WMI, Win32_Process class. Run a select query on ProcessId, the ExecutablePath property gives you what you are looking for. Use the WMI Code Creator utility to experiment, it auto-generates the C# code you need. The odds are however not zero that this will fail the same way.
回答3:
It works well for me on a 64x machine. The only changes to the code are checking the values, as:
if (hwnd != IntPtr.Zero) { if (pid != 0) { var p = Process.GetProcessById((int) pid) if (p != null) { //... } } }
回答4:
This thing is absolutely possible from a 32 bit application when checking a 64 bit process, though not as straightforward as it would be if it were a 64 bit compiled application:
[Flags] enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VMOperation = 0x00000008, VMRead = 0x00000010, VMWrite = 0x00000020, DupHandle = 0x00000040, SetInformation = 0x00000200, QueryInformation = 0x00000400, Synchronize = 0x00100000, ReadControl = 0x00020000, PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 } [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll")] private static extern bool QueryFullProcessImageName(IntPtr hprocess, int dwFlags, StringBuilder lpExeName, out int size); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hHandle); private static Process GetProcessByHandle(IntPtr hwnd) { try { uint processID; GetWindowThreadProcessId(hwnd, out processID); return Process.GetProcessById((int)processID); } catch { return null; } } private static string GetExecutablePathAboveVista(int ProcessId) { var buffer = new StringBuilder(1024); IntPtr hprocess = OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessId); if (hprocess != IntPtr.Zero) { try { int size = buffer.Capacity; if (QueryFullProcessImageName(hprocess, 0, buffer, out size)) { return buffer.ToString(); } } finally { CloseHandle(hprocess); } } return null; } private static string GetWindowPath(IntPtr hwind) { try { Process currentProcess = GetProcessByHandle(hwind); if (Environment.OSVersion.Version.Major >= 6) { string newMethReturn = GetExecutablePathAboveVista(currentProcess.Id); if (!string.IsNullOrWhiteSpace(newMethReturn)) return newMethReturn; } if (currentProcess != null) return currentProcess.MainModule.FileName; else return null; } catch { return null; } }