(C#)Windows Shell 外殼編程系列5 – 擷取表徵圖

來源:互聯網
上載者:User

(本系列文章由檸檬的(lc_mtt)原創,轉載請註明出處,謝謝~)

有關 PIDL

PIDL亦有“絕對路徑”與“相對路徑”的概念。表示“相對路徑”的PIDL(本文簡稱為“相對PIDL”)只有一個ITEMIDLIST結構的元素,用於標識相對於父資料夾的“路徑”;表示“絕對路徑”的PIDL(簡稱為“絕對PIDL”)有若干個ITEMIDLIST結構的元素,第一個元素表示外殼名字空間根資料夾(“案頭”)下的某一子檔案夾A,第二個元素則表示檔案夾A下的某一子檔案夾B,其餘依此類推。這樣絕對PIDL就通過儲存一條從“案頭”下的直接子檔案夾或檔案的絕對PIDL與相對PIDL是相同的,而其他的檔案夾或檔案的相對PIDL就只是其絕對PIDL的最後一部分了。
為什麼要說這些呢?因為有些函數,必須使用絕對PIDL,例標,如果不使用絕對PIDL,某些表徵圖是無法正常獲得的(磁碟機、控制台等)。
    但使用 EnumObjects 獲得的,僅僅是相對PIDL,如果通過相對PIDL擷取絕對PIDL呢?我參考了開源項目 C# FileBrowser 中的 PIDL 類

PIDL.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;

namespace WinShell
{
public class PIDL
{
private IntPtr pidl = IntPtr.Zero;

public PIDL(IntPtr pidl, bool clone)
{
if (clone)
this.pidl = ILClone(pidl);
else
this.pidl = pidl;
        }

public PIDL(PIDL pidl, bool clone)
{
if (clone)
this.pidl = ILClone(pidl.Ptr);
else
this.pidl = pidl.Ptr;
        }


public IntPtr Ptr 

get { return pidl; }
        }

public void Insert(IntPtr insertPidl)
{
            IntPtr newPidl = ILCombine(insertPidl, pidl);

            Marshal.FreeCoTaskMem(pidl);
            pidl = newPidl;
        }

public void Free()
{
if (pidl != IntPtr.Zero)
{
                Marshal.FreeCoTaskMem(pidl);
                pidl = IntPtr.Zero;
            }
        }


private static int ItemIDSize(IntPtr pidl)
{
if (!pidl.Equals(IntPtr.Zero))
{
byte[] buffer = new byte[2];
                Marshal.Copy(pidl, buffer, 0, 2);
return buffer[1] * 256 + buffer[0];
            }
else
return 0;
        }

private static int ItemIDListSize(IntPtr pidl)
{
if (pidl.Equals(IntPtr.Zero))
return 0;
else
{
int size = ItemIDSize(pidl);
int nextSize = Marshal.ReadByte(pidl, size) + (Marshal.ReadByte(pidl, size + 1) * 256);
while (nextSize > 0)
{
                    size += nextSize;
                    nextSize = Marshal.ReadByte(pidl, size) + (Marshal.ReadByte(pidl, size + 1) * 256);
                }

return size;
            }
        }

public static IntPtr ILClone(IntPtr pidl)
{
int size = ItemIDListSize(pidl);

byte[] bytes = new byte[size + 2];
            Marshal.Copy(pidl, bytes, 0, size);

            IntPtr newPidl = Marshal.AllocCoTaskMem(size + 2);
            Marshal.Copy(bytes, 0, newPidl, size + 2);

return newPidl;
        }

public static IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2)
{
int size1 = ItemIDListSize(pidl1);
int size2 = ItemIDListSize(pidl2);

            IntPtr newPidl = Marshal.AllocCoTaskMem(size1 + size2 + 2);
byte[] bytes = new byte[size1 + size2 + 2];

            Marshal.Copy(pidl1, bytes, 0, size1);
            Marshal.Copy(pidl2, bytes, size1, size2);

            Marshal.Copy(bytes, 0, newPidl, bytes.Length);

return newPidl;
        }
    }
}

該類實現了 PIDL 的複製和結合功能。現在我們修改 ShellItem 類,使它帶有父節點的 IShellFolder 以及提供擷取絕對 PIDL 的屬性:

private ShellItem m_ParentItem;

public ShellItem ParentItem
{
get { return m_ParentItem; }
set { m_ParentItem = value; }
}

/**//// <summary>
/// 絕對 PIDL
/// </summary>
public PIDL PIDLFull
{
get
{
        PIDL pidlFull = new PIDL(PIDL, true);
        ShellItem current = ParentItem;
while (current != null)
{
            pidlFull.Insert(current.PIDL);
            current = current.ParentItem;
        }
return pidlFull;
    }
}

擷取表徵圖
    言歸正傳,既然已經獲得絕對 PIDL,那麼擷取表徵圖就是很簡單的事情了,我們使用的是 SHGetFileInfo 這個API:

[DllImport("shell32", EntryPoint = "SHGetFileInfo", ExactSpelling = false, 
    CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SHGetFileInfo(
    IntPtr ppidl, 
    FILE_ATTRIBUTE dwFileAttributes, 
ref SHFILEINFO sfi, 
int cbFileInfo, 
    SHGFI uFlags);

[DllImport("Shell32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SHGetFileInfo(
string Path, 
    FILE_ATTRIBUTE fileAttributes, 
out SHFILEINFO sfi, 
int cbFileInfo, SHGFI flags);

這裡提供了一個重載,你可以選擇是通過 PIDL 還是 路徑 擷取表徵圖(如果是路徑,那麼僅僅能擷取 檔案夾/檔案 的表徵圖)。

/**//// <summary>
/// 擷取小表徵圖索引
/// </summary>
public static int GetSmallIconIndex(string strFilename)
{
    SHFILEINFO psfi = new SHFILEINFO();
    IntPtr ipIcon = SHGetFileInfo(strFilename, 0, out psfi, Marshal.SizeOf(psfi),
        SHGFI.ICON | SHGFI.SMALLICON | SHGFI.SYSICONINDEX);

return psfi.iIcon;
}

public static int GetSmallIconIndex(IntPtr ipIDList)
{
    SHFILEINFO psfi = new SHFILEINFO();
    IntPtr ipIcon = SHGetFileInfo(ipIDList, 0, ref psfi, Marshal.SizeOf(psfi),
        SHGFI.ICON | SHGFI.PIDL | SHGFI.SMALLICON | SHGFI.SYSICONINDEX);

return psfi.iIcon;
}

大家也許會覺得奇怪,GetSmallIconIndex 返回的是 int ,到底要怎麼使用?
其實沒錯,GetSmallIconIndex 僅僅是返回該表徵圖在系統映像列表(System ImageList)的索引(Index)而已。我們只要擷取系統映像列表的指標,再把它關聯到你的 TreeView 或 ListView ,即可通過 Icon Index 來顯示表徵圖了。

IntPtr m_ipSmallSystemImageList;
IntPtr m_ipLargeSystemImageList;

//擷取系統 ImageList
SHFILEINFO shfi = new SHFILEINFO();

m_ipSmallSystemImageList = API.SHGetFileInfo("", 0, out shfi, Marshal.SizeOf(typeof(SHFILEINFO)),
    SHGFI.SYSICONINDEX | SHGFI.SMALLICON | SHGFI.USEFILEATTRIBUTES);

m_ipLargeSystemImageList = API.SHGetFileInfo("", 0, out shfi, Marshal.SizeOf(typeof(SHFILEINFO)),
    SHGFI.SYSICONINDEX | SHGFI.LARGEICON | SHGFI.USEFILEATTRIBUTES);

//把系統 ImageList 關聯到 TreeView 和 ListView
API.SendMessage(Tree1.Handle, API.TVM_SETIMAGELIST, API.TVSIL_NORMAL, m_ipSmallSystemImageList);
API.SendMessage(lvFile.Handle, API.LVM_SETIMAGELIST, API.LVSIL_NORMAL, m_ipLargeSystemImageList);

OK,我們修改以往的例子,就可以在 Tree 節點上顯示表徵圖了:


ShellItem shellItem=new ShellItem(pidlSub, iSub, sItem);
int imgIndex = API.GetSmallIconIndex(shellItem.PIDLFull.Ptr);
TreeNode nodeSub = new TreeNode(name, imgIndex, imgIndex);

(註:關於文中出現的一些結構體或常量,讀者可以自行查閱 MSDN,精力有限實在不能一一說明。)
我們來看一下效果:

事實上,這個代碼改了很多,也涉及到下一節的部分內容,因此代碼將在下一節中拋出...

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.