您正在尝试的代码依赖于各个按钮的标题来识别它们。例如,它使用以下代码来获取“1”按钮的句柄:
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "1");
它为窗口类的名称指定“Button”,为窗口的名称指定“1”(在按钮的情况下,这与按钮本身上显示的标题文本相同)。
此代码在 Windows XP(和以前的版本)下运行良好,其中计算器按钮用文本标题标识。“1”按钮的窗口名称为“1”,因此“1”显示为按钮的标题。
但是,在 Windows 7 下,情况似乎发生了变化(可能在 Vista 下也是如此,尽管我无法验证这一点,因为我无法访问这样的系统)。使用 Spy++ 调查计算器窗口确认“1”按钮不再具有窗口名称“1”。事实上,它根本没有窗口名称。标题为 NULL。据推测,计算器的新花哨外观要求自定义绘制按钮,因此不再需要标题来指示哪个按钮对应于哪个功能。自定义绘画例程负责绘制必要的标题。
由于在您指定的窗口文本中找不到任何按钮,因此NULL
为窗口句柄返回值 0 ( )。
该函数的文档FindWindowEx
表明您可以指定NULL
参数lpszWindow
,但这当然会匹配指定类的所有窗口。在这种情况下可能不是您想要的,因为计算器应用程序有一堆按钮。
我不知道一个好的解决方法。Calculator 的设计初衷不是以这种方式“自动化”,而且微软从未保证他们不会改变其内部运作。这是您使用这种方法弄乱其他应用程序窗口的风险。
编辑:您链接到的代码在另一种相当严重的方面也是错误的,即使在早期版本的 Windows 上也是如此。它将hwnd
变量声明为 type int
,而不是 type IntPtr
。由于窗口句柄是一个指针,因此您应该始终将其存储为一个IntPtr
类型。这也修复了FindWindowEx
函数调用中应该发出危险信号的丑陋演员。
您还需要修复 的声明SendMessage
,使其第一个参数的类型为IntPtr
。
代码应该是这样写的:
IntPtr hwnd = IntPtr.Zero;
IntPtr hwndChild = IntPtr.Zero;
//Get a handle for the Calculator Application main window
hwnd = FindWindow(null, "Calculator");
if(hwnd == IntPtr.Zero)
{
if(MessageBox.Show("Couldn't find the calculator" +
" application. Do you want to start it?",
"TestWinAPI",
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
System.Diagnostics.Process.Start("Calc");
}
}
else
{
//Get a handle for the "1" button
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "1");
//send BN_CLICKED message
SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);
//Get a handle for the "+" button
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "+");
//send BN_CLICKED message
SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);
//Get a handle for the "2" button
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "2");
//send BN_CLICKED message
SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);
//Get a handle for the "=" button
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "=");
//send BN_CLICKED message
SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);
}