none
ExtractAssociatedIconでネットワーク上のファイルのICONが取得できない

    質問

  • VS2005 VB.NETで開発中です。ListViewにアイコン付きのファイル一覧を表示しようとしていますが、
    ネットワーク上の共有フォルダにあるファイルのアイコンを取得しようとするとエラーが発生して困っています。
    何かご存知方いらっしゃいましたらアドバイスをお願いします。

    ◇ローカルドライブの場合
     Icon = Icon.ExtractAssociatedIcon("C:\WINDOWS\system32\notepad.exe")
     メモ帳のアイコンが取れます。

    ◆ネットワーク上のファイルの場合
     notepad.exeを共有フォルダに置いて、サーバー名で取得しようとすると 
     Icon = Icon.ExtractAssociatedIcon("\\hogeServer\hoge\notepad.exe")
      「'\\hogeServer\hoge\notepad.exe' の値は 'filePath' に対して有効ではありません。」
     と言うエラーになります。

    ◇ネットワークドライブに割り当てた場合
     \\hogeServer\hogeをZドライブに割り当てると
      Icon = Icon.ExtractAssociatedIcon("Z:\notepad.exe")
     メモ帳のアイコンが取れます。

    ☆\\[サーバー名]\で始まるパスは、ExtractAssociatedIconでは使えないのでしょうか。

     よろしくお願いします。

     

    2011年5月20日 0:25

回答

すべての返信

  • ドキュメント通りです。Icon.ExtractAssociatedIconには、\\で始まるUNCパスの場合、ArgumentException例外になると書かれています。
    • 回答としてマーク zuguro 2011年5月21日 4:38
    2011年5月20日 1:28
  • ドキュメント通りです。Icon.ExtractAssociatedIconには、\\で始まるUNCパスの場合、ArgumentException例外になると書かれています。

    回答ありがとうございます。

    つまり、例外[ArgumentException]に書いてある、
    > filePath は、UNC (Universal Naming Convention) パスを示します
    とは、UNCは駄目だと言う意味ですよね。

    Icon.ExtractAssociatedIcon はローカルフォルダでしか使えないと言うことですね。
    残念(MSさんも中途半端に便利な物を用意してくれますよね)。

    結局、
    SHGetFileInfo Lib "shell32.dll" 
    iconFile = Icon.FromHandle(shinfo.hIcon)
    こちらの方法を使うことにしました。が、

    Icon.ExtractAssociatedIcon を使用した方が、SHGetFileInfo を使用するより速いのです。
    フォルダのファイル数が少ない時はスピードの差は感じませんが、
    ファイル数が数千になると違いが出ます。

    ファイルのパスを見て、UNC だったら、SHGetFileInfo を使用し、
    ローカルパスだったら、Icon.ExtractAssociatedIcon を使用することにしたら
    こんな感じになりました。

    For Each file In dir.GetFiles()
      Dim uriAdd As New Uri(file.FullName)
      If uriAdd.IsUnc = True Then
        'ネットの共有フォルダ(スピードが落ちる)
        Dim shinfo As New SHFILEINFO()
        Dim hSuccess As IntPtr = SHGetFileInfo(file.FullName, 0, shinfo, Marshal.SizeOf(shinfo), SHGFI_ICON Or SHGFI_LARGEICON)
        If hSuccess.Equals(IntPtr.Zero) = False Then
            iconForFile = Icon.FromHandle(shinfo.hIcon)
        End If
      Else
        'ローカルフォルダ(こちらが速い)
        iconForFile = System.Drawing.Icon.ExtractAssociatedIcon(file.FullName)
      End If
      'イメージリストにアイコンを貯める
      ImageList.Images.Add(file.Extension, iconForFile)
    Next

    2011年5月21日 4:25
  • SHGetFileInfo()を使うぐらいなら同じくShell APIのExtractAssociatedIcon()を使えばいいのでは。というかIcon.ExtractAssociatedIconは内部でこれを使っています。

    もちろんRemarksに書かれている通り、DestroyIcon()を呼ぶのをお忘れなく。

    2011年5月21日 11:56
  • SHGetFileInfo()を使うぐらいなら同じくShell APIのExtractAssociatedIcon()を使えばいいのでは。というかIcon.ExtractAssociatedIconは内部でこれを使っています。

    もちろんRemarksに書かれている通り、DestroyIcon()を呼ぶのをお忘れなく。


     なるほど。先にそう言って頂ければこちらを探したのですが、
    説明を読んでプログラムが書ける程のレベルではないものですから、
    (つまり、サンプルが無いと何もできない)
    他の方法を探していたら、SHGetFileInfo が目に入ってしまいました。

    こちらの方がスピードも差が無いですし、UNCかどうかを気にする必要もありませんね。
    DestroyIcon()は、何処で実行するのが良いのか良く分かりませんが、
    動かした感じでは、やってもやらなくても影響は少ない感じでした。

    Imports System.Reflection
    Imports System.Runtime.InteropServices

    Private Declare Function ExtractAssociatedIcon Lib "shell32" Alias "ExtractAssociatedIconA" (ByVal hInst As System.IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal lpIconPath As String, ByRef lpiIcon As Integer) As System.IntPtr
    Private Declare Function DestroyIcon Lib "user32" (ByVal hIcon As System.IntPtr) As Long

    Public Sub SetListViewFolder(ByVal sPath As String, ByRef ListView As ListView, ByRef ImageList As ImageList)
        ListView.SmallImageList = ImageList 'イメージリストをリストビューに適用
        ImageList.Images.Clear()
        ListView.Clear()
        ListView.View = View.Details    '詳細表示
        ListView.Columns.Add("名前", 300, HorizontalAlignment.Left) 

        Dim item As ListViewItem
        Dim dir As New System.IO.DirectoryInfo(sPath)
        Dim file As System.IO.FileInfo
        Dim iconForFile As Icon = SystemIcons.WinLogo    'デフォルトアイコンをセット
        
        ListView.BeginUpdate()  '描画をストップ
        '指定フォルダの下のファイルを取る
        For Each file In dir.GetFiles()
            '拡張子を見て、異なる拡張子のアイコンを取りだす
            If Not (ImageList.Images.ContainsKey(file.Extension)) Then
                'アイコンを取得
                Dim hInst As IntPtr = Marshal.GetHINSTANCE([Assembly].GetExecutingAssembly().GetModules()(0))
                Dim iIcon As Int32
                Dim hIcon As IntPtr
                hIcon = ExtractAssociatedIcon(hInst, file.FullName, iIcon)
                iconForFile = Icon.FromHandle(hIcon)
                'イメージリストに追加
                ImageList.Images.Add(file.Extension, iconForFile)
                'アイコンをメモリから削除
                Call DestroyIcon(hIcon)
            End If
            '拡張子をセットして、ファイルを追加する
            item = New ListViewItem(file.Name, 1)
            item.ImageKey = file.Extension
            ListView.Items.Add(item) 'ファイル名
        Next
        ListView.EndUpdate()    '描画を再開
    End Sub

    こうなりました(数千ファイル有っても、あっという間に持ってきますね)。

    2011年5月23日 7:28