You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 TreeView添加节点时突发内存异常(代码未改动)

内存异常问题排查:长期稳定代码突发SendDlgItemMessage访问违规

问题背景

一段存储在Git仓库、稳定运行2年以上未改动的代码,今年6月突发内存访问违规(错误码0xC0000005),读取无效内存地址0x00000000000000E4。异常发生在调用SendDlgItemMessage插入树节点时,而此前向同一控件发送TVM_SETIMAGELIST消息设置图像列表的操作可正常执行。

异常相关代码细节

引发异常的getTreeNode函数

HTREEITEM getTreeNode(HWND hDlg, char *name, HTREEITEM parent)
{
    TV_INSERTSTRUCT tvinsert = {};
    tvinsert.hParent = parent;
    tvinsert.hInsertAfter = parent ? TVI_SORT : TVI_ROOT;
    tvinsert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
    tvinsert.item.pszText = name;
    tvinsert.item.lParam = -1;
    tvinsert.item.iImage = 0;
    tvinsert.item.iSelectedImage = 1;

    if (IsWindow(hDlg))
        LogError("**** window ok");
    else
        LogError("**** Window issue....");

    HWND presetHDlg = GetDlgItem(hDlg, IDC_PRESET_TREE);
    if (IsWindow(presetHDlg))
        LogError("**** Preset Tree window ok");
    else
        LogError("**** Preset Tree Window issue....");

    HTREEITEM newNode = (HTREEITEM)SendDlgItemMessage(hDlg, IDC_PRESET_TREE,
        TVM_INSERTITEM, 0, (LPARAM)&tvinsert);
    if (nodes == NULL)
    { 
        // 后续代码省略
    }
}

前置正常执行的addPresetsTree函数

void addPresetsTree(HWND hDlg)
{
    // Load bitmaps used in tree view
    HIMAGELIST hImageList = ImageList_Create(16, 16, ILC_COLOR16, 2, 10);
    HBITMAP hBitMap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_OPEN_FOLDER));
    if (hBitMap == NULL) LogError("*** Failed to load bitmap for IDB_OPEN_FOLDER");
    else
    {
        ImageList_Add(hImageList, hBitMap, NULL);
        DeleteObject(hBitMap);
    }
    hBitMap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_CLOSED_FOLDER));
    if (hBitMap == NULL) LogError("*** Failed to load bitmap for IDB_CLOSED_FOLDER");
    else
    {
        ImageList_Add(hImageList, hBitMap, NULL);
        DeleteObject(hBitMap);
    }
    hBitMap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PRESET));
    if (hBitMap == NULL) LogError("*** Failed to load bitmap for IDB_PRESET");
    else
    {
        ImageList_Add(hImageList, hBitMap, NULL);
        DeleteObject(hBitMap);
    }
    hBitMap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SELECTED_PRESET));
    if (hBitMap == NULL) LogError("*** Failed to load bitmap for IDB_SELECTED_PRESET");
    else
    {
        ImageList_Add(hImageList, hBitMap, NULL);
        DeleteObject(hBitMap);
    }
    LRESULT res = SendDlgItemMessage(hDlg, IDC_PRESET_TREE, TVM_SETIMAGELIST, 0, (LPARAM)hImageList);
    
    HTREEITEM Parent = NULL;
    char p[] = "RootNode";
    Parent = getTreeNode(hDlg, p, Parent);
}

代码调用时机

上述代码在对话框过程的WM_INITDIALOG消息中触发:

BOOL CALLBACK FindPresetDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            InitCommonControls();     // make our tree control work
            // Populate preset tree and select current entry, if available
            addPresetsTree(hDlg);
            break;
        // 其他消息处理省略
    }
}

异常特征

  • 两次IsWindow()调用均返回TRUE,确认对话框和树控件句柄有效
  • 4月构建版本曾正常运行,6月后所有Git旧版本均触发异常
  • 栈跟踪指向KernelBase.dll、comctl32.dll等系统模块
  • 用相同代码创建的最小化测试程序可正常运行
  • 重装Windows 11后异常仍存在,无法追溯系统/Visual Studio更新历史

已知分析

异常为访问违规(0xC0000005),因读取无效内存地址0x00000000000000E4。GitHub Copilot推测直接原因是getTreeNode中SendDlgItemMessage使用无效HWND,但与实际IsWindow验证结果矛盾。其提出的可能根本原因包括:

  1. GetDlgItem获取的IDC_PRESET_TREE句柄无效(与当前验证结果冲突)
  2. hDlg参数未初始化或损坏(同样与验证结果冲突)
    建议方向:调用GetDlgItem前验证hDlg有效性;检查对话框资源中IDC_PRESET_TREE是否存在

排查方向与可能原因

  • 系统控件库行为变更:Windows更新可能修改了comctl32.dll中树控件处理TVM_INSERTITEM消息的逻辑,比如对TV_INSERTSTRUCT字段的校验更严格。需确认tvinsert的所有字段是否符合当前系统控件的要求,比如pszText的内存有效性、字段取值范围等。
  • 控件初始化时序问题:WM_INITDIALOG期间调用InitCommonControls的时序可能无法满足系统更新后的控件初始化要求。尝试将InitCommonControls移至程序启动时执行,而非对话框初始化阶段。
  • 图像列表生命周期问题:addPresetsTree中创建的hImageList在设置给树控件后,可能因系统更新后的引用逻辑变化,导致插入节点时图像列表已无效。可尝试将hImageList设为全局变量,确保其生命周期覆盖树节点插入过程。
  • 编译器/链接器行为变更:Visual Studio 2022更新可能改变了代码生成逻辑,比如结构体对齐、栈布局变化。检查TV_INSERTSTRUCT的内存布局是否与系统期望一致,可显式指定结构体对齐方式(如#pragma pack(push, 8))。
  • 对话框资源隐性变更:虽然代码未改动,但Visual Studio更新可能隐性修改了对话框资源文件(.rc),比如IDC_PRESET_TREE的样式、类别属性。对比正常版本与异常版本的.rc文件,检查树控件定义是否一致。
  • 内存隐性损坏:系统更新后底层内存分配逻辑变化,导致此前未触发的内存越界、句柄误用等问题显现。启用Visual Studio的地址 sanitizer 或使用Application Verifier工具,排查内存 corruption 问题。
  • 字符集/编码问题:系统更新后默认字符集变化,导致char*类型的pszText编码与控件期望不匹配。尝试将char*改为TCHAR*,并使用TEXT宏包裹字符串,确保与项目字符集设置一致。

内容的提问来源于stack exchange,提问作者John D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:44:49