一个最后警告:众所周知,发送击键来控制另一个应用程序是一种易出问题的方法。您必须正确地获取所有键,并且上下文或用户界面中的细微变化也会致使所有事情出错。如果您正在尝试控制另一个应用程序,请检查脚本系统、编程接口或宏语言。问:我已经阅读了几篇有关如何禁用系统键组合(如 Ctrl+Alt+Del)的文章,包括您在 2002 年 9 月出版的 MSDN&reg;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 <mscorlib.dll> 来调用,但是我得到一个消息“/RTC1 incompatible with /clr.”我如何才能从 MFC 应用程序中调用 .NET?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 managed
void DoSomethingWithDotNET(...) {
   // call framework classes here
   // safe from managed functions
}到目前为止,我一直使用该技巧(将 #pragma unmanaged 放到 stdafx.h 的结尾)。但是,它没有解决运行时检查问题,因为 /clr 仍然不与 /RTC 兼容,即使您的大部分函数是本地的。在混合模式应用程序中,编译器不会让您说“需要时在本地函数中进行运行时检查。”图 5项目设置
实际上您所需做的只是与实际调用框架的 /clr 兼容。但是在“使用托管扩展”出现在项目范围设置中时,该如何做呢?很简单:您可以始终在模块构建属性的命令行处添加特定的转换。图 5 和图 6 演示了如何进行这种设置。在您的全局项目设置中,将“使用托管扩展”设置为“否”,然后向每个调用框架的模块中添加 /clr。现在,其他所有模块仍然可以使用 /RTC1,并能很好地进行运行时检查。当然,现在必须将 <mscorlib.dll> 和其他所有框架文件从 stdafx.h 移动到托管模块中。如果您愿意,您可以将所有标准的 .NET 包括在一个 UsesDotNet.h 文件中,如下所示: #using 
#using 
#include 
using namespace System;图 6模块设置
然后,在调用框架的每个模块中只有 #include “UsesDotNet.h”。您仍然可以在一个模块中使用 #pragma managed/unmanaged 来混合托管代码和本地代码。还有另一层含义。在向其中一个模块中添加 /clr时,除关闭运行时检查之外,您还必须选择“Not Using Precompliled Headers”。(不使用预编译标头)只有所有模块具有相同的编译选项时,预编译标头信息才能工作;如果一个模块具有 /clr,则它不能使用与其他没有 /clr 的模块相同的预编译标头。嗨!最近计算机发展日新月异,还有谁需要预编译标头呢?如果您真的喜欢,您可以始终通过不同的标头文件(如 UsesDotNet.h)来编译托管模块。对于使用我所描述方法的实际工作项目,请查看本专栏第一个问题中的 Typematic.NET 应用程序。您可以从 MSDN Magazine Web 站点下载该应用程序。通常都是以普通的本地方式,通过 stdafx.h 使用预编译标头对 Typematic.NET 中的所有模块进行编译,并且这些模块没有托管扩展。SendString 是调用 .NET Framework 的唯一一个函数,它位于相当独立的 SendString.cpp 模块中。该模块使用托管编译处理 /clr,没有运行时检查,也没预编译标头。好好干吧!请将给 Paul 的问题和评论发送至[email protected]。Paul DiLascia 是自由作家、顾问以及最权威的 Web/UI 设计师。他是 Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992) 一书的作者。您可以通过 www.dilascia.com 与Paul 取得联系。