11

我需要以编程方式处理另一个 Windows 应用程序,搜索谷歌我找到了一个使用 DLLImport 属性处理 Windows 计算器并将 user32.dll 函数导入 C# 中的托管函数的示例。

应用程序正在运行,我正在获取主窗口的句柄,即计算器本身,但之后的代码不起作用。FindWindowEx 方法不返回 Calculator 子项的句柄,例如按钮和文本框。

我尝试在 DLLImport 上使用 SetLastError=True,发现我收到错误代码 127,即“找不到程序”。

这是我获得示例应用程序的链接:

http://www.codeproject.com/script/Articles/ArticleVersion.aspx?aid=14519&av=34503

如果有人知道如何解决它,请帮助。

更新:DLLImport 是:

[DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className,  string  windowTitle);

不起作用的代码是:

hwnd=FindWindow(null,"Calculator"); // This is working, I am getting handle of Calculator

// The following is not working, I am getting hwndChild=0 and err = 127
hwndChild = FindWindowEx((IntPtr)hwnd,IntPtr.Zero,"Button","1");

                Int32 err = Marshal.GetLastWin32Error();
4

3 回答 3

13

您正在尝试的代码依赖于各个按钮的标题来识别它们。例如,它使用以下代码来获取“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);
}
于 2011-03-09T06:14:47.713 回答
2

I was able to repro this on Win7 Pro. Your problem is likely that the labels on the buttons are drawn via the theme of the calculator and not as a caption. When the Themes service is running, starting Calculator will cause it to have buttons with no caption.

In order to get proper button captions, you must:

  1. Stop the Themes service (run net stop themes from an elevated command prompt or use the Services administrative tool).
  2. Start Calculator.

If you have Calculator running when you stop the Themes service, you will notice that all of its buttons become blank.

于 2011-03-09T06:51:34.670 回答
2

以下代码在经典主题的 Windows 7 的 Caculator 中运行良好(不适用于 Basic 或 Aero 主题):

IntPtr hwndFrame = FindWindowEx(hwnd, IntPtr.Zero, "CalcFrame", null); 
IntPtr hwndDialog = FindWindowEx(hwndFrame, IntPtr.Zero, "#32770", null); 
IntPtr hwndDialog2 = FindWindowEx(hwndFrame, (IntPtr)hwndDialog, "#32770", null);

IntPtr hwndThree = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "3"); 
SendMessage((int)hwndThree, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndPlus = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "+");
SendMessage((int)hwndPlus, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndOne = FindWindowEx((IntPtr)hwndDialog2, IntPtr.Zero, "Button", "1");
SendMessage((int)hwndOne, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndEqual = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "=");
SendMessage((int)hwndEqual, BN_CLICKED, 0, IntPtr.Zero);
于 2012-04-16T11:25:32.960 回答