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

64位MS Access中VBA的TVITEM结构适配问题求助

问题描述

在64位MS Access中通过Win32 API操作OCX TreeView控件时出现崩溃问题。现有32位环境下可配合SendMessage正常使用的TVITEM结构如下:

Public Type TVITEM
    Mask As Long
    hItem As LongPtr
    State As Long
    StateMask As Long
    pszText As String
    cchTextMax As Long
    iImage As Long
    iSelectedImage As Long
    cChildren As Long
    lParam As LongPtr
End Type

该结构在64位环境中调用SendMessage会直接崩溃。尝试改用TVM_SETITEMW虽能避免崩溃,但无法实现隐藏复选框的效果;手动添加Padding字段后依然会崩溃。需要确认该结构的64位适配是否正确,若不正确需如何调整?

补充背景:代码用于隐藏/显示TreeView特定节点的复选框,核心逻辑如下:

If oModTreeViewParent.CheckBoxes Then
    If blnHide Then
        With typTVI
            .hItem = lpModNodeHandle
            .Mask = TVIF_STATE
            .StateMask = TVIS_STATEIMAGEMASK
            .State = 0 'ShiftLeft(1, 12)
        End With
        lpTemp = SendMessageA(oModTreeViewParent.hWnd, TVM_SETITEMA, CLngPtr(0), typTVI)
        'Debug.Print lpTemp & " - " & lpModNodeHandle
        blnModCheckBoxHidden = True
    Else
        With typTVI
            .hItem = lpModNodeHandle
            .Mask = TVIF_STATE
            .StateMask = TVIS_STATEIMAGEMASK
            .State = 1 'ShiftLeft(0, 12)
        End With
        lpTemp = SendMessageA(oModTreeViewParent.hWnd, TVM_SETITEMA, CLngPtr(0), typTVI)
        'Debug.Print lpTemp & " - " & lpModNodeHandle
        blnModCheckBoxHidden = False
    End If
End If
64位适配方案

1. 修正TVITEM结构的内存对齐问题

64位环境下,Win32 API结构体要求8字节内存对齐,原结构中Long(4字节)和LongPtr(8字节)交错排列会导致内存偏移错误,这是崩溃的核心原因。正确的64位适配TVITEM结构如下:

#If VBA7 Then
    Public Type TVITEM
        Mask As Long
        hItem As LongPtr
        State As Long
        StateMask As Long
        ' 补充4字节Padding,保证后续LongPtr字段8字节对齐
        Padding1 As Long
        pszText As LongPtr ' 64位环境下必须用LongPtr存储字符串指针,不能直接用String
        cchTextMax As Long
        ' 补充4字节Padding,保证后续LongPtr字段8字节对齐
        Padding2 As Long
        iImage As Long
        iSelectedImage As Long
        cChildren As Long
        ' 补充4字节Padding,保证最后LongPtr字段8字节对齐
        Padding3 As Long
        lParam As LongPtr
    End Type
#Else
    ' 32位环境保留原结构
    Public Type TVITEM
        Mask As Long
        hItem As Long
        State As Long
        StateMask As Long
        pszText As String
        cchTextMax As Long
        iImage As Long
        iSelectedImage As Long
        cChildren As Long
        lParam As Long
    End Type
#End If

关键调整说明:

  • 字符串指针类型修改:64位环境下pszText不能直接用String,必须改为LongPtr存储字符串的内存地址,否则会导致API调用时参数传递错误。
  • 添加Padding字段:在Long类型字段之后、LongPtr类型字段之前补充4字节的Padding(用Long类型),确保所有LongPtr字段都处于8字节对齐的内存位置,避免结构内存偏移。

2. 修正SendMessage调用与复选框状态设置

(1)统一使用宽字符版本API

64位MS Access默认使用Unicode编码,应改用SendMessageW和TVM_SETITEMW,避免ANSI与Unicode编码不匹配导致的功能失效:

' 先声明SendMessageW
#If VBA7 Then
    Declare PtrSafe Function SendMessageW Lib "user32" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
#Else
    Declare Function SendMessageW Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
#End If

' 定义相关常量
Public Const TVM_SETITEMW = &H110D
Public Const TVIF_STATE = &H8
Public Const TVIS_STATEIMAGEMASK = &HF000&

(2)正确设置复选框隐藏的State值

隐藏复选框时,State不能直接设为0,需要将状态位的图像索引部分清零。正确的做法是用TVIS_STATEIMAGEMASK掩码清除对应位:

If oModTreeViewParent.CheckBoxes Then
    If blnHide Then
        With typTVI
            .hItem = lpModNodeHandle
            .Mask = TVIF_STATE
            .StateMask = TVIS_STATEIMAGEMASK
            ' 清除状态图像位,实现隐藏复选框
            .State = .State And Not TVIS_STATEIMAGEMASK
        End With
        lpTemp = SendMessageW(oModTreeViewParent.hWnd, TVM_SETITEMW, CLngPtr(0), typTVI)
        blnModCheckBoxHidden = True
    Else
        With typTVI
            .hItem = lpModNodeHandle
            .Mask = TVIF_STATE
            .StateMask = TVIS_STATEIMAGEMASK
            ' 恢复默认复选框图像索引(0对应未选中,1对应选中)
            .State = (.State And Not TVIS_STATEIMAGEMASK) Or (0 << 12)
        End With
        lpTemp = SendMessageW(oModTreeViewParent.hWnd, TVM_SETITEMW, CLngPtr(0), typTVI)
        blnModCheckBoxHidden = False
    End If
End If

(3)字符串处理注意事项

如果后续需要操作节点文本,需将VBA字符串转换为Unicode指针后赋值给pszText,例如:

typTVI.pszText = StrPtr("节点文本")
typTVI.cchTextMax = Len("节点文本") + 1

内容的提问来源于stack exchange,提问作者Ian J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:58:22