如何优化 Shell32 方法调用
本文关键字:Shell32 方法 调用 优化 何优化 | 更新日期: 2023-09-27 18:36:46
我正在设计一个媒体播放器,我设计了一个电影类。Movie 类有一个 MovieInfo 成员,该成员继承自 MediaInfo。MediaInfo有几个属性表示电影文件的元数据(这就是你所说的吗?),如文件长度,文件大小,文件路径等。为了提取这些信息,我使用 Shell32。
问题是 Shell32 中提供的方法非常非常慢。我的数据库中有 1 部电影,这不是问题,但对于 10 部电影,它开始变得明显,对于 100 部电影,加载程序大约需要 5 分钟,在某些情况下我必须在运行时重新初始化电影,这再次停止程序的流程。
构造函数调用 Initialize 方法:
/// <summary>
/// Initializes all the information variables of the class.
/// </summary>
private void Initialize()
{
Folder mediaFolder = Shell32Processing.GetShellFolder(this.path);
FolderItem media = Shell32Processing.GetShellFolderItem(this.path);
//initialize bit rate value
this.bitrate = mediaFolder.GetDetailsOf(media, 28);
//initialize date accessed value
this.dateAccessed = mediaFolder.GetDetailsOf(media, 5);
//initialize date created value
this.dateCreated = mediaFolder.GetDetailsOf(media, 4);
//initialize date modified value
this.dateModified = mediaFolder.GetDetailsOf(media, 3);
//initialize data rate value
this.dataRate = mediaFolder.GetDetailsOf(media, 279);
//initialize file name value
this.fileName = mediaFolder.GetDetailsOf(media, 155);
//initialize file type value
this.fileType = mediaFolder.GetDetailsOf(media, 181);
//initialize folder value
this.folder = mediaFolder.GetDetailsOf(media, 177);
//initialize folder name value
this.folderName = mediaFolder.GetDetailsOf(media, 175);
//initialize folder path value
this.folderPath = mediaFolder.GetDetailsOf(media, 176);
//initialize frame height value
this.frameHeight = mediaFolder.GetDetailsOf(media, 280);
//initialize frame rate value
this.frameRate = mediaFolder.GetDetailsOf(media, 281);
//initialize frame width value
this.frameWidth = mediaFolder.GetDetailsOf(media, 282);
//initialize length value
this.length = mediaFolder.GetDetailsOf(media, 27);
//initialize title value
this.title = FileProcessing.GetFileTitle(this.path);
//initialize total bitrate value
this.totalBitrate = mediaFolder.GetDetailsOf(media, 283);
//initialize size value
this.size = mediaFolder.GetDetailsOf(media, 1);
}
以下是 Shell32Processing.GetShellFolder 方法:
/// <summary>
/// Gets a Shell32 Folder, initialized to the directory of the file path.
/// </summary>
/// <param name="path">A string representing the file/folder path</param>
/// <returns>The Shell32 Folder.</returns>
public static Folder GetShellFolder(string path)
{
//extract the folder subpath from the file path
//i.e extract "C:'Example" from "C:'Example'example.avi"
string directoryPath = new FileInfo(path).Directory.ToString();
return new Shell().NameSpace(directoryPath);
}
和 Shell32Processing.GetShellFolderItem 方法:
/// <summary>
/// Gets a Shell32 FolderItem, initialized to the item specified in the file path.
/// </summary>
/// <param name="path">A string representing the file path.</param>
/// <returns>The Shell32 FolderItem.</returns>
/// <exception cref="System.ArgumentException">Thrown when the path parameter does not lead to a file.</exception>
public static FolderItem GetShellFolderItem(string path)
{
if (!FileProcessing.IsFile(path))
{
throw new ArgumentException("Path did not lead to a file", path);
}
int index = -1;
//get the index of the path item
FileInfo info = new FileInfo(path);
DirectoryInfo directoryInfo = info.Directory;
for (int i = 0; i < directoryInfo.GetFileSystemInfos().Count(); i++)
{
if (directoryInfo.GetFileSystemInfos().ElementAt(i).Name == info.Name) //we've found the item in the folder
{
index = i;
break;
}
}
return GetShellFolder(path).Items().Item(index);
}
每次调用 GetDetailsOf(这是 Shell32 中提供的代码)都需要花费大量的时间来处理 - 我使用 ANTS 分析器来查找它,因为起初我无法识别是什么让我的程序速度变慢了。
所以问题是:我如何优化 Shell32 方法,如果不能,有没有替代方案?
你的代码中有很多事情做错了。您声明问题出在您提供的代码之外,但也许我们可以通过修复损坏的内容来到达某个地方。
public static Folder GetShellFolder(string path)
{
//extract the folder subpath from the file path
//i.e extract "C:'Example" from "C:'Example'example.avi"
string directoryPath = new FileInfo(path).Directory.ToString();
return new Shell().NameSpace(directoryPath);
}
您出去访问文件系统只是为了获取文件路径的目录部分(new FileInfo(path).Directory
)。您可以执行此操作,而无需使用System.IO.Path.GetDirectoryName(path)
.
每次开始处理新项时,都会创建一个新的 shell 对象。我会创建一个,处理所有项目,然后释放它。因此,让我们像这样更改GetShellFolder
:
public static Folder GetShellFolder(Shell shell, string path)
{
//extract the folder subpath from the file path
//i.e extract "C:'Example" from "C:'Example'example.avi"
string directoryPath = System.IO.Path.GetDirectoryName(path);
return shell.NameSpace(directoryPath);
}
并将Shell
对象传递给您的Initialize
方法。接下来GetShellFolderItem
.这又是你的代码:
public static FolderItem GetShellFolderItem(string path)
{
if (!FileProcessing.IsFile(path))
{
throw new ArgumentException("Path did not lead to a file", path);
}
int index = -1;
//get the index of the path item
FileInfo info = new FileInfo(path);
DirectoryInfo directoryInfo = info.Directory;
for (int i = 0; i < directoryInfo.GetFileSystemInfos().Count(); i++)
{
if (directoryInfo.GetFileSystemInfos().ElementAt(i).Name == info.Name) //we've found the item in the folder
{
index = i;
break;
}
}
return GetShellFolder(path).Items().Item(index);
}
第一个错误是在访问该文件之前使用"文件是否存在"。别这样。只需访问该文件,如果它不存在,就会发生FileNotFoundException
。您所做的只是添加已经完成的额外工作。无论您是否这样做,它仍然有可能通过"文件存在测试",但无法访问。
接下来,您将解析目录以获取文件夹中文件的索引。这是一个严重的竞争条件。您完全有可能在这里获得错误的索引值。这也没有必要,因为Folder
公开了一种按名称获取FolderItem
的方法:ParseName
。
最后,您正在创建另一个Folder
(通过调用 GetShellFolder),该也会创建另一个Shell
项。您已经有Folder
,使用它。
因此,我们可以通过完全删除GetShellFolderItem
来更改它:
FolderItem media = mediaFolder.ParseName(System.IO.Path.GetFileName(path));
我们可以同样巧妙地摆脱GetShellFolder
:
private void Initialize(Shell shell)
{
Folder mediaFolder = null;
FolderItem media = null;
try
{
mediaFolder = shell.NameSpace(Path.GetDirectoryName(path));
media = mediaFolder.ParseName(Path.GetFileName(path));
...
}
finally
{
if (media != null)
Marshal.ReleaseComObject(media);
if (mediaFolder != null)
Marshal.ReleaseComObject(mediaFolder);
}
}
让我们看看这一切有多大的不同。
您还调用 GetDetailsOf 以获取您已经知道或可以从媒体对象获取的内容:
//initialize folder name value
this.folderName = mediaFolder.GetDetailsOf(media, 175);
//initialize folder path value
this.folderPath = mediaFolder.GetDetailsOf(media, 176);
//initialize size value
this.size = mediaFolder.GetDetailsOf(media, 1);
将这些更改为:
//initialize folder path value
this.folderPath = Path.GetDirectoryName(path);
//initialize folder name value
this.folderName = Path.GetFileName(folderPath);
//initialize size value
this.size = media.Size;
检查此链接。您将获得有关GetDetailsOf()及其基于Win-OS版本的文件属性的更多许可。
List<string> arrHeaders = new List<string>();
Shell shell = new ShellClass();
Folder rFolder = shell.NameSpace(_rootPath);
FolderItem rFiles = rFolder.ParseName(filename);
for (int i = 0; i < short.MaxValue; i++)
{
string value = rFolder.GetDetailsOf(rFiles, i).Trim();
arrHeaders.Add(value);
}
希望这对某些人有所帮助。