VBA解析复合文档04——解析目录信息
时间:2022-07-22
本文章向大家介绍VBA解析复合文档04——解析目录信息,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
有了FAT数组的信息,就可以继续解析目录信息和MiniFAT。
01
解析目录
目录信息存放的才是复合文档中的真正数据的一些信息,也只有通过目录信息才能正确读取到数据流:
Private Type CFDir
EntryName(63) As Byte
EntryNameLen As Integer
ObjectType As Byte '1仓storage 2流 5根
ColorFlag As Byte '0红色 1黑色
LeftSiblingID As Long '-1表示叶子
RightSiblingID As Long
ChildID As Long
CLSID(16 - 1) As Byte
StateBits As Long
CreationTime As Date
ModifiedTime As Date
StartingSectorID As Long '目录入口所表示的第1个扇区编码
StreamSize As Long '目录入口流尺寸,可判断是否是短扇区
not_used As Long '我是32位office,读不了64位整数!
'这个不是结构体的字段
StrDirName As String
'在文件中的偏移位置
lOffset As Long
End Type
'解析目录
Private Function parseDir() As String
Dim l_sub_dir As Long
Dim l_SID As Long
Dim k As Long
Dim i As Long
Dim lOffset As Long
l_SID = cf.Header.FirstDirSID
k = 0
Do
lOffset = getOffsetBySID(l_SID)
'设置读取的位置
cf.r.SeekFile lOffset, OriginF
ReDim Preserve cf.ArrDir(k + 4 - 1) As CFDir
For i = 0 To 4 - 1
cf.r.Read cf.ArrDir(k + i).EntryName
cf.ArrDir(k + i).EntryNameLen = cf.r.ReadInteger()
'名称长度为0就可以退出了
If cf.ArrDir(k + i).EntryNameLen = 0 Then Exit Do
cf.ArrDir(k + i).ObjectType = cf.r.ReadByte() '1仓storage 2流 5根
cf.ArrDir(k + i).ColorFlag = cf.r.ReadByte() '0红色 1黑色
cf.ArrDir(k + i).LeftSiblingID = cf.r.ReadLong() '-1表示叶子
cf.ArrDir(k + i).RightSiblingID = cf.r.ReadLong()
cf.ArrDir(k + i).ChildID = cf.r.ReadLong()
cf.r.Read cf.ArrDir(k + i).CLSID
cf.ArrDir(k + i).StateBits = cf.r.ReadLong()
cf.ArrDir(k + i).CreationTime = cf.r.ReadDate()
cf.ArrDir(k + i).ModifiedTime = cf.r.ReadDate()
cf.ArrDir(k + i).StartingSectorID = cf.r.ReadLong() '目录入口所表示的第1个扇区编码
cf.ArrDir(k + i).StreamSize = cf.r.ReadLong() '目录入口流尺寸,可判断是否是短扇区
cf.ArrDir(k + i).not_used = cf.r.ReadLong()
If cf.ArrDir(k + i).EntryName(0) <= 5 Then
cf.ArrDir(k + i).StrDirName = VBA.CStr(cf.ArrDir(k + i).EntryName(0))
cf.ArrDir(k + i).EntryName(0) = VBA.Asc("]")
cf.ArrDir(k + i).StrDirName = "[" & cf.ArrDir(k + i).StrDirName & VBA.Left$(cf.ArrDir(k + i).EntryName, cf.ArrDir(k + i).EntryNameLen 2 - 1)
Else
cf.ArrDir(k + i).StrDirName = VBA.Left$(cf.ArrDir(k + i).EntryName, cf.ArrDir(k + i).EntryNameLen 2 - 1) '-1包含结尾的0
End If
cf.ArrDir(k + i).lOffset = lOffset
lOffset = lOffset + DIR_SIZE
Next
k = k + 4
l_SID = cf.FAT(l_SID)
Loop Until l_SID = End_Of_Chain_SID
'去掉最后的一个空白
ReDim Preserve cf.ArrDir(k + i - 1) As CFDir
recordDir
GetDirsName
End Function
'记录dir的完整名称到hash
Private Function recordDir() As String
'记录目录名称的Hash
Set cf.h = NewCHash(UBound(cf.ArrDir) + 1)
RrecordDir cf.ArrDir(0), "", 0
End Function
Private Function RrecordDir(d As CFDir, preDir As String, dirIndex As Long)
cf.h.Add preDir & d.StrDirName, dirIndex
d.StrDirName = preDir & d.StrDirName
If d.LeftSiblingID = -1 And d.RightSiblingID = -1 And d.ChildID = -1 Then
Exit Function
End If
If d.LeftSiblingID <> -1 Then RrecordDir cf.ArrDir(d.LeftSiblingID), preDir, d.LeftSiblingID
If d.RightSiblingID <> -1 Then RrecordDir cf.ArrDir(d.RightSiblingID), preDir, d.RightSiblingID
If d.ChildID <> -1 Then RrecordDir cf.ArrDir(d.ChildID), d.StrDirName & Application.PathSeparator, d.ChildID
End Function
目录信息读取的时候,同时将目录名称的一些信息记录到了一个Hash类中。
02
解析MiniFAT
MiniFAT是不一定会存在的,这个主要是看目录信息中的文件StreamSize有没有小于Header结构中的MiniStreamSize,有的情况下才会出现MiniFAT。
'读取短扇区配置表(Allocator for mini stream user-defined data)
'是一个SID数组
Private Function parseMiniFAT() As String
Dim l_SID As Long
Dim i As Long, j As Long
If cf.Header.MiniFATSectorsCount = 0 Then Exit Function
cf.lShortSectorSize = 2 ^ cf.Header.MiniSectorShift
cf.ssNumPerSector = cf.lSectorSize / cf.lShortSectorSize
' 根目录的 stream_size 表示短流存放流的大小,每64个为一个short sector
ReDim cf.MiniFAT(cf.ArrDir(0).StreamSize / cf.lShortSectorSize - 1) As Long
l_SID = cf.Header.FirstMiniFATSID '短流起始SID
For i = 0 To UBound(cf.MiniFAT) Step cf.longNumPerSector
'设置读取的位置
cf.r.SeekFile getOffsetBySID(l_SID), OriginF
For j = 0 To cf.longNumPerSector - 1
cf.MiniFAT(i + j) = cf.r.ReadLong()
If i + j = UBound(cf.MiniFAT) Then Exit For
Next
l_SID = cf.FAT(l_SID)
Next
End Function
到此复合文档的结构就解析完成了,如果想要读取具体某个数据流,只需要根据FAT或者MiniFAT所构建的扇区链表,逐个扇区读取就可以。
- java:利用xpath删除xml中的空节点
- java array to list
- Python之Dijango的“坑” hostname, aliases, ipaddrs = gethostbyaddr(name) UnicodeDecodeError: 'utf-8' cod
- nGrinder 简易使用教程
- UI设计高效学习网站&工具,来自学长的收藏夹哦
- 安装git出现templates not found的问题
- 时间戳 时间
- jenkins 设置 gitlab web hooks
- 测试流程?项目管理流程?
- 学web前端开发写给新手的建议,超实用!
- 价值22万的5字母域名sanwa.com被启用
- Django admin 一些有用的设置
- mysql @value := 用法
- css样式—字体垂直、水平居中
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Java开发编程规范:5.集合处理
- codeforce 272E Dima and Horses (假DFS)
- 网速慢?NO可能是路由器的原因?
- codeforce 272B Dima and Sequence
- 微软自家的.Net下的JavaScript引擎--- ClearScript
- HDU 1159.Common Subsequence【动态规划DP】
- Codeforce 270D Greenhouse Effect
- 地表最简单安装MySQL及配置的方法,没有之一
- HDOJ 1176 免费馅饼 -- 动态规划
- flex-grow计算公式
- Cypress系列(67)- 环境变量设置指南
- codeforce 270C Magical Boxes
- Java开发编程规范: 4.OOP 规约
- Codeforces 1323 div2题解ABC
- order排序