来自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上重现这一点。 您的问题很可能是按钮上的标签是通过计算器的主题绘制的,而不是标题。 当主题服务运行时,启动计算器将使其具有没有标题的按钮。
为了获得正确的按钮标题,您必须:
- 停止Themes服务(从提升的命令提示符运行
net stop themes
或使用服务管理工具)。 - 启动计算器。
如果在停止主题服务时运行计算器,您会注意到它的所有按钮都变为空白。