有没有比使用 shell COM 组件更快的方法来获取文件元数据?



在这里和其他地方阅读各种答案,我将这一部分拼凑在一起以获得我需要的文件元数据:

Public Class windows_metadata_helper
Public Shared shell As New Shell32.Shell
Public Shared indices_of_interest As New Dictionary(Of Integer, String)
Public Shared path_index As Integer
Shared Sub New()
'snipped long piece code for figuring out the indices of the attributes that I need, they are stored in indices_of_interest, for example 0:Name
End Sub
Public Shared Function get_interesting_data(path) As Dictionary(Of String, String)
Dim fi As New IO.FileInfo(path)
Dim f_dir = shell.NameSpace(fi.DirectoryName)
Dim data As New Dictionary(Of String, String)
For Each item In f_dir.Items()
If f_dir.GetDetailsOf(item, path_index) = fi.FullName Then
For Each kvp In indices_of_interest
Dim val = f_dir.GetDetailsOf(item, kvp.Key)
If Not String.IsNullOrEmpty(val) Then data.Add(kvp.Value, val)
Next
Exit For
End If
Next
Return data
End Function
End Class

它不是世界上最高效的代码,即获取目录中每个文件的 path 属性来标识我真正感兴趣的文件。优化它以仅读取每个文件的 path 属性一次,使其速度提高约 50%(通过让它获取它找到的第一个文件来测试它是否是正确的文件(,但无论如何,它比预期的要慢得多。

它需要从每个文件中获取 24 个属性,并且需要从 ~100k 内查找大约 20k 个文件,目前这需要整整一个小时。

分析告诉我,CPU是瓶颈,任何占用周期的东西我都看不到,因为它在Shell32.Folder.GetDetailsOf方法中占99%。

有没有更快的方法来获取元数据?答案不必特定于 vb 或 .net。

由于您正在寻求最大速度,因此建议您为代码启用Option Strict并进行 IDE 建议的必要修改。 这将消除不必要的类型转换。

例如

Public Shared Function get_interesting_data(path) As Dictionary(Of String, String)

应该是:

Public Shared Function get_interesting_data(path As String) As Dictionary(Of String, String)

使用 Shell32.Folder.ParseName 方法直接检索FolderItem对象,而不是枚举Shell32.Folder.Items集合。 此对象可以强制转换为允许使用 ShellFolderItem.ExtendedProperty 方法的Shell32.ShellFolderItem

有两种方法可以指定属性。首先是分配 属性的知名名称,例如"作者"或"日期",到 sPropName。 但是,每个属性都是组件对象模型 (COM( 的成员 属性集,也可以通过指定其格式 ID 来标识 (FMTID( 和属性 ID (PID(。FMTID 是标识 属性集,PID 是标识特定 属性集中的属性。

通过其 FMTID/PID 值指定属性通常更多 比使用其名称更有效。使用属性的 FMTID/PID 值 使用扩展属性,它们必须组合成一个 SCID。SCID 是 一个字符串,包含 "FMTID**PID" 形式的 FMTID/PID 值, 其中,FMTID 是属性集的 GUID 的字符串形式。为 例如,摘要信息属性集作者的 SCID 属性为"{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 4"。

许多 FMTID/PID 值可以在 Windows Properties.
上提供的链接下找到,您可以在此处找到完整的属性表(向下滚动(。

将一些选定属性放在一起:

Public Shared Function get_interesting_data(path As String) As Dictionary(Of String, String)
Dim fi As New IO.FileInfo(path)
Dim f_dir As Shell32.Folder = shell.NameSpace(fi.DirectoryName)
' instead of enumerating f_dir.Items to find the file of interest
' directly retrieve the item reference
Dim item As Shell32.ShellFolderItem = DirectCast(f_dir.ParseName(fi.Name), Shell32.ShellFolderItem)
Dim scid_Bitrate As String = "{64440490-4C8B-11D1-8B70-080036B11A03} 4"    ' Audio: System.Audio.EncodingBitrate
Dim scid_Title As String = "{F29F85E0 - 4.0FF9-1068-AB91-08002B27B3D9} 2"  ' Core: System.Title
Dim scid_Created As String = "{B725F130-47EF-101A-A5F1-02608C9EEBAC} 15"   ' Core: System.DateCreated
Dim scid_Copyright As String = "{64440492-4C8B-11D1-8B70-080036B11A03} 11" ' Core: System.Copyright
Dim scid_Publisher As String = "{64440492-4C8B-11D1-8B70-080036B11A03} 30" ' Media: System.Media.Publisher
Dim scid_FullDetails As String = "{C9944A21-A406-48FE-8225-AEC7E24C211B} 2" ' PropList: System.PropList.FullDetails
Dim bitrate As Object = item.ExtendedProperty(scid_Bitrate)
Dim title As Object = item.ExtendedProperty(scid_Title)
Dim created As Object = item.ExtendedProperty(scid_Created)
Dim copyright As Object = item.ExtendedProperty(scid_Copyright)
Dim publisher As Object = item.ExtendedProperty(scid_Publisher)
Dim fullDetails As Object = item.ExtendedProperty(scid_FullDetails)
Dim data As New Dictionary(Of String, String)
' save the retrieved properties
Return data
End Function

我不知道这种检索属性的技术是否比您目前使用的GetDetailsOf更快,但其他更改应该会有所改进。

最新更新