3

我正在设计一个媒体播放器,并且我设计了一个电影类。Movie 类有一个 MovieInfo 成员,它继承自 MediaInfo。MediaInfo 有几个属性表示电影文件的元数据(你叫它吗?),例如文件长度、文件大小、文件路径等。为了提取这些信息,我使用了 Shell32。

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

MediaInfo 构造函数调用 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 方法,如果不能,是否有替代方法?

4

2 回答 2

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).

每次开始处理新项目时,您都在创建一个新的外壳对象。我会创建一个,处理所有项目,然后发布它。所以让我们这样改变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;
于 2012-06-08T17:19:41.237 回答
0

检查此链接。您将根据 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);
 }

希望这可能对某人有所帮助..

于 2013-02-27T12:03:40.073 回答