
向任何应用程序发送击键,从MFC应用程序中调用.NET及其他
发布时间:2006-05-05 19:02:59 来源:msdn 网友评论 0 条下载本文的示例代码: CQA0501.exe
问:我正在尝试编写一个应用程序,该应用程序通过向另外一个应用程序发送击键来填充窗体。我是应该发送 WM_KEYDOWN 和 WM_KEYUP 消息,还是有更好的方法?
许多读者
答:虽然通过发送 WM_KEYDOWN 和 WM_KEYUP 消息,您可能使应用程序进行工作,但是,SendInput 是一个专门为发送该消息而设计的 API 函数。它通过采用一组 INPUT 结构(每个输入事件 ─ 击键或鼠标操作 ─ 对应一个结构)对输入(包括击键和鼠标事件)进行了组合。INPUT 结构包含一个联合,该联合可以是 MOUSEINPUT 或 KEYBDINPUT(或是模拟烘烤炉的 HARDWAREINPUT)。对于键盘,它如下所示:
struct KEYBDINPUT { WORD wVk; // virt key code WORD wScan; // hw scan code DWORD dwFlags; // flags—see doc DWORD time; // time stamp, 0 = dflt ULONG_PTR dwExtraInfo; // app-defined}; 
图 1Typematic 初始对话框
因此,向另外一个应用程序发送击键基本上是构建一组 INPUT 结构的问题,每个击键(上下)对应一个结构,然后调用 SendInput。为演示它在实际中如何工作,我编写了一个名为 Typematic 的小程序,它让您仅仅按下一个热键就可以快速向窗体中输入您的姓名、地址、电话号码或其他信息。对于 Web 购物狂,这真是一件大好事。在首次运行 Typematic 时,它显示图 1中的对话框,然后就隐藏起来。此后,您可以按下
struct ABBREV { TCHAR key; LPCTSTR text;} MYABBREVS[] = { { _T('n'),_T("Elmer Fudd") }, { _T('a'),_T("1 Bunny Way") }, ... { 0,NULL} }; 
图 2重新激活的 Typematic
当然,在实际工作中,您不会硬编码该信息;您应提供可以自定义的用户界面,并在用户配制文件中保存该信息,以使该计算机上的每位用户都拥有不同的设置。
Typematic 显示了其他几个窍门:如何注册一个热键以激活您的应用程序(参见我的 December 2000专栏)和如何制作接受键盘输入的静态文本控件(您必须处理 WM_GETDLGCODE 并返回 DLGC_WANTCHARS。)
Typematic 定义了一个专门的静态文本控件 CStaticAbbrev,该控件既可以显示缩写,又可以读取快捷键。图 3显示了源代码。在用户按下热键时,Typematic 唤醒并将焦点赋于 CStaticAbbrev 控件,该控件等待一个字符。在 CStaticAbbrev::OnChar 获得一个与表中缩写相匹配的键时,它隐藏该对话框,然后调用一个 Helper 函数 SendString 发送文本:
// in CStaticAbbrev::OnCharif (/* find char in ABBREV table */) { GetParent()->ShowWindow(SW_HIDE); // hide dialog SendString(abbrev.text); // send text} 在 Typematic 隐藏自身时,Windows®自动将焦点还原到之前拥有该焦点的窗口,这样,输入就进入在用户按下热键之前正使用的窗口。相当智能化,不是吗?如果您需要直接输入到一个特定的应用程序或窗口,请确保在调用 SendInput 之前它被激活。SetForegroundWindow 是一个调用函数。
发送击键的所有工作在 SendString 中发生,它构建 INPUT 数组并调用 SendInput(参见 图 3)。SendString 发送 INPUT 结构的一系列 KEYDOWN/KEYUP 对,字符串中的每个字符对应一个 KEYDOWN/KEYUP 对。它使用 KEYEVENTF_UNICODE 标记发送作为 Unicode 字符的字符串。Unicode 使工作更轻松,因为您无需使用 Shift 键来组合大写字母。如果没有 KEYEVENTF_ UNICODE,您就必须先按下
如果您正在使用托管 C++ 或其他 Microsoft®.NET 兼容的语言进行编程,则发送击键甚至更简单。有了带有静态函数 Send 的 Framework 类和 SendKey,发送键简直就是小菜一碟。甚至可以使用波浪线语法发送特殊键。例如,“{F1}{BACKSPACE}A”发送 F1、Backspace 和 A。出于娱乐,我编写了 Typematic 的另一个使用 SendKey 的版本 Typematic.NET。目前,SendString 函数是比较常用的:
#pragma managedvoid SendString(LPCTSTR str) { SendKeys::SendWait(str);}
怎么样才能更简单一些?在我首次尝试时,我自然尝试 SendKeys::Send,而不是 SendWait。为什么我应当等待应用程序完成键输入呢?唉!很遗憾。在我尝试时,由于 Windows.Forms.dll 中某处的 System.InvalidOperationException,Typematic 崩溃了。在我调用公共语言运行库 (CLR) 时,调试器发现了一些糟糕的事情,输出窗口显示如下消息:“Additional information:SendKey cannot run inside this application because the application is not handling windows messages. Either change the application to handle messages, or use the SendKeys.SendWait method.”(无法在此应用程序中运行,因为该应用程序无法处理 Windows 消息。请更改该应用程序以处理消息,或使用 SendKeys.SendWait 方法。)现在,我称之为友好的错误消息!希望大家引以为戒。但是,在您尝试使用 SendKey 之前,我要郑重告知大家,它没有 SendInput可靠。通过针对不同的应用程序(如 Microsoft Internet Explorer、Notepad、MFC 窗体视图或其它喜欢的应用程序)对 Typematic 和 Typematic.NET 进行测试,您自己就可以发现这个问题。出于某些原因,SendKey 并不能始终起作用。我怀疑是焦点和计时问题 ─ 键已经发送,但然后却蒸发了,因为在您看来拥有焦点的窗口实际上还没有获得焦点。因此,尽管与 SendInput 相比,SendKey 更容易使用,功能也更强大,但它不能在所有环境中很好地工作。唉!也许 Redmondtonians 会在下一个版本中修复这个问题。
一个最后警告:众所周知,发送击键来控制另一个应用程序是一种易出问题的方法。您必须正确地获取所有键,并且上下文或用户界面中的细微变化也会致使所有事情出错。如果您正在尝试控制另一个应用程序,请检查脚本系统、编程接口或宏语言。
问:我已经阅读了几篇有关如何禁用系统键组合(如 Ctrl+Alt+Del)的文章,包括您在 2002 年 9 月出版的 MSDN®Magazine中的文章。但是,我如何才能以编程方式发送 Ctrl+Alt+Del?
William Burns
答:第一个问题演示了如何向任何应用程序中发送击键,但是您无法使用 SendInput 发送 Ctrl+Alt+Del,因为该组合是在操作系统的底层进行处理的。总之,组合击键不是“发送”Ctrl+Alt+Del 的适当方式。您要尝试做什么呢?如果您要调用任务管理器,请使用“taskmgr.exe”调用 ShellExecute。如果正在尝试重新启动,您可以使用 EWX_REBOOT 标记调用 ExitWindowsEx。ExitWindowsEx 具有所有类型的标记,这些标记用于以不同方式关机或重新启动计算机(参见 图 4)。而且,要重新启动,您的应用程序需要 SE_SHUTDOWN_NAME 权限。
有人知道 Ctrl+Alt+Del 的出处吗?如果您知道,请给我发邮件。我将在以后的专栏中提供答案。
问:我可以从 MFC 应用程序中调用 .NET Framework 吗?我想从非托管 MFC 代码中调用托管类,并且我已经尝试通过 #using
Julian Kinsey
答:当然,您可以从 MFC 应用程序中调用 .NET!正如 Windows 中的其他所有事情一样,只要您了解其中正确的“魔法”,这就是轻而易举的事。在您首次创建一个 MFC 应用程序时,“应用程序向导”为您设置所有种类的编译器选项。C/C++ 代码生成器中的其中一个选项是“基本的运行时检查”。在您创建 MFC 应用程序时,“应用程序向导”在您的调试版本中选择“Both (/RTC1, equiv. to /RTCsu)”进行运行时检查(如检查错误的堆栈框架、未初始化的变量或缓冲区超限和不足等)。这些检查与 /clr 不兼容,因为托管代码完全不同(它是 Microsoft 的中间语言,而不是本地语言),但是在您添加 /clr 时,IDE 不会自动删除 /RTC1。因此,您必须手动删除。
对于项目中的每个 .cpp 文件,将“基本的运行时检查”设置为“默认”。这有点不合适,因为对于本地/非托管函数来说,这种检查是好事。它们帮助您在发布程序之前查找其中的错误。如果仅仅因为想调用一两个 .NET 类就不进行检查,就有点可惜了。是否还有别的方法可以对 .NET 和运行时进行检查?
问题在于使用托管扩展调用框架,您必须将“使用托管扩展”设置为“是”(设置 /clr),并且您只能在项目设置中进行这种更改,它是全局性的。使用托管扩展在您的项目中打开所有模块的 /clr。默认情况下,现在您的所有函数都是托管的。如果您将本地模式设为默认,则可在 stdafx.h 结尾添加一行:
#pragma unmanaged 由于每个模块都包括 stdafx.h,现在您的所有模块都以之前一直使用的本地模式进行编译。在调用框架时,您可以翻转托管代码,如下所示:
#pragma managedvoid DoSomethingWithDotNET(...) { // call framework classes here // safe from managed functions} 到目前为止,我一直使用该技巧(将 #pragma unmanaged 放到 stdafx.h 的结尾)。但是,它没有解决运行时检查问题,因为 /clr 仍然不与 /RTC 兼容,即使您的大部分函数是本地的。在混合模式应用程序中,编译器不会让您说“需要时在本地函数中进行运行时检查。”

图 5项目设置
实际上您所需做的只是与实际调用框架的 /clr 兼容。但是在“使用托管扩展”出现在项目范围设置中时,该如何做呢?很简单:您可以始终在模块构建属性的命令行处添加特定的转换。图 5和图 6演示了如何进行这种设置。在您的全局项目设置中,将“使用托管扩展”设置为“否”,然后向每个调用框架的模块中添加 /clr。现在,其他所有模块仍然可以使用 /RTC1,并能很好地进行运行时检查。当然,现在必须将
#using #using #include using namespace System; 然后,在调用框架的每个模块中只有 #include “UsesDotNet.h”。您仍然可以在一个模块中使用 #pragma managed/unmanaged 来混合托管代码和本地代码。
还有另一层含义。在向其中一个模块中添加 /clr时,除关闭运行时检查之外,您还必须选择“Not Using Precompliled Headers”。(不使用预编译标头)只有所有模块具有相同的编译选项时,预编译标头信息才能工作;如果一个模块具有 /clr,则它不能使用与其他没有 /clr 的模块相同的预编译标头。嗨!最近计算机发展日新月异,还有谁需要预编译标头呢?如果您真的喜欢,您可以始终通过不同的标头文件(如 UsesDotNet.h)来编译托管模块。
对于使用我所描述方法的实际工作项目,请查看本专栏第一个问题中的 Typematic.NET 应用程序。您可以从 MSDN MagazineWeb 站点下载该应用程序。通常都是以普通的本地方式,通过 stdafx.h 使用预编译标头对 Typematic.NET 中的所有模块进行编译,并且这些模块没有托管扩展。SendString 是调用 .NET Framework 的唯一一个函数,它位于相当独立的 SendString.cpp 模块中。该模块使用托管编译处理 /clr,没有运行时检查,也没预编译标头。好好干吧!
- 对前面对于引用的c++入门教程的补充说明
- 基于VC++的OpenGL编程讲座之曲线和曲面
- C++箴言:拷贝一个对象的所有组成部分
- VC#2005快速入门之使用do语句
- MFC六大关键技术剖析之动态创建
- 利用VC实现图像的特殊显示效果
- 轻轻松松学习C++ 标准模板库STL
- 聆听混沌的声音
- C++/VC++编程的疑难问题及解答
- 为C++程序添加文件保存加载功能
- 1.Borland 发布C++ Builder 2006 RAD 环境
- 2.C/C++程序员应聘常见面试题深入剖析
- 3.Visual C++常用数据类型转换详解
- 4.C++中的 static 关键字
- 5.利用VC++实现局域网实时视频传输
- 6.浅谈C/C++内存泄漏及其检测工具
- 7.英国投票否决C++/CLI 微软强攻ISO标准受挫
- 8.VC++下用MSComm控件实现串口通讯
- 9.伪随机数生成及在VC++中的实现
- 10.VC++编程实现对波形数据的频谱分析
- 大话G游 专题:手机病毒揭密
- ARP攻击防范与解决方案 路由故障处理手册
- Picasa中文版_Picasa教程 专题:清除流氓软件
- Firefox专题 seo搜索引擎优化专区
- 重装Windows必知的事情 装机之必备软件大行动
