【问题标题】:Where is SAFEARRAY var type stored?SAFEARRAY var 类型存储在哪里?
【发布时间】:2021-01-19 23:47:40
【问题描述】:

我想了解 SAFEARRAY 的实施。

在我看来,SAFEARRAY 结构中没有用于存储元素类型信息的字段,例如 VT_I4(3) 或 VT_R4(4),但 SafeArrayGetVartype 函数返回正确的类型。

有人在下面的MSDN页面评论说cLocks的高位字持有类型信息:SAFEARRAY structure on MSDN

但是当我通过类型库将 Long 和 Single 数组从 VBA 传递到 DLL 函数时,这些数组的 fFeatures 都是 0x80,cLocks 都是 0,并且 stll SafeArrayGetVartype 可以告诉 VT_I4(3) 和 VT_R4(4)。

【问题讨论】:

    标签: com automation safearray


    【解决方案1】:

    根据安全数组的创建方式,变体类型可能会存储在内存中,位于SAFEARRAY 结构之前(从开始的偏移量-4 处)。 fFeatures中的FADF_HAVEVARTYPE标志表示该类型是否可用。

    同样,FADF_HAVEIID 表示 GUID(请参阅SafeArrayCreateEx)存储在偏移量 -16 处,可通过SafeArrayGetIID 获得。 FADF_HAVEVARTYPEFADF_HAVEIID 永远不能同时存在(因为否则 VARTYPEGUID 会在内存中重叠),但 SafeArrayGetVartype 足够聪明,可以合成 VT_RECORDVT_DISPATCH 或 @ 之一987654334@ 在看到相应的功能标志时输入。

    【讨论】:

    • 感谢您的明确答复。因此,总而言之,对于 fFeatures 字段,可以设置 A) FADF_HAVEVARTYPE 或 B) FADF_HAVEIID 或 C) FADF_RECORD。对于A)4,对于B)16,对于C)SAFEARRAY之前的4个字节用于携带有关SAFEARRAY的补充信息。一次只能设置 A、B 或 C 之一。
    【解决方案2】:

    您永远不会手动初始化SAFEARRAY,它始终是调用SafeArrayCreate 的产物,它为此结构分配内存。我认为假设为 SafeArray 的内部数据结构分配了一些额外的字节是安全的。这是可以存储任何扩展类型信息的地方。

    【讨论】:

    • 谢谢。实际上,我检查的安全数组是从 VBA 传递的,但可以肯定的是,VBA 所做的事情与 SafeArrayCreate 所做的事情一样。
    【解决方案3】:

    要进一步扩展 SAFEARRAY var 类型的存储位置并探索 SafeArrayGetVartype 函数和其他 SafeArray 函数的具体作用,请参阅:

    SafeArrayGetVartype

    /* Memory Layout of a SafeArray:
     *
     * -0x10: start of memory.
     * -0x10: GUID for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID)
     * -0x04: DWORD varianttype; (for all others, except VT_RECORD) (if FADF_HAVEVARTYPE)
     *  -0x4: IRecordInfo* iface;  (if FADF_RECORD, for VT_RECORD (can be NULL))
     *  0x00: SAFEARRAY,
     *  0x10: SAFEARRAYBOUNDS[0...]
     */
    

    对于以下使用 MS Access VBA 7 的整数数组声明,其中地址引用的大小为 8 个字节。

    Dim myArray() As Integer
    ReDim myArray(9)
    

    SafeArray 结构的内存转储,包括使用 MS Access VBA 7 的先前隐藏的 VT 数据

    Pos  Address Dec    Address Hex     Hex    
    0    (1185248224)   (46A573E0) >>    0h     
    1    (1185248225)   (46A573E1) >>    0h     
    2    (1185248226)   (46A573E2) >>    0h     
    3    (1185248227)   (46A573E3) >>    0h     
    4    (1185248228)   (46A573E4) >>    0h     
    5    (1185248229)   (46A573E5) >>    0h     
    6    (1185248230)   (46A573E6) >>    0h     
    7    (1185248231)   (46A573E7) >>    0h     
    8    (1185248232)   (46A573E8) >>    0h     
    9    (1185248233)   (46A573E9) >>    0h     
    10   (1185248234)   (46A573EA) >>    0h     
    11   (1185248235)   (46A573EB) >>    0h
    -------------------------------------------------------------------------------------
    Hidden DWord for VT when FADF_HAVEVARTYPE = 0x0080
    VT = 2 i.e. Integer    
    12   (1185248236)   (46A573EC) >>    2h     
    13   (1185248237)   (46A573ED) >>    0h     
    14   (1185248238)   (46A573EE) >>    0h     
    15   (1185248239)   (46A573EF) >>    0h
    -------------------------------------------------------------------------------------
    cDims = 1 i.e. One dimensional array   
    16   (1185248240)   (46A573F0) >>    1h     
    17   (1185248241)   (46A573F1) >>    0h
    -------------------------------------------------------------------------------------
    fFeatures = FADF_HAVEVARTYPE  
    18   (1185248242)   (46A573F2) >>    80h    
    19   (1185248243)   (46A573F3) >>    0h
    -------------------------------------------------------------------------------------
    cbElements = 2 i.e. element size is 2 bytes
    20   (1185248244)   (46A573F4) >>    2h     
    21   (1185248245)   (46A573F5) >>    0h     
    22   (1185248246)   (46A573F6) >>    0h     
    23   (1185248247)   (46A573F7) >>    0h
    -------------------------------------------------------------------------------------
    cLocks     
    24   (1185248248)   (46A573F8) >>    0h     
    25   (1185248249)   (46A573F9) >>    0h     
    26   (1185248250)   (46A573FA) >>    0h     
    27   (1185248251)   (46A573FB) >>    0h
    -------------------------------------------------------------------------------------
    Padding     
    28   (1185248252)   (46A573FC) >>    0h     
    29   (1185248253)   (46A573FD) >>    0h     
    30   (1185248254)   (46A573FE) >>    0h     
    31   (1185248255)   (46A573FF) >>    0h
    -------------------------------------------------------------------------------------
    pvData     
    32   (1185248256)   (46A57400) >>    0h    
    33   (1185248257)   (46A57401) >>    97h    
    34   (1185248258)   (46A57402) >>    DCh    
    35   (1185248259)   (46A57403) >>    1Bh    
    36   (1185248260)   (46A57404) >>    0h     
    37   (1185248261)   (46A57405) >>    0h     
    38   (1185248262)   (46A57406) >>    0h     
    39   (1185248263)   (46A57407) >>    0h
    -------------------------------------------------------------------------------------
    rgsabound(0).cElements = 10
    40   (1185248264)   (46A57408) >>    Ah     
    41   (1185248265)   (46A57409) >>    0h     
    42   (1185248266)   (46A5740A) >>    0h     
    43   (1185248267)   (46A5740B) >>    0h
    -------------------------------------------------------------------------------------
    rgsabound(0).lLbound  
    44   (1185248268)   (46A5740C) >>    0h     
    45   (1185248269)   (46A5740D) >>    0h     
    46   (1185248270)   (46A5740E) >>    0h     
    47   (1185248271)   (46A5740F) >>    0h 
    

    由于pvData 的 8 字节内存地址和填充,VBA 7 64 位的注释显示了 SAFEARRAYBOUNDS[0...] 在 +24 Dec,+18 十六进制的偏移量。

    我希望这有助于进一步解释SafeArray 函数的具体执行情况以及SafeArray 结构项的位置。如果有人手动操作SafeArray 结构,请注意任何填充并正确设置偏移量。

    我还将尝试解决您关于 VT_I4(3) 或 VT_R4(4) 的问题。

    在 VBA 中肯定会发生一些额外的事情,然后是 SafeArray.c 函数所执行的操作,因为从使用 SafeArrayDescriptorEx 创建一个初始化的空整数数组进行测试,cbElements 没有设置,前面的 VT 已设置。奇怪的是,VBA 整数数组仍然可以在没有将整数数组的 cbElements 设置为 2 个字节的情况下工作。创建 SafeArrayDescriptor 时,我现在手动设置 cbElements。

    使用上述 VBA 示例创建时,cbElements 已正确设置。

    在 SafeArray.c 函数中,它们不返回 SizeOf(VT_I4) 或 SizeOf(VT_R4),即 C 不支持,所以我假设在 VBA 中必须扩展 SafeArray.c 函数并满足其数据类型不覆盖在 C 中。

    对 C 有更多了解的人可能能够更好地澄清或解释。

    【讨论】:

      【解决方案4】:

      短版

      varType = SafeArrayGetVarType(mySafeArray);
      

      加长版

      SAFEARRAY 有一个 features 成员,可以帮助描述数组中的内容

      2.2.30.10 SAFEARRAY (archive)

      • fFeatures:必须设置为2.2.9 部分中指定的位标志的组合。

      然后你咨询:

      2.2.9 ADVFEATUREFLAGS Advanced Feature Flags (archive)

      以下值用于 SAFEARRAY(第 2.2.30.10 节)数据类型的字段 fFeatures。

      typedef  enum tagADVFEATUREFLAGS
       {
         FADF_AUTO = 0x0001,
         FADF_STATIC = 0x0002,
         FADF_EMBEDDED = 0x0004,
         FADF_FIXEDSIZE = 0x0010,
         FADF_RECORD = 0x0020,
         FADF_HAVEIID = 0x0040,
         FADF_HAVEVARTYPE = 0x0080,
         FADF_BSTR = 0x0100,
         FADF_UNKNOWN = 0x0200,
         FADF_DISPATCH = 0x0400,
         FADF_VARIANT = 0x0800
       } ADVFEATUREFLAGS;
      
      • FADF_RECORD: SAFEARRAY 必须包含 UDT 的元素(请参阅第 2.2.28.1 节)
      • FADF_HAVEIID: SAFEARRAY 必须包含 MInterfacePointers 元素。
      • FADF_HAVEVARTYPE: 如果设置了该位标志,则 SAFEARRAY 的 cLocks 字段的高位字必须包含描述数组元素类型的 VARIANT 类型常量(参见 2.2.7 和 2.2.30.10 节)。
      • FADF_BSTR: SAFEARRAY 必须包含 BSTR 元素数组(请参阅第 2.2.23 节)。
      • FADF_UNKNOWN: SAFEARRAY 必须包含指向 IUnknown 的指针数组。
      • FADF_DISPATCH: SAFEARRAY 必须包含指向 IDispatch 的指针数组(请参阅第 3.1.4 节)。
      • FADF_VARIANT: SAFEARRAY 必须包含一个 VARIANT 实例数组。

      所以根据FADF,你可以想出一个对应的Variant Type:

      Feature Flag Corresponding Variant Type
      FADF_UNKNOWN VT_UNKNOWN
      FADF_DISPATCH VT_DISPATCH
      FADF_VARIANT VT_VARIANT
      FADF_BSTR VT_BSTR
      FADF_HAVEVARTYPE SafeArrayGetVarType(mySafeArray)

      事实证明,上述所有工作(将 FADF_BSTR 匹配到 VT_BSTR 等)都由辅助函数 SafeArrayGetVarType (archive) 完成:

      • 如果设置了FADF_HAVEVARTYPESafeArrayGetVartype返回存储在数组描述符中的VARTYPE。
      • 如果设置了FADF_RECORD,则返回VT_RECORD
      • 如果设置了FADF_DISPATCH,则返回VT_DISPATCH
      • 如果设置了FADF_UNKNOWN,则返回VT_UNKNOWN
      对于基于 IUnknown 的 SAFEARRAY 类型,

      SafeArrayGetVartype 可能无法返回 VT_UNKNOWN。调用者还应检查 SAFEARRAY 类型的 fFeatures 字段是否设置了 FADF_UNKNOWN 标志。

      【讨论】:

      • FADF_HAVEVARTYPE: If this bit flag is set, the high word of the cLocks field of the SAFEARRAY MUST contain a VARIANT type constant that describes the type of the array's elements (see sections 2.2.7 and 2.2.30.10). MS 文档确实声称这个奇怪的cLocks 要求。但是,VBA 从不使用它创建的 SAFEARRAYS 执行此操作。相反,它使用偏移量 -4 处的 4 个字节(为 FADF_RECORD 保留的相同字节)来存储 VARIANT 类型常量……即使fFeatures 位域不包含FADF_HAVEVARTYPE,它也会这样做。这很令人沮丧,但 VBA 会有所不同。
      猜你喜欢
      • 2011-10-12
      • 1970-01-01
      • 2014-01-26
      • 1970-01-01
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 2014-02-11
      • 2011-11-05
      相关资源
      最近更新 更多