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

从VT_BLOB读取Property Store二进制文件到IPropertyStore时GetCount返回0求助

问题分析与修复方案

核心问题

你遇到的GetCount返回0和persistStream.Load无反应,本质原因是内存属性存储不支持IPersistStream接口:

  • PSCreateMemoryPropertyStore创建的对象仅实现IPropertyStore和IPropertyStoreCache,没有实现IPersistStream,所以你强制转换persistStream = propertyStore是无效的,调用Load时COM会直接忽略这个不支持的接口调用,不会报错也不会执行任何操作。
  • VT_BLOB中的二进制内容是Property Set Storage(OLE结构化存储格式),不能直接用内存属性存储加载。

修复代码(VB.NET)

Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices.ComTypes

' 定义必要的COM接口与结构体
<ComImport>
<Guid("0000013A-0000-0000-C000-000000000046")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IPropertySetStorage
    Sub Open(<MarshalAs(UnmanagedType.LPWStr)> ByVal pszName As String, ByVal grfMode As UInt32, <Out> ByRef ppPropStg As IPropertyStorage)
End Interface

<ComImport>
<Guid("0000013B-0000-0000-C000-000000000046")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IPropertyStorage
    ' 仅保留必要方法,其余可省略
End Interface

<StructLayout(LayoutKind.Sequential)>
Public Structure PROPVARIANT
    Public vt As UInt16
    Public wReserved1 As UInt16
    Public wReserved2 As UInt16
    Public wReserved3 As UInt16
    <StructLayout(LayoutKind.Explicit)>
    Public Union
        <FieldOffset(0)> Public blobVal As BLOB
    End Union
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure BLOB
    Public cbSize As UInt32
    Public pData As IntPtr
End Structure

Public Class Functions
    <DllImport("ole32.dll", PreserveSig:=False)>
    Public Shared Sub StgOpenStorageOnILockBytes(<MarshalAs(UnmanagedType.Interface)> ByVal plkbyt As ILockBytes, <MarshalAs(UnmanagedType.Interface)> ByVal pStgPriority As IStorage, ByVal grfMode As UInt32, ByVal snbExclude As IntPtr, ByVal reserved As UInt32, <Out> ByRef ppStg As IStorage)
    End Sub

    <DllImport("propsys.dll", PreserveSig:=False)>
    Public Shared Sub PSCreatePropertyStoreFromPropertySetStorage(<MarshalAs(UnmanagedType.Interface)> ByVal pPropSetStg As IPropertySetStorage, ByVal flags As UInt32, <MarshalAs(UnmanagedType.LPStruct)> ByRef riid As Guid, <Out> ByRef ppv As IntPtr)
    End Sub
End Class

' 自定义ILockBytes实现,用于读取内存BLOB
Public Class ManagedLockBytes
    Implements ILockBytes

    Private _data As Byte()

    Public Sub New(pData As IntPtr, cbSize As UInt32)
        _data = New Byte(cbSize - 1) {}
        Marshal.Copy(pData, _data, 0, CInt(cbSize))
    End Sub

    Public Sub Flush() Implements ILockBytes.Flush
    End Sub

    Public Sub LockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer) Implements ILockBytes.LockRegion
        Throw New NotImplementedException()
    End Sub

    Public Sub ReadAt(ByVal ulOffset As Long, <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=2)> ByVal pv As Byte(), ByVal cb As Integer, <Out> ByVal pcbRead As IntPtr) Implements ILockBytes.ReadAt
        Dim bytesToRead = Math.Min(cb, _data.Length - CInt(ulOffset))
        Array.Copy(_data, ulOffset, pv, 0, bytesToRead)
        If pcbRead <> IntPtr.Zero Then Marshal.WriteInt32(pcbRead, bytesToRead)
    End Sub

    Public Sub SetSize(ByVal cb As Long) Implements ILockBytes.SetSize
        Throw New NotImplementedException()
    End Sub

    Public Sub Stat(<Out> ByVal pstatstg As STATSTG, ByVal grfStatFlag As Integer) Implements ILockBytes.Stat
        pstatstg.cbSize = _data.Length
        pstatstg.type = 2 ' STGTY_LOCKBYTES
    End Sub

    Public Sub UnlockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer) Implements ILockBytes.UnlockRegion
        Throw New NotImplementedException()
    End Sub

    Public Sub WriteAt(ByVal ulOffset As Long, <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=2)> ByVal pv As Byte(), ByVal cb As Integer, <Out> ByVal pcbWritten As IntPtr) Implements ILockBytes.WriteAt
        Throw New NotImplementedException()
    End Sub
End Class

' 主加载逻辑
Public Sub LoadPropertyStoreFromBlob(propVariant As PROPVARIANT)
    ' 验证是否为VT_BLOB类型
    If propVariant.vt <> &H41 Then Throw New ArgumentException("输入不是VT_BLOB类型")

    ' 从内存BLOB创建ILockBytes
    Dim lockBytes As ILockBytes = New ManagedLockBytes(propVariant.Union.blobVal.pData, propVariant.Union.blobVal.cbSize)
    Dim storage As IStorage = Nothing
    ' 以只读模式打开结构化存储
    Functions.StgOpenStorageOnILockBytes(lockBytes, Nothing, &H1 Or &H2, IntPtr.Zero, 0, storage)

    ' 转换为Property Set Storage
    Dim propSetStorage As IPropertySetStorage = CType(storage, IPropertySetStorage)
    Dim propertyStorePtr As IntPtr = IntPtr.Zero
    Dim iidPropertyStore As Guid = GetType(IPropertyStore).GUID
    ' 从Property Set Storage创建IPropertyStore
    Functions.PSCreatePropertyStoreFromPropertySetStorage(propSetStorage, 0, iidPropertyStore, propertyStorePtr)

    ' 获取属性数量
    Dim propertyStore As IPropertyStore = CType(Marshal.GetTypedObjectForIUnknown(propertyStorePtr, GetType(IPropertyStore)), IPropertyStore)
    Dim count As UInt32 = 0
    propertyStore.GetCount(count)
    Debug.WriteLine($"属性数量: {count}")

    ' 释放COM资源
    Marshal.ReleaseComObject(propertyStore)
    Marshal.ReleaseComObject(propSetStorage)
    Marshal.ReleaseComObject(storage)
    Marshal.ReleaseComObject(lockBytes)
End Sub

关键说明

  1. 正确解析Property Set Storage:VT_BLOB中的内容是OLE结构化存储,必须先通过StgOpenStorageOnILockBytes打开为IStorage,再转换为IPropertySetStorage,最后用PSCreatePropertyStoreFromPropertySetStorage生成可用的IPropertyStore。
  2. 自定义ILockBytes:因为数据在内存中,需要实现ILockBytes接口让OLE存储系统读取内存数据,上述实现仅支持只读操作,满足需求。
  3. 资源清理:所有COM对象使用后必须调用Marshal.ReleaseComObject释放资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者miss programmer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:24:51