サイズの大きいZIPファイルの属性をIShellFolder.GetAttributesOf やSHGetFileInfo で取得すると時間がかかる - C#

サイズの大きいZIPファイルの属性を取得すると時間がかかることに関する記事です。

概要

100MBを超え、圧縮ファイル数が1000以上のZIPファイルの属性をIShellFolder.GetAttributesOf()メソッドや、SHGetFileInfo()で取得しようとすると処理に長い時間がかかることがあります。

処理に時間のかかるコードの例

例1

  WindowsAPI.SFGAOF uFlags = WindowsAPI.SFGAOF.SFGAO_FOLDER | WindowsAPI.SFGAOF.SFGAO_HASSUBFOLDER
    | WindowsAPI.SFGAOF.SFGAO_STREAM | WindowsAPI.SFGAOF.SFGAO_DROPTARGET;
  ParentShellFolder.GetAttributesOf(1, new IntPtr[] { pIDL }, ref uFlags);
  //ParentShellFolder はIShellFolder

例2

  WindowsAPI.SHFILEINFO shInfo = new WindowsAPI.SHFILEINFO();
  WindowsAPI.SHGFI vFlags = WindowsAPI.SHGFI.SHGFI_SMALLICON | WindowsAPI.SHGFI.SHGFI_SYSICONINDEX |
    WindowsAPI.SHGFI.SHGFI_PIDL | WindowsAPI.SHGFI.SHGFI_DISPLAYNAME | WindowsAPI.SHGFI.SHGFI_ATTRIBUTES;

  WindowsAPI.SHGetFileInfo(m_pIDL, 0, out shInfo, (uint)Marshal.SizeOf(shInfo), vFlags);

補足

SHGetFileInfoの場合は、属性取得がなければ速度低下は起きません。
下記の場合は速度低下はありません。
  WindowsAPI.SHFILEINFO shInfo = new WindowsAPI.SHFILEINFO();
  WindowsAPI.SHGFI vFlags = WindowsAPI.SHGFI.SHGFI_SMALLICON | WindowsAPI.SHGFI.SHGFI_SYSICONINDEX |
    WindowsAPI.SHGFI.SHGFI_PIDL | WindowsAPI.SHGFI.SHGFI_DISPLAYNAME;

  WindowsAPI.SHGetFileInfo(m_pIDL, 0, out shInfo, (uint)Marshal.SizeOf(shInfo), vFlags);

対策

IShellFolder.GetAttributesOf()メソッドや、SHGetFileInfo()でZIPファイルの場合は属性を取得しないように対策する方法があります。

Zipファイルの判定をSHGetFileInfo()やIShellFolder.GetAttributesOf()を利用せずに、System.IO.File.Exists()とSystem.IO.Path.GetExtension()で判定し、ZIPファイルの場合はシェルネームペースの関数を呼び出さない対処をすることで動作速度を上げることができます。
  WindowsAPI.SHFILEINFO shInfo = new WindowsAPI.SHFILEINFO();
  WindowsAPI.SHGFI vFlags = WindowsAPI.SHGFI.SHGFI_SMALLICON | WindowsAPI.SHGFI.SHGFI_SYSICONINDEX |
      WindowsAPI.SHGFI.SHGFI_PIDL | WindowsAPI.SHGFI.SHGFI_DISPLAYNAME;

  WindowsAPI.SHGetFileInfo(m_pIDL, 0, out shInfo, (uint)Marshal.SizeOf(shInfo), vFlags);
  DisplayName = shInfo.szDisplayName;
  IconIndex = shInfo.iIcon;
  Path = GetPath();

  if (System.IO.File.Exists(Path) == true && System.IO.Path.GetExtension(Path).ToLower() == ".zip") {
    //ZIP
  }
  else {
    WindowsAPI.SFGAOF uFlags = WindowsAPI.SFGAOF.SFGAO_FOLDER | WindowsAPI.SFGAOF.SFGAO_HASSUBFOLDER
      | WindowsAPI.SFGAOF.SFGAO_STREAM | WindowsAPI.SFGAOF.SFGAO_DROPTARGET;
    ParentShellFolder.GetAttributesOf(1, new IntPtr[] { pIDL }, ref uFlags);

    //shDesktop.GetAttributesOf(1, new IntPtr[]{ m_pIDL}, ref uFlags);
    IsFolder = Convert.ToBoolean(uFlags & WindowsAPI.SFGAOF.SFGAO_FOLDER);
    HasSubFolder = Convert.ToBoolean(uFlags & WindowsAPI.SFGAOF.SFGAO_HASSUBFOLDER);
    IsDropTarget = Convert.ToBoolean(uFlags & WindowsAPI.SFGAOF.SFGAO_DROPTARGET);
    IsStream = Convert.ToBoolean(uFlags & WindowsAPI.SFGAOF.SFGAO_STREAM);
  }
上記のGetPath()メソッドは下記です。
public string GetPath()
{
  StringBuilder strBuffer = new StringBuilder(256);
  WindowsAPI.SHGetPathFromIDList(m_pIDL, strBuffer);
  return strBuffer.ToString();
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2024-01-06
作成日: 2015-06-08
iPentec all rights reserverd.