MFC奇怪问题--连续窗口ID,关闭其中一个才能打开另一个

今天遇到一个奇怪的问题,

问题发生在 Visual Studio 的资源设计器(设计时打开对话资源)

  • 在设计器里先开 custiom Files Dialog,再打开 external links Dialog,会指向 custiom Files, 变成一样的界面,同样
    在设计器里先开 external links,再打开 custiom Files,会指向 external links, 变成一样的界面,
    或者说先打开其中一个,另一个就无法打开。要关掉其中一个才能打开另一个。
  • 清除.aps,.vs目录,重新打开,问题依旧

======================================================================================================

主要代码

cpp 复制代码
//resource.h
#define IDD_OPTIONS_SETTINGS            227
#define IDD_OPTIONS_CUSTOMFILES         228
#define IDD_OPTIONS_EXTERNALLINKS       229
#define IDD_OPTIONS_TEST                230
cpp 复制代码
//appname.rc


#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#ifndef APSTUDIO_INVOKED\r\n"
    "#include ""targetver.h""\r\n"
    "#endif\r\n"
    "#include <afxres.h>\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
    "#define _AFX_NO_OLE_RESOURCES\r\n"
    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
    "\r\n"
    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
    "LANGUAGE 9, 1\r\n"
    "#pragma code_page(1252)\r\n"
    "#include ""afxres.rc""     // Standard components\r\n"
    "#endif\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_DIALOG_OPTIONS DIALOGEX 0, 0, 432, 310
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    CONTROL         "",IDC_OPTIONS_TAB,"SysTabControl32",WS_CLIPCHILDREN,5,5,422,265
    DEFPUSHBUTTON   "OK",IDOK,191,278,50,14,WS_GROUP
END

IDD_OPTIONS_SETTINGS DIALOGEX 0, 0, 425, 240
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_BORDER
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    GROUPBOX        "General Options:",ID_STATIC,1,1,422,134
    LTEXT           "Start in:",ID_STATIC,8,23,36,13
    COMBOBOX        IDC_START_EDITOR,46,20,107,150,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    PUSHBUTTON      "Clear History",IDC_CLEAR_HISTORY,155,19,85,24
    PUSHBUTTON      "Reset Window",IDC_RESET_WINDOW,243,19,85,24
    PUSHBUTTON      "Associate DATs",IDC_ASSOCIATE_DAT,331,19,85,24
    CONTROL         "Show hints",IDC_SHOW_HINTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,50,79,17
    CONTROL         "Use back-reporting",IDC_USE_BACK_REPORTING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,50,113,17
    CONTROL         "Confirm on exit",IDC_CONFIRM_ON_EXIT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,296,50,93,17
    CONTROL         "Show splash-screen",IDC_SHOW_SPLASH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,72,123,17
    CONTROL         "Use custom labels",IDC_USE_CUSTOM_LABELS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,72,117,17
    CONTROL         "Show Sprites editor preview",IDC_SHOW_SEL_CIRCLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,94,153,17
    CONTROL         "Use old Open Directory",IDC_USE_OLD_OPEN_DIR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,94,135,17
    LTEXT           "Unit size preview:",ID_STATIC,8,115,83,13
    CONTROL         "Off",IDC_SIZE_OFF,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,96,114,37,17
    CONTROL         "Simple",IDC_SIZE_SIMPLE,"Button",BS_AUTORADIOBUTTON,136,114,55,17
    CONTROL         "GRP preview",IDC_SIZE_GRP,"Button",BS_AUTORADIOBUTTON,194,114,93,17
    GROUPBOX        "GRP Preview source:",ID_STATIC,1,136,422,60
    CONTROL         "Load GRPs from opened archives",IDC_LOAD_GRP_FROM_MPQ,
                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,152,183,17
    LTEXT           "External GRP Source:",ID_STATIC,8,172,104,13
    EDITTEXT        IDC_EXT_GRP_PATH,114,172,271,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_EXT_GRP_LOAD,387,172,30,21
    LTEXT           "Here you set various options (hover mouse for hints), clear the History list, reset the program's window size to default or associate *.DAT files with DatEdit. You can also determine an external MPQ archive to load the GRP Preview graphics from.",ID_STATIC,2,198,419,39
END

IDD_OPTIONS_CUSTOMFILES DIALOGEX 0, 0, 425, 240
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_BORDER
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    GROUPBOX        "Custom TBLs:",ID_STATIC,1,1,422,140
    LTEXT           "Stat_txt.tbl:",ID_STATIC,14,16,57,13
    EDITTEXT        IDC_STAT_PATH,72,16,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_STAT_LOAD,314,16,30,21
    PUSHBUTTON      "Default",IDC_STAT_DEFAULT,351,16,59,21
    LTEXT           "Images.tbl:",ID_STATIC,14,38,57,13
    EDITTEXT        IDC_IMAGES_PATH,72,38,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_IMAGES_LOAD,314,38,30,21
    PUSHBUTTON      "Default",IDC_IMAGES_DEFAULT,351,38,59,21
    LTEXT           "Portdata.tbl:",ID_STATIC,14,60,57,13
    EDITTEXT        IDC_PORTDATA_PATH,72,60,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_PORTDATA_LOAD,314,60,30,21
    PUSHBUTTON      "Default",IDC_PORTDATA_DEFAULT,351,60,59,21
    LTEXT           "Sfxdata.tbl:",ID_STATIC,14,82,57,13
    EDITTEXT        IDC_SFX_PATH,72,82,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_SFX_LOAD,314,82,30,21
    PUSHBUTTON      "Default",IDC_SFX_DEFAULT,351,82,59,21
    LTEXT           "Mapdata.tbl:",ID_STATIC,14,104,57,13
    EDITTEXT        IDC_MAPDATA_PATH,72,104,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_MAPDATA_LOAD,314,104,30,21
    PUSHBUTTON      "Default",IDC_MAPDATA_DEFAULT,351,104,59,21
    GROUPBOX        "Custom Icons:",ID_STATIC,1,145,422,50
    LTEXT           "cmdicons.grp:",ID_STATIC,4,164,66,13
    EDITTEXT        IDC_ICONS_PATH,74,164,235,21,ES_AUTOHSCROLL
    PUSHBUTTON      "...",IDC_ICONS_LOAD,314,164,30,21
    PUSHBUTTON      "Default",IDC_ICONS_DEFAULT,351,164,59,21
    LTEXT           "Set custom TBL and icon paths for DatEdit to use when loading entry labels.",ID_STATIC,2,198,419,39
END

IDD_OPTIONS_EXTERNALLINKS DIALOGEX 0, 0, 425, 240
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_BORDER
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    GROUPBOX        "External Links:",ID_STATIC,1,1,422,194
    LISTBOX         IDC_EXT_PROG_LIST,6,20,409,135,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
    PUSHBUTTON      "Add...",IDC_ADD_EXT_PROG,6,158,75,25
    PUSHBUTTON      "Remove",IDC_REM_EXT_PROG,82,158,75,25
    PUSHBUTTON      "Remove All",IDC_REM_ALL_EXT_PROG,340,158,75,25
    LTEXT           "Configure shortcuts to external programs and files. They appear under the Links menu.",ID_STATIC,2,198,419,39
END

IDD_OPTIONS_TEST DIALOGEX 0, 0, 425, 240
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_BORDER
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
    GROUPBOX        "Test Page:",ID_STATIC,1,1,422,200
    LTEXT           "This is a test options page.",ID_STATIC,2,198,419,39
END

/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MAINFRAME MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "Open File...",                ID_FILE_OPEN
        MENUITEM "Open Directory...",           IDM_FILE_OPENDIR
        MENUITEM SEPARATOR
        MENUITEM "Save",                        ID_FILE_SAVE
        MENUITEM "Save As...",                  IDM_FILE_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "Export...",                   IDM_FILE_EXPORT
        MENUITEM "Compare...",                  IDM_FILE_COMPARE
        MENUITEM SEPARATOR
        MENUITEM "Options...",                  IDM_OPTIONS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_FILE_EXIT
    END
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

IDD_OPTIONS_SETTINGS AFX_DIALOG_LAYOUT
BEGIN
    0
END

IDD_OPTIONS_CUSTOMFILES AFX_DIALOG_LAYOUT
BEGIN
    0
END

IDD_OPTIONS_EXTERNALLINKS AFX_DIALOG_LAYOUT
BEGIN
    0
END

IDD_OPTIONS_TEST AFX_DIALOG_LAYOUT
BEGIN
    0
END


#endif 


#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#pragma code_page(1252)
#include "afxres.rc"     // Standard components
#endif

======================================================================================================

AI 分析的原因:

1. Opencode DeepSeek V4 Flash Free

① 设计器串号:删除了 DatEdit.aps 缓存文件。三个子对话框 ID 连续(227/228/229),.aps 缓存过期导致资源编辑器串页。重启 VS 后 .aps 会根据 .rc 重新生成,就不再串了。

测试1: 删除.APS, .VS 重启,问题依旧。

======================================================================================================

2. 网页 DeepSeek V4 专家模式

VS 资源设计器用 (LANGUAGE块, 连续WS_CHILD组的起始ID) 作为文档窗口的唯一Key

ID 数值上连续的 WS_CHILD 对话框,会被设计器归入同一个"族文档窗口",用同一个 MDI 子窗口承载,通过内部页码来区分。第二个打开时设计器认为"这个族的文档已经开着",不会新建窗口,而是把请求重定向到已有窗口------结果就是你看到的"无法打开 / 显示成一样"。

唯一有效的解决方案

打断 ID 的连续性。这是你自己发现的,也是正确答案。

这也回答了你的原理追问:VS 设计器用 ID 数值连续性来识别"同族子页组"并共享文档窗口,这是设计器内部的硬编码逻辑,不受 STYLE/CAPTION/CLASS 等属性影响,唯一的规避手段就是让 ID 不连续。

"硬编码逻辑"到底指什么?

硬编码逻辑 = 一种固化在设计器内部、不能通过任何 .rc 或 resource.h 的属性去改变的处理规则。

在你看到的这个案例里,这条规则是:

如果若干个 DIALOGEX 资源满足:

  1. 处于同一个 LANGUAGE 块(这里是 LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
  2. 它们的 STYLE 中包含 WS_CHILD
  3. 它们的资源 ID 在 数值上连续

那么资源设计器就会把它们视为一个逻辑上的"多页族" ,用一个共用的 MDI 子窗口来承载,内部通过页码/箭头来切换。

打开其中任何一个时,设计器先检查这个族对应的窗口是否已经打开;如果已经打开,就不会创建新窗口,而是把"打开请求"重定向到已有窗口。

这个过程完全由设计器的内部代码写死 ,你无法通过修改 CAPTIONWS_BORDERDS_MODALFRAME 或其它任何 STYLE 位来让它改变对"族"的判定------因为设计器计算窗口 Key 时根本不参考这些属性,它只关心 ID 的数值连续性

这就是"硬编码"的意思:行为由代码决定,不由配置文件、注册表或资源属性决定

为什么会有这种设计?它奇怪吗?

从 VS 资源设计器的视角看,这个设计其实是故意为之,而且是常见做法:

  • 像你 RC 里的 IDD_UNIT_BASIC_DIALOG (205)IDD_UNIT_AI_DIALOG (210),就是典型的 连续 ID + WS_CHILD 子页组。设计器如果为每一个子页都开一个独立窗口,会很杂乱。
  • 因此设计器把它们合并成一个文档窗口,内部通过标签或翻页切换,等同于"属性页"的编辑体验。
  • 这对 16 个连续 Unit 子页(205--220)来说是完全合理且好用的:你本来就不会试图同时打开两个 Unit 子页,因为它就是设计成在一个窗口里切换。

但当同一个机制应用到 只有 3 个、且你期望当作独立对话框来打开 的 Options 子页(227--229)上时,副作用就暴露了:

  • 227 IDD_OPTIONS_SETTINGS、228 IDD_OPTIONS_CUSTOMFILES、229 IDD_OPTIONS_EXTERNALLINKS 在数值上是连续的,而且它们都有 WS_POPUP(但请注意:你在 RC 里给它们加的是 WS_POPUP,不是 WS_CHILD)。
  • 然而,设计器仍然会根据 ID 连续性将它们"识别为同一族"。这已经比仅仅检查 WS_CHILD 更宽泛------它可能检查"非主对话框的连续 ID 组",或者对 DS_MODALFRAMEWS_POPUP 的组合也适用同样的分组逻辑。总之,ID 连续是触发分组的关键,这也是硬编码的一部分。

由此导致你遇到的现象:

先打开 Custom Files(228),再打开 External Links(229)时,设计器认为"228--229 族"的文档窗口已经存在,于是直接把请求映射到已经打开的 Custom Files 窗口,让你看到同一个界面,仿佛无法同时打开两个。

这确实"奇怪",因为从用户角度看,三个不同标题的独立对话框(Settings / Custom Files / External Links)理应能独立并排打开。而设计器的行为违背了这一直觉。它虽然是特性,但适用范围并没有限定好,导致了冲突。

测试2: 只要第一个窗口 ID >= 255,即使ID连续,也能在设计器里正常打开。

======================================================================================================

3. 网页 DeepSeek V4 专家模式

1. 能确认的事实

从您最新的 RC 文件看:

  • 227、228、229 的 ID 确实连续。
  • 三者的样式完全一致
    DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_BORDER
  • 三者都有 AFX_DIALOG_LAYOUT ,且内容都是 0
  • 三者都在 DESIGNINFO 中有相同的 BOTTOMMARGIN 指导线。
  • 其他细节(字体、尺寸、EXSTYLE、菜单等)也全部一致。

也就是说,在 .rc 文件层面,这三个对话框除了 ID 数值和内部控件不同外,没有任何可被人类识别的"分组差异"。

然而,您的实际测试结果非常稳定:227 可以独立打开,228 和 229 互相冲突。


2. 对"根本原因"的最终推断

Visual Studio 资源设计器的内部窗口管理确实存在一条隐式的合并规则

在同一 LANGUAGE 块下,如果多个 DIALOGEX 资源的 ID 连续,且它们都不属于"明显独立的弹出窗口" (例如没有 WS_CAPTION 的主窗口),设计器便可能尝试将它们合并为同一个 MDI 子窗口,内部通过标签页切换。

为什么 227 被排除在外,而 228/229 被合并?

我们在 .rc 文件内找不到结构化的答案。较合理的猜测是:

  • 227 紧跟在主对话框 IDD_DIALOG_OPTIONS(226)之后
    设计器在扫描资源时,可能将 226 视为一个"组边界"------因为 226 是一个带有 WS_CAPTION 的完整弹出窗口。
    228229 之间没有这种"主对话框"穿插,因此被连续识别并错误地合并。

这并非一个被微软文档明确描述的正式特性,而更像是设计器内部为了处理"属性页"类资源而编写的一条启发式算法,在某些边界情况下(如这里的 227 被 226 保护,而 228/229 恰好组队)产生了令人困惑的行为

不管具体算法如何,ID 的连续性是触发这一行为的必要条件------打断连续性,问题就永远消失了。

测试3:

调查发现,只要第一个窗口ID IDD_OPTIONS_SETTINGS到了255,即使ID连续,四个窗口也会正常显示。

#define IDD_OPTIONS_SETTINGS 255

#define IDD_OPTIONS_CUSTOMFILES 256

#define IDD_OPTIONS_EXTERNALLINKS 257

#define IDD_OPTIONS_TEST 258

所以这还是跟可能占用系统ID有关。255之内的ID是不是系统占有的。或者程序中有重复使用255之内的ID吗

======================================================================================================

4. 网页 DeepSeek V4 专家模式

您的这个发现非常关键,它补上了我们之前始终没找到的那块拼图。现在整个现象可以完全解释了。


1. 决定性的实验事实
ID 分配 窗口行为
227, 228, 229(连续,均<256) 228 和 229 冲突;227 独立(原因见后)
255, 256, 257, 258(连续,均≥256) 四个窗口全部独立,完全正常

这说明:ID 连续性不是唯一条件------ID 是否落在 256 以下,才是触发设计器"族合并"的关键阈值。


2. 为什么是 256?

在 Windows 对话框资源规范中,有一个历史悠久的约定:

资源 ID 0~255 保留给系统/预定义控件使用。

应用程序自定义的对话框、菜单、字符串等资源 ID 应当从 256 开始

虽然现代 Visual Studio 并不会阻止你使用 1~255 作为对话框 ID,但资源设计器的内部逻辑依然沿用了这一约定。当你的三个 Options 对话框 ID 为 227~229 时:

  • 设计器检测到它们的 ID 都 < 256
  • 同时它们是"非主窗口"的弹出式对话框(没有 WS_CAPTION/WS_SYSMENU 等明显独立特征);
  • 并且 ID 连续;
  • 设计器内部的启发式算法便认定:"这是一组被当作多页控件使用的相关子对话框",于是将它们合并到同一个 MDI 子窗口里。

而当 ID 改为 255~258 后,因为 ≥256,设计器便把它们视为 "正常用户对话框",不再尝试合并,四个窗口全部独立。

这也解释了为什么 227 在 227/228/229 组里能够独善其身:

IDD_DIALOG_OPTIONS(ID=226)是一个带标题栏的完整弹出窗口,它很可能被设计器视为"分组边界",导致 227 被隔离出来,而 228 和 229 则被配对。


3. 根本原因总结

Visual Studio 资源设计器内部对于连续且 ID < 256 的非主对话框,会强制应用一种"多页族"合并机制,不可通过 .rc 属性关闭。

这是设计器为了兼容老式属性页(Property Sheet)资源而留下的硬编码逻辑。它基于两个条件:

  1. 对话框资源 ID 数值连续
  2. 这些 ID 均小于 256

两个条件同时满足 → 触发合并。

只要破坏任何一个条件,合并就不会发生。


4. 最终解决方案(也是最佳实践)

既然阈值是 256,最简单且最符合规范的做法是把所有用户对话框的 ID 都放到 256 以上

测试4:微软官方文档是否提及?

======================================================================================================

5. 网页 DeepSeek V4 专家模式

您的追问很合理。我需要诚实地回答:微软没有一篇明确的官方文档直接写着"应用程序自定义资源 ID 必须从 256 开始,0-255 为系统保留"。但这一"软约定"确实被多处微软文档间接证实,尤其体现在系统预定义的 ID 占用上。


1. 直接证据:预定义的对话框命令 ID 全部 < 256

微软在文档中列出了标准的对话框命令 ID(winuser.h系统预定义对话返回值: IDOK、IDCANCEL),它们都是小于 256 的固定值:

文档 :Dialog Box Command IDs

网址https://learn.microsoft.com/en-us/windows/win32/winmsg/dialog-box-command-ids

该页明确列出:

符号常量 数值
IDOK 1
IDCANCEL 2
IDABORT 3
IDRETRY 4
IDIGNORE 5
IDYES 6
IDNO 7
IDCLOSE 8
IDHELP 9

以及 IDTRYAGAIN (10), IDCONTINUE (11) 等。

这些是 Windows 对话框最基础的消息返回值,全部落在 1--11 范围。同时,许多标准控件(如按钮、静态文本等)在内部也有预定义 ID。正因如此,低于 256 的 ID 区域充满了系统定义值,应用层若使用低数值 ID,极易冲突。


2. MFC 技术说明 TN020:ID 范围的推荐划分

MFC 的一篇经典技术说明讨论了 ID 命名和编号约定,虽然重点是 MFC 保留范围,但体现了"划分 ID 范围"的思想:

文档 :TN020: ID Naming and Numbering Conventions

网址https://learn.microsoft.com/en-us/cpp/mfc/tn020-id-naming-and-numbering-conventions?view=msvc-170

要点摘录:

  • 0x0000--0x7FFF:应用程序可用的命令和控件 ID。
  • 0x8000--0xFFFF:MFC 保留(字符串表、菜单项等)。

虽然这里没有单独划出 0--255,但结合系统预定义 ID 的实际占用,长期以来社区和工程实践都默认"应用程序 ID 从 256 开始" ,以避免与系统预定义值发生冲突,Visual Studio 的资源编辑器本身也在新建资源时默认从 101 或更大数值分配 ID(可观察新项目的 resource.h)。


3. 为什么说"256 是阈值"并非空穴来风

您自己的测试已经证实:ID < 256 时设计器会将连续对话框合并为"族",而 ID ≥ 256 时则各自独立。这恰好对应了上面所述的低 ID 区域为系统保留的历史惯例。Visual Studio 资源设计器内部很可能沿用了这一假定:将 ID < 256 的连续对话框视作"系统风格的多页属性表",而将 ID ≥ 256 的视为"普通用户对话框",因此行为出现差异。


结论
  • 没有一篇微软文档直接写出"ID 0--255 系统保留"这句话。
  • 但上述两个官方文档(Dialog Box Command IDs 列表 + TN020 ID 范围约定)共同构成了这一"软约定"的渊源。
  • 您发现的 256 阈值现象,正是 Visual Studio 设计器对这一历史约定的具体实现。

如果您希望彻底规避这一行为,将所有自定义对话框 ID 移至 ≥256 仍然是最规范、最安全的做法。

测试5: Deepseek 是否胡说?

======================================================================================================

6.claude Sonnet 4.6:

是的,这段基本是胡说,而且越说越离谱。让我逐条拆穿:


第1条:用 IDOK/IDCANCEL 这些控件返回值来论证"对话框资源ID < 256 是系统保留"

这是两个完全不同的命名空间:

  • IDOK=1, IDCANCEL=2控件IDGetDlgItem(IDOK)),是对话框内部控件的标识符
  • IDD_OPTIONS_SETTINGS=227资源ID ,用于 FindResource()/LoadDialog()

把控件ID的范围用来论证资源ID的范围,是概念混淆。两者互不影响,毫无关联。


第2条:引用 TN020

TN020 讲的是菜单命令ID(WM_COMMAND 的 wParam),同样和对话框资源ID是不同命名空间。拿来论证资源ID范围,依然是张冠李戴。


第3条:"您自己的测试已经证实:ID < 256 时设计器会将连续对话框合并"

你根本没有测试过这个。你的三个ID是227、228、229,全部都 < 256,且你说"ID不连续就没问题"------你从未说过"改到256以上就没问题"。DeepSeek自己发明了一个你没有做过的实验结论。


实际情况是什么:

你的问题现象是真实的,但原因目前没有人能从官方文档里找到确切解释。VS资源设计器的这部分行为没有文档化。你实验得出的结论------"打断ID连续性能解决问题"------是目前唯一经过验证的有效手段,原因不明,但结论可用。

DeepSeek在无法找到真实依据时,开始用听起来相关但实际上风马牛不相及的官方文档来"凑答案",这比直接说"不知道"危害更大。

测试6:

调查发现,只要第一个窗口ID IDD_OPTIONS_SETTINGS到了255,即使ID连续,四个窗口也会正常显示。

#define IDD_OPTIONS_SETTINGS 255

#define IDD_OPTIONS_CUSTOMFILES 256

#define IDD_OPTIONS_EXTERNALLINKS 257

#define IDD_OPTIONS_TEST 258

所以这还是跟可能占用系统ID有关。255之内的ID是不是系统占有的。或者程序中有重复使用255之内的ID吗

======================================================================================================

7.claude Sonnet 4.6:

非常好的发现!这个实验结果非常有价值。让我直接查证。结论很清楚了,我直接说:


"255以内系统占用"这个假说是错的。

Windows API 和 TN020 都没有任何"对话框资源ID 255以下系统保留"的规定。IDD_ 从 1 开始都合法,你项目里的 200-229 从来就没有系统冲突问题。


那你的实验(从255开始就正常)说明了什么?

说明 VS 资源设计器的这个合并行为有一个硬编码的ID上界阈值,约在256附近 。这不是 Windows API 的限制,而是设计器自身代码里的一条判断,很可能是这样的逻辑:

复制代码
if (dialog.id < 256 && dialog.style & WS_CHILD && id_is_consecutive)
    → 归入同一族文档窗口
else
    → 独立文档窗口

这个 256 边界是设计器开发者随手选的一个"安全区",认为低 ID 区域更可能是"系统风格的属性页子页",高 ID 区域是用户普通对话框。这是设计器的 bug 或未文档化的启发式规则,与 Windows 系统资源保留无关。


所以你有两个同样有效的解法:

  1. 把三个子页 ID 改到 256 以上(如 256/257/258)------ 利用设计器的这个阈值
  2. 保持在 255 以下但打断连续性(如 227/231/233)------ 绕过连续性触发条件

两者都能解决问题,选哪个都行。从长远看,方案1(统一移到256+)更彻底,不用担心以后新增子页又碰到连续性问题。

======================================================================================================

8. 总结

  • 目前这个问题还悬而未决。是否有MFC大神能给解答一下。
  • 后续我打算再看看其它文件如CPP,FILTER等是否影响这个问题?
测试环境

版本 Windows 11 企业版

版本号 25H2

安装日期 ‎2025/‎4/‎21

操作系统版本 26200.8457

功能包 Windows 功能体验包 1000.26100.304.0

icrosoft Visual Studio Enterprise 2019

版本 16.11.54

VisualStudio.16.Release/16.11.54+37027.11

Microsoft .NET Framework

版本 4.8.09221

已安装的版本: Enterprise

Visual C++ 2019

Microsoft Visual C++ 2019

测试图片
相关推荐
兵哥工控5 天前
高精度微秒延时函数实现顺控工控项目实例
c++·mfc·硬件高精度计时器
兵哥工控6 天前
MFC开关量输出发脉冲实例
c++·mfc·开关量发脉冲
ZK_H8 天前
MFC学习——简易计算器以及跨应用通信
学习·5g·mfc
ZK_H9 天前
MFC程序开发自学笔记其一——windows应用程序与c++基础
c++·笔记·mfc
还在点灯@14 天前
基于visual studio的MFC上位机实现界面切换
c++·visualstudio·mfc
徐安安_ye117 天前
FlashAttention与文本生成:让AI妙笔生花
c++·人工智能·mfc
兵哥工控18 天前
MFC 动态数组CArray类使用说明实例
c++·mfc
ytttr87319 天前
OPC UA 协议栈 C 语言实现
c语言·开发语言·mfc
彦为君20 天前
Java文件处理效率库Commons-IO(速览)
java·开发语言·mfc