2012-06-08 47 views
3

我在设计一个媒体播放器,并且我设计了一个电影类。 Movie类有一个MovieInfo成员,它从MediaInfo继承。 MediaInfo有几个属性,代表电影文件的元数据(就是你称之为?),例如文件长度,文件大小,文件路径等。为了提取这些信息,我使用了Shell32。如何优化Shell32方法调用?

问题是Shell32提供的方法非常非常慢。在我的数据库中有1部电影,这不是问题,但有10部电影,它开始变得明显,并有100部电影,大约需要5分钟的时间才能加载该程序,并且在某些情况下,我必须重新初始化运行时的电影,这又会暂停程序的流程。

的的MediaInfo构造函数调用初始化方法:

/// <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方法,如果我不能,是否有替代方案?

回答

9

在代码中有很多事情你做错了。您声明问题出在您提供的代码之外,但也许我们可以通过修复破坏的问题来解决问题。

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; 
+0

谢谢!这并没有完全解决问题,但仍然有明显的延迟,但现在它变短了:)。 – Daniel

+1

@Daniel另外,这些是COM对象,所以不要忘记释放它们。我已经更新了上面的Initialize,以显示最正确的方式来做到这一点。 'Marshal'可以在'System.Runtime.InteropServices'中找到(mscorlib) – Tergiver

+0

哦,我不知道我需要这么做。谢谢 :) – Daniel

0

检查这个Link。您将基于Win-OS版本明智地获得关于GetDetailsOf()及其文件属性的更多清除。

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); 
} 

希望这可能有助于一些的..