来自user32.dll的FindWindowEx使用dllimport返回零句柄和错误代码127

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

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

我尝试在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(); 

您尝试的代码依赖于各个按钮的标题来识别它们。 例如,它使用以下代码来获取“1”按钮的句柄:

 hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "1"); 

其中窗口类名称为“Button”,窗口名称为“1”(如果是按钮,则与按钮本身显示的标题文本相同)。

此代码在Windows XP(和以前的版本)下运行良好,其中计算器按钮用文本标题标识。 “1”按钮的窗口名称为“1”,因此“1”显示为按钮的标题。

但是,看起来Windows 7下的情况已经发生了变化(可能在Vista下也是如此,虽然我无法validation这一点,因为我无法访问这样的系统)。 使用Spy ++调查计算器窗口确认“1”按钮不再具有窗口名称“1”。 实际上,它根本没有窗口名称; 标题为NULL。 据推测,计算器的新外观要求按钮是自定义绘制的,因此不再需要字幕来指示哪个按钮对应于哪个function。 自定义绘图例程负责绘制必要的标题。

由于找不到您指定的窗口文本的按钮,因此窗口句柄返回值0( NULL )。

FindWindowEx函数的文档表明您可以为lpszWindow参数指定NULL ,但这当然会匹配指定类的所有窗口。 在这种情况下可能不是你想要的,因为计算器应用程序有一堆按钮。

我不知道一个好的解决方法。 计算器并非设计为以这种方式“自动化”,微软从未保证他们不会改变其内部工作方式。 使用这种方法来混淆其他应用程序的窗口是一种风险。


编辑:你链接到的代码在另一个相当严重的方式也是错误的,即使在早期版本的Windows上。 它将hwnd变量声明为int类型,而不是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); } 

以下代码在经典主题的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); 

我能够在Win7 Pro上重现这一点。 您的问题很可能是按钮上的标签是通过计算器的主题绘制的,而不是标题。 当主题服务运行时,启动计算器将使其具有没有标题的按钮。

为了获得正确的按钮标题,您必须:

  1. 停止Themes服务(从提升的命令提示符运行net stop themes或使用服务管理工具)。
  2. 启动计算器。

如果在停止主题服务时运行计算器,您会注意到它的所有按钮都变为空白。