问题
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 ofInteger
). - 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; }
}
来源:https://stackoverflow.com/questions/4768689/c-sharp-get-path-filename-by-handle-hwnd-32-and-64bit