3

私はメディア プレーヤーを設計しており、Movie クラスを設計しました。Movie クラスには、MediaInfo を継承する MovieInfo メンバーがあります。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)

新しいアイテムの処理を開始するたびに、新しいシェル オブジェクトを作成しています。1つ作成し、すべてのアイテムを処理してからリリースします。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);
}

最初の間違いは、そのファイルにアクセスする前に「ファイルは存在しますか」を使用することです。これをしないでください。ファイルにアクセスするだけで、存在しない場合は aFileNotFoundExceptionが発生します。あなたがしているのは、すでに完了している追加の作業を追加することだけです。実行するかどうかに関係なく、「ファイル存在テスト」に合格する可能性はありますが、アクセスに失敗する可能性があります。

次に、ディレクトリを解析して、フォルダー内のファイルのインデックスを取得します。これは重大な競合状態です。ここで間違ったインデックス値を取得する可能性は十分にあります。名前Folderで を取得するメソッドを公開しているため、これも必要ありません。FolderItemParseName

最後に、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 に答える