C# FindWindowEx 通过 WinSpy++ 获取 lpszClass 变量的参数不起作用

Posted

技术标签:

【中文标题】C# FindWindowEx 通过 WinSpy++ 获取 lpszClass 变量的参数不起作用【英文标题】:C# FindWindowEx Obtain parameter for lpszClass variable by WinSpy++ not work 【发布时间】:2014-11-01 07:25:13 【问题描述】:

问题

这是我在后台发送击键的功能。

class SendMessage


[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

public static void sendKeystroke(string appName)

    const int WM_KEYDOWN = 0x100; 
    IntPtr hWnd = FindWindow(null, appName);
    IntPtr editx = FindWindowEx(hWnd, IntPtr.Zero, "edit", null);
    PostMessage(editx, WM_KEYDOWN, (IntPtr)Keys.A, (IntPtr)0);  



此代码适用于 notepad,例如 let appName = "notepad"。

但是,我可以在其他应用程序中做到这一点..我在 LINE 应用程序上练习。

正如你在图片中看到的lpszClass variable = "edit"(红色小圆圈)用于记事本。

我需要为 LINE 应用程序找到它,所以我使用 WinSpy++ 来捕获这些类名。

我发现它的类名是“ATL:00B53BE8”(大红色圆圈),我可以输入消息

在WinSpy++中回车,会出现Line文本框(蓝色圆圈)。

结论

我尝试将捕获类名称替换为“编辑”,但没有希望。

我不明白为什么捕获类名不可用,请帮助或给我一些提示。

我不知道可能是系统应用程序的层次结构不同(粉红色的)

而且我不知道 FindWindowEx 中的哪些参数意义重大。

我的最终目标是将击键发送到其他应用程序而不关注它们。

【问题讨论】:

您的问题是什么?就像这里经常出现的情况一样,输入伪造是使用 SendInput 完成的,而自动化是使用 UIAutomation 完成的。你为什么要这样黑客攻击? 黑客攻击???我只想在后台传递输入并找出一些解决方案,这似乎都有效 你在破解。为什么不使用我概述的首选解决方案? 因为它无法在没有焦点的情况下发送密钥,这是我所知道的。所以这不符合我的要求我想做其他窗口,同时让它在后台工作(发送密钥)到其他应用程序。 你应该使用 UIAutomation。它可以生成表示 GUI 的树形结构。 FindWindowEx 有据可查。您需要多次调用才能遍历父子层次结构。但这样的黑客行为不是前进的方向,我并不希望你,像你之前的许多人一样,接受这个建议。 【参考方案1】:

来自与David Herffernan的讨论

“您需要多次调用才能遍历父子层次结构”

现在,我可以让我的代码工作了。但是,这似乎不是正确的做法(黑客)

但是,我想回答这个问题以供参考

关键是使用FindWindowEx 它就像David Herffernan 所说的那样工作。

而不是这个..

public static void sendKeystroke(string appName)

    const int WM_KEYDOWN = 0x100; 
    IntPtr hWnd = FindWindow(null, appName);
    IntPtr editx = FindWindowEx(hWnd, IntPtr.Zero, "edit", null);
    PostMessage(editx, WM_KEYDOWN, (IntPtr)Keys.A, (IntPtr)0);  

我对此进行了编辑..

public static void sendKeystroke(string appName)

    const int WM_KEYDOWN = 0x100; 
    IntPtr hWnd = FindWindow(null, appName);
    IntPtr editx1 = FindWindowEx(hWnd, IntPtr.Zero, "SkinScrollWnd", null);
    IntPtr editx2 = FindWindowEx(editx1, IntPtr.Zero, "SkinScrollMidWnd", null);
    IntPtr editx3 = FindWindowEx(editx2, IntPtr.Zero, "ATL:00B53BE8", null);
    PostMessage(editx3, WM_KEYDOWN, (IntPtr)Keys.A, (IntPtr)0);  

【讨论】:

【参考方案2】:

您不能期望WM_KEYDOWN 消息发送到没有焦点的应用程序会产生预期的效果。根本不支持。根据 Windows 的规则,接收WM_KEYDOWN 消息意味着您的应用程序具有焦点。只有获得焦点的应用程序才能获得键盘输入。

所以你正在尝试的可能在某些情况下有效,但不能保证有效。如果应用程序正在接收键盘输入,它可能会做出相当合理的假设,即它具有焦点。

如果它在记事本中“有效”,那是因为记事本是一个非常dumb 简单的应用程序。它只是一个带有菜单栏的编辑控件。它对WM_KEYDOWN 消息的处理可能只是将消息参数指示的键所代表的字符添加到编辑控件。大多数其他应用程序更复杂。甚至记事本也不能保证在所有情况下都能正常工作。例如,当它的窗口最小化时。在本网站和 Interwebz 的其他地方有大量此类问题的报告。

简单地说:发送WM_KEYDOWN 和它的朋友不是在Windows 中模拟键盘输入的方式。有两种基本方法。首先是使用SendInput函数;另一种是安装WH_JOURNALPLAYBACK钩子。 .NET 中SendKeys 类的不同实现版本采用了这两种方法。它们都将合成的输入发送到焦点窗口,因为在 Windows 中,这是接收所有输入的窗口。

如果要这样做,您绝对必须找到另一种方法。在 cmets 中,David 建议使用 UI Automation,这是一个为此目的而设计的工具。它是conveniently wrapped by the .NET Framework。目前尚不清楚您为什么拒绝这个建议。借助 UI 自动化,您可以使用嵌套树结构轻松浏览应用程序中的窗口层次结构。该树是根据进程中的所有窗口句柄自动构建的。这使您可以轻松找到要操作的控件。然后,您获得相应的control patterns,并执行您想要的任何操作。

【讨论】:

以上是关于C# FindWindowEx 通过 WinSpy++ 获取 lpszClass 变量的参数不起作用的主要内容,如果未能解决你的问题,请参考以下文章

c# 用 FindWindowEx 获取子窗体时 出异常

WinSpy涉及的windows api

在 Visual Studio C# 中控制外部窗口

c#获取窗口句柄后 如何遍历所有控件

python selenium+pywin32+winspy64工具 完成百度上传图片识图功能

C#获取当前窗体句柄及该窗体里的信息