分类: C/C++
2008-08-07 17:41:25
CWindowDC dc(NULL); // 用 NULL 获取整个屏幕 CDC memdc; ... // 创建, 初始化 memdc memdc.BitBlt(..., &dc); // 拷贝屏幕内容若要复制当前活动窗口的内容,只要获取该窗口的 CWnd 指针,然后用它来构造一个 CWindowDC,即可从中提取内容。
// 热键的处理方法上述代码段展示了一个典型的 MFC CMainFrame 类实现。OnCreate/OnDestroy 函数用来注册/注销 IDHOT_SNAPDESKTOP 热键;OnActivate 函数用来在应用程序 处于激活/和非激活状态时注册/注销 IDHOT_SNAPWINDOW 热键。当你的窗口处于非激活状态时,通过重新启用 IDHOT_SNAPWINDOW,当别的应用程序拥有焦点时,用户仍然能用 Alt Print Screen 来复制屏幕。
// MainFrame.h #include "FolderFrame.h" #include "resource.h" //////////////// // Typical MFC Main frame window, override to disable PrintScreen. // class CMainFrame : public CFrameWnd { protected: ... afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // disable PrintScreen afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); afx_msg LRESULT OnHotKey(WPARAM wp, LPARAM lp); afx_msg void OnDestroy(); DECLARE_MESSAGE_MAP() }; MainFrame.cpp #include "StdAfx.h" #include "MainFrm.h" #include "View.h" IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ... // disable PrintScreen: ON_WM_CREATE() ON_WM_DESTROY() ON_WM_ACTIVATE() ON_MESSAGE(WM_HOTKEY, OnHotKey) END_MESSAGE_MAP() ... int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... RegisterHotKey(m_hWnd, IDHOT_SNAPDESKTOP, 0, VK_SNAPSHOT); return 0; } void CMainFrame::OnDestroy() { UnregisterHotKey(m_hWnd, IDHOT_SNAPDESKTOP); } ////////////////// // Handle hotkey: should be PrintScreen or Alt-PrintScreen. // Do nothing (bypass Windows screen capture) // LRESULT CMainFrame::OnHotKey(WPARAM wp, LPARAM) { UNREFERENCED_PARAMETER(wp); return 0; // ignore } ////////////////// // When window is activated/deactivated, disable/enable Alt-PrintScreen. // (IDHOT_SNAPWINDOW) // void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) { CFrameWnd::OnActivate(nState, pWndOther, bMinimized); if (nState) RegisterHotKey(m_hWnd, IDHOT_SNAPWINDOW, MOD_ALT, VK_SNAPSHOT); else UnregisterHotKey(m_hWnd, IDHOT_SNAPWINDOW); }
在 2004 年 11 月的专栏中,你谈到了在托管和非托管代码中调用虚拟函数的问题,参见“调用虚拟函数,持续化视图状态,POD
类型概念”。在 C 里,如果我想让派生类的析构函数在释放内存时被调用,我得在基类中将其声明为虚拟函数。那么在 Visual Studio 2005
中,对于某个派生类来说,即使它在基类中未被声明为虚拟的,其析构也会被调用吗?
ctor: CBase ctor: CDerived dtor: CDerived dtor: CBase
这个信息说明了即使派生类和基类都不把析构函数声明为虚拟,派生类的析构都会被调用。构造函数和析构函数按期望的顺序被调用,先调用基类构造函数,最后是析构函数。
为什么要将托管析构函数声明为虚拟的呢?回想一下每一个托管类要么必须显式地从另一个托管类派生,要么隐式地从根基类 Object
派生。还要记住的是 C 编译器将托管析构转换为 Finalize 方法,该方法在 Object 类中是虚拟的。为了明白这一点,你只要用
ILDASM 反汇编器察看一下所编译的代码既可。
Figure 3 反汇编后的 vdtor.exe
Figure 3 展示了 vdtor.cpp 的反汇编代码。CBase 和 CDerived 都有 Finalize 方法;Figure 4
是派生类 CDerived 的 Finalize 方法。它还说明了编译器为每个类创建了一个特殊的 __dtor 方法。该方法在你调用
delete 时被调用。如果你检查一下主入口函数 main 的微软中间语言(IL)代码,你会看到如下的代码行:
// delete pBase; IL_0008: ldloc.0 IL_0009: call instance void CBase::__dtor()
当你 delete 某个托管对象时,编译器产生一个对 __dtor 方法的调用。但编译器调用的是哪个 __dtor 方法呢?因为我将 pBase 声明为一个 CBase 指针(CBase*),编译器便调用 CBase::__dtor,正如前面的代码段所示。这似乎就是说 CDerived 在析构期间被旁路掉了,直到你看到 CBase::__dtor 实现:
// in CBase::__dtor() IL_0000: ldarg.0 IL_0001: call void [mscorlib]System.GC::SuppressFinalize(object) IL_0006: ldarg.0 IL_0007: callvirt instance void CBase::Finalize() IL_000c: ret
__dtor 函数用 callvirt 调用 Finalize,即使你从没听说过IL,也能猜到那是一个调用虚拟方法的指令。无论实际对象是哪个,公共语言运行时(CLR)都调用 Finalize 方法——此处是 CDerived。为了强制期望的 C 析构语义,每个 Finalize 方法显式地调用其基类的 Finalize 方法,正像你在 CDerived::Finalize 所看到的那样:
// in CDerived::Finalize() IL_000b: ldarg.0 IL_000c: call instance void CBase::Finalize()
这里编译器产生一个常规调用指令,而非 callvirt。否则你的程序将出现死循环直到耗尽堆栈。
你会注意到在调用 Finalize 方法之前,CBase::__dtor 调用 SuppressFinalize,为什么呢?因为在 C
里,当你清除某个托管对象时,系统不会释放那个对象的存储区。至少不会立即释放。在垃圾收集器运行之前,该对象的内存不会被释放。必须用 SuppressFinalize
来避免对象被终结两次——第一次是调用 delete 时,再一次是垃圾收集器运行的时候。明白吗?
有没有办法从 .NET 框架程序集中调用 MFC 扩展 DLL?我知道如何用 P/Invoke 来调用常规的 DLL 或 COM
DLL,但是不知道如何处理 MFC 扩展 DLL。
// mark entire assembly as CLS-compliant [assembly:CLSCompliant(true)];
为了标记特定的类或方法是 CLS 兼容的,你得对类/方法应用这个属性。
唉!虽然 C 编译器识别 CLSCompliant 属性,但它无法检查兼容性。也就是说,即使你标记了它们的兼容性,C
编译器对于非兼容代码不会报错。有一个单独的工具叫 FxCop,这个工具就像托管程序集的绷带(应该有人知道绷带是什么东西吧),然而,FxCop
虽然功能很强,会检查并报错(比如,空析构函数,以“C”开头的类名以及变量名包含非英文字符等),但也它不检查 CLS
兼容性,检查兼容性对你来说更有用。所以在我写此文时,恐怕没有什么好办法自动检查托管 C 程序的 CLS 兼容性。
顺祝编程愉快!
您的提问和评论可发送到 Paul 的信箱:cppqa@microsoft.com.