2012-06-08 103 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); 
} 

希望這可能有助於一些的..