50岁买保险要交多少钱:SetForegroundWindow及类保护

来源:百度文库 编辑:中财网 时间:2024/05/10 07:03:43

SetForegroundWindow及类保护

时间:2010-11-29 彭德奎

1、列表视图模式

2、SetForegroundWindow

3、类保护

我试图使用C++/MFC自定义文件打开对话框。是否有一种办法能在打开/保存对话 框启动时改变列表视图的类型?启动时默认的是列表视图, 这个视图没什么用 。我希望程序启动对话框时采用详细资料视图,或最好是用户最后一次使用的视 图。您能推荐一种方法吗?

Udi Mishan

当然,在 Windows 中总 是有办法的。当我第一次看你的问题时,我想那很容易。只要在 WM_INITDIALOG 消息处理函数中获取列表视图,然后将其视图模式设置为详细资料即可。但在 Windows 中你经常会碰到逻辑上可行,实际做起来行不通。上述做法有三个问题 。

问题一是获取列表视图。个别读者已经问到过这个问题,因为它显得 有价值了。使用Microsoft Spy++你可以发现,列表控制不是对话框的直接子类 ,它是孙子类。Spy++运行的屏幕截图 Figure 1 显示了文件打开对话框的真实 的窗口层次。你可以看到,主对话框有一个子窗口,类的名字为 SHELLDLL_DefView。接着依次包括文件和文件夹的列表控制。(我第一次提到 SHELLDLL_DefView 是在2002年2月专栏)SHELLDLL_DefView的 ID 是 lst2 (值 为 0x0461, 在 dlgs.h 中定义),但它不是列表框或列表控制。真正的 SysListView32 是 SHELLDLL_DefView 的 孩子,子 ID 为 1。

Figure 1 窗口层次

问题二是当你的对话框获得 WM_INITDIALOG 时,结合列表控制/SHELLDLL_DefView的窗口还不存在。当你得到CDN_INITDONE 时,它依然不存在 ,尽管这个消息的意思是打开对话框已完成初始化。好了, 实验是最好的证明:要想Windows做了什么,唯一的途径是做一个实验,或是阅 读 MSDN 杂志。Figure 2 是我编写的用来说服自己确信列表控制并不存在的测 试对话框。CMyOpenDlg 有一个函数叫 SetListView,顾名思义。此函数也显示 TRACE 诊断信息 ,指示它能否找到列表控制。Figure 3 是 TRACE 流输出的结 果,当 WM_INITDIALOG 或 CDN_INITDONE 到来时列表视图不存在。两种情况下 GetDlgItem 都返回 NULL。那么你该怎么做呢?最简单的 做法是让你的对话框 给自己发个消息:

BOOL CMyOpenDlg::OnInitDialog()
{
  CFileDialog::OnInitDialog();
  PostMessage (MYWM_POSTINIT,0,0);
  return TRUE;
}

Figure 3 Trace

MYWM_POSTINIT是我自己定义的消息 (WM_USER+1),其处理函数调用 SetListView,因为 OnInitDialog 用 PostMessage 发送消息,而不是 SendMessage。Windows 一直到所有的其它未决 的消息已经处理之后才会处理 MYWM_POSTINIT 消息。到那时,打开对话框已经 设置妥当,而且 SetListView 成功地获得列表控制。

第三个问题,一旦 你最后拥有了列表控制,你怎样设置它的视图模式?你可能想你所要做的只是调 用SetView/LVM_SETVIEW。唉,这样做行不通。列表视图为空白。 难道是一个画 面刷新问题吗?也许吧。但如果你仔细想一想,你将意识到发送 LVM_SETVIEW 充满危险。记得 SHELLDLL_DefView 窗口吗?不难想象它维持着 有关它的列 表控制的某种状态信息。如果你直接操作列表控制,它怎样知道你做了什么?无 论怎样,它都不工作。你必须另辟蹊径。

Figure 4 使用 Spy++

幸运的事,这种情况找到解决方法并不难。 Spy++ 又一次帮上大忙。用 Spy++ 进行深入探究后,揭示出当用户操作下拉菜 单,如 Figure 4 所示,选择不同的视图时会发生什么。对话框发送了一个 WM_COMMAND 消息给 SHELLDLL_DefView,命令ID= 0x702c 。所以为了将视图改 为详细资料视图,你要做的是发送一个 WM_COMMAND 消息到外壳窗口,而不是列 表控制:

CWnd* pshellwnd = dlg->GetDlgItem(lst2);
  pshellwnd->SendMessage(WM_COMMAND, ODM_VIEW_DETAIL); // 0x702c

经历了所有的惊愕以及重重艰难险阻,最终的解决方案很简单 。你可以用 Spy++ 来检验每个视图模式的命令代码。我是个大好人,为你做了 这些。结果参见 Figure 5 。我在 Windows XP 中对它们进行了调用;Windows 的其它版本有一或两个别的代码。Figure 2 是 CMyOpenDlg 的源码,它以详细 资料视图模式调用文件打开对话框。 至于如何保存不同用户会话的列表模式, 我将它作为一个练习由你自己解决;实现细节很简单。如通常一样,你可以从本 文顶部的链接处下载完整的测试程序源代码。

我想在 .NET 框架中用 C# 编写一个程序,该程序要激活另一个窗口。在 Windows/MFC 中我可以调用 SetActiveWindow 函数来实现。在.NET 框架中我该怎么做呢?

John McCormick

你可以调用 Form.Activate 来激活你自己的窗体,但惊奇的 是,在 .NET Framework 中没有函数可以激活属于另外一个进程或程序的窗体。 不要害怕,任何时候,只要.NET Framework无法满足你的需要,你通常都可以使 用托管(interop)机制直接与Windows 交互。目前情况下,你需要的函数是 SetForegroundWindow。它带唯一的参数——你想激活的窗 口的句柄 (HWND).

using System.Runtime.InteropServices;
public class MyClass {
 [DllImport("user32.dll")]
  public static extern void
  SetForegroundWindow(IntPtr hwnd);
}

在你的代码中使用此托管申明,并且假设你已经拥有 了你希望激活的窗口的句柄,你要做的是调用 SetForegroundWindow:

IntPtr hwnd = // get HWND
SetForegroundWindow (hwnd);

你怎样获得窗口句柄呢?根据你的程序的工作方式,有许多 方法可以做到,但最通用的一种方法是调用 FindWindow,你可以用这个 API 函 数由窗口的标题或类名获得窗口句柄,在此你又要在 C# 中用到托管:

public class MyClass {
 [DllImport ("user32.dll")]
 public static extern IntPtr
   FindWindow(String classname, String title);
}
classname 是 Window 注册的窗口类的名字,title 是窗口标题。这些参数只能有一个为 NULL ,不能全为 NULL。

我怎样才能在编译时阻止其它的类从我的 C++ 类派 生? 例如,我有一个类:class MyClass { };

如果有人试图像 这样申明一个类

class Derived : public MyClass { };

我 希望此时编译器抛出一个错误,可以实现吗?

Asha Udupa

在C#中, 有一个关键字正好是你想要的: sealed。当你的C#类被限定为 sealed 类型,相 当于你告诉编译器无人能从这个类派生其它类。 例如,

sealed class MyClass { ... }

意思是说没有人能从 MyClass 派生出别的类。 在.NET Framework中许多(一些人说太多)类自身是密封的。

但你的问题 的是 C++,而不是C#。啊哈,C++中可没有 sealed 这个关键字(至少现在还没有 ——我得到消息它不久将被加入到官方标准中)。但有一个相当简单 的方法完成同一个目的 。只要你把构造函数申明为 private 即可:

class MyClass {
private:
 MyClass() { ... }
 MyClass(int arg) { ... }
};

这样便没有办法从 MyClass 派生新类。因为没法实例化它。等一等——如果没法实例化你的类。 那别人如何使用它呢?问得好。答案是你必须添加静态函数 来创建类的实例。

class MyClass {
public:
 static MyClass* CreateInstance() {
  return new MyClass();
 }
private:
 MyClass() { }
};

现在任何人可以调用 MyClass::CreateInstance 来创建你的类的实例,但没有人能从它派生。这种方 法在大多情况下工作得很好,可它有一个 缺点:你很难在堆栈中创建 MyClass 实例。要解决这个问题,你需要一个稍微复杂的解决方案:

class MakeSealed {
private:
  MakeSealed () { }
  friend class MyClass;
};
class MyClass : virtual MakeSealed { };

现在除了 MakeSealed 的友元类 MyClass 以外,无人能创建 MakeSealed 实例。你能在堆栈中创建 MyClass 的实例 ,但你不能从MyClass 派生。你可以使用 MakeSealed 来使其它成为密封类,但同时必须添加它们为友 元。MyClass 以 MakeSealed 为虚拟基类 ,以便于你在使用多重继承时不会出 现问题。相当聪明,不是们吗?编程快乐!

本文配套源码