MFC对话框的“双胞胎”控件:隐藏了一个,还有一个在看我

缘起

前段日子,遇到两个非常有意思的 MFC 对话框相关的 bug

  1. ComboBox 控件设置了文本,但是却显示为空
  2. 明明已经隐藏了控件,但是控件却没有隐藏

最后发现这两个问题是同一个问题,对话框中存在相同 ID 的控件。因为这两个问题是同一个问题,本文只介绍其中一个问题的排查思路。

说明: 实际项目中的情况要复杂的多,本文用到的例子都是我准备的示例程序

初遇错误

程序会在某些情况下显式/隐藏某些特定控件,但是却发现对应的控件没有被隐藏,如下图:

can-not-hide-control

对应的控制代码也非常简单,如下:

1
2
3
4
5
6
7
8
CString strTitle;
GetDlgItem(IDC_BTN_SHOW_HIDE)->GetWindowTextW(strTitle);

int showStatus = (strTitle == L"Show") ? SW_SHOW : SW_HIDE;
GetDlgItem(IDC_EDIT_TEXT)->ShowWindow(showStatus);

strTitle = (strTitle == L"Show") ? L"Hide" : L"Show";
GetDlgItem(IDC_BTN_SHOW_HIDE)->SetWindowTextW(strTitle);

反复确认代码逻辑,没有问题。那会是什么原因呢?GetDlgItem() 获取的控件不对?还是调用 ShowWindow() 失败了?还是其它什么原因?

深入调查

通过查看 ShowWindow() 的返回值,发现对 ShowWindow() 的调用是成功的。GetDlgItem() 获取的控件句柄是 0x00020a7c

search-target-window

通过 spyxx 的窗口搜索功能,在 句柄(A): 对应的位置输入 0x00020a7c,可以发现控件确实已经被隐藏了。

那么显示着的控件又是哪一个呢?再用 spyxx 的窗口搜索功能,拖动靶心到对应的窗口上,可以发现控件句柄是 00060a5e,在对应的列表项上右键,突出显式,可以确认显示着的控件句柄确实是 0x00060a5e

highlight-visible-control

难道同一个位置有两个一模一样的控件?赶紧查看一下 rc 文件。

查看 rc

果然,在 rc 文件中有重复的控件。

1
2
3
4
5
6
7
8
9
10
11
12
IDD_REPEATCONTROLID_DIALOG DIALOGEX 0, 0, 272, 110
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "RepeatControlId"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "退出",IDOK,215,89,50,14
EDITTEXT IDC_EDIT_TEXT,89,7,176,15,ES_AUTOHSCROLL //<----
EDITTEXT IDC_EDIT_TEXT,89,7,176,15,ES_AUTOHSCROLL
PUSHBUTTON "Hide",IDC_BTN_SHOW_HIDE,7,7,73,15
LTEXT "点击按钮会隐藏右侧文本框,...",IDC_STATIC,7,26,258,59
END

说明: 实际项目中,对话框中的控件数量超级多,不像示例程序这么明显,一眼就能看出来有重复控件

至此,可以结案了,因为 rc 中有重复的控件,所以导致在隐藏控件时,只隐藏了一个。另外一个 bug 也是同样的问题,不再赘述。

警示

按理说,同一个对话框中存在相同 ID 的控件,编译的时候至少会报警告。重新编译程序,观察编译警告,果然发现有提示:

1>RepeatControlId.rc(106): warning RC2182: duplicate dialog control ID 1000

一定要注意编译器的警告啊!

亲自动手

示例程序代码已经上传到了github,感兴趣的朋友可以下载体验。

总结

  • 同一个对话框中不要存在有相同 ID 的控件

  • spyxx 是查看窗口句柄的好工具,一定要善加利用

  • 务必注意编译器的警告信息!

BianChengNan wechat
扫描左侧二维码关注公众号,扫描右侧二维码加我个人微信:)
0%