我想知道我将如何做到这一点,因为我尝试了无数不同的user32功能并在网上进行了相当广泛的研究,但不幸的是我还没有能够提出解决方案。
有一个应用程序有5个线程。只要进程'PID,就可以通过.NET Process类GetProcessById方法轻松访问这些线程。但是,似乎没有一个函数可用于提供线程ID并枚举其窗口(父级或子级)。其中一个线程共有10个窗口,9个隐藏,1个可见。该可见线程的标题是我试图以编程方式获得的。
我的最新方法是获取进程句柄,将其放入EnumChildWindows,并尝试将每个窗口句柄添加到集合中,但我的集合始终为空。
以下是我在ProcessThreadsView工具中看到的截图:
我有什么遗失的吗?我通过电子邮件发送了该工具的作者,看看他是如何做到的,但我想我会问你们看看是否有一个既定的方法。
更新:我尝试过使用GetGUIThreadInfo,这就是我的称呼方式:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
public uint cbSize;
public uint flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public Rect rcCaret;
}
static IEnumerable<IntPtr> EnumerateThreadWindowHandlesByProcessId(int processId)
{
List<IntPtr> threadWindowHandles = new List<IntPtr>();
foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
{
GUITHREADINFO threadInfo = new GUITHREADINFO();
threadInfo.cbSize = (uint)Marshal.SizeOf(threadInfo);
bool returnValue = GetGUIThreadInfo((uint)thread.Id, out threadInfo);
threadWindowHandles.Add(threadInfo.hwndActive);
}
return threadWindowHandles;
}
更新2:
使用EnumThreadWindows,这就是我所拥有的:
public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
private static bool ThreadWindows(IntPtr handle, IntPtr param)
{
//get window from handle later, testing for now
logger.Info("foo bar");
return true;
}
[STAThread]
public void Execute()
{
Process[] processes = Process.GetProcessesByName("MyProcessName");
Process processOfInterest = processes[0];
foreach (ProcessThread thread in processOfInterest.Threads)
{
EnumThreadWindows(thread.Id, new EnumThreadDelegate(ThreadWindows), IntPtr.Zero);
}
}
答案 0 :(得分:4)
(道歉,如果我之前的回答很严厉;我说“看似很明显”,因为它对我来说似乎很明显,所以我认为它对OP来说也是显而易见的,因此是错误的,但他没有提供任何关于什么的细节已经/未曾尝试过。)
EnumThreadWindows
绝对是你想要的功能,它应该有效。对于我系统上运行的每个应用程序,我只能使用EnumThreadWindows
和IsWindowVisible
从ProcessThreadsView复制信息。 (另请注意,“可见”与“有效”不同。)
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool EnumThreadWindowsProc(IntPtr handle, int param);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumThreadWindows(uint threadId,
EnumThreadWindowsProc callback, int param);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr handle);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int GetWindowText(
IntPtr handle,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder caption,
int count);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int GetWindowTextLength(IntPtr handle);
static void Main(string[] args)
{
var callback = new EnumThreadWindowsProc(Program.ThreadWindows);
foreach (var proc in Process.GetProcesses())
{
foreach (ProcessThread thread in proc.Threads)
{
Program.EnumThreadWindows((uint)thread.Id, callback, 0);
}
}
Console.ReadLine();
}
private static bool ThreadWindows(IntPtr handle, int param)
{
if (Program.IsWindowVisible(handle))
{
var length = Program.GetWindowTextLength(handle);
var caption = new StringBuilder(length + 1);
Program.GetWindowText(handle, caption, caption.Capacity);
Console.WriteLine("Got a visible window: {0}", caption);
}
return true;
}
尝试使用EnumThreadWindows时,您到底得到了什么?你的回调有没有发生?
答案 1 :(得分:4)
我相信您正在寻找GetGUIThreadInfo
。
更新:您的p / invoke存在一些问题。
rect字段是错误的(这并不重要)。使用此:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
而不是System.Drawing.Rectangle
。
其他字段的类型声明也已关闭。它应该是这样的:
[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
public uint cbSize;
public uint flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public Rect rcCaret;
}
最好检查一下您的返回值并在SetLastError = true
属性中使用DllImport
,这样就可以看出函数调用失败的原因。还有,你
应该通过ref
传递结构,因为你传入了结构大小。
[DllImport("user32.dll", SetLastError=true)]
static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
使用Marshal.GetLastWin32Error()
获取错误代码。但只有GetGUIThreadInfo
返回false才会检查此内容。
通过上述更改,当给定有效的线程ID时,对GetGUIThreadInfo
的调用将起作用。还要注意,其他进程需要具有输入焦点,以便GetGUIThreadInfo
返回任何有用的内容。
如果指定的线程不存在或具有输入队列,则该函数将失败。
答案 2 :(得分:1)
您在Update 2中的代码很好(假设eraAccessProcess
实际上是processOfInterest
)。该代码有效。我能看到的唯一可能的失败原因是:
processOfInterest
不是您真正感兴趣的过程。