(C#)Windows Shell 外殼編程系列2 – 解釋,從“案頭”開始展開

來源:互聯網
上載者:User

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

讓我們詳細解釋一下 Shell 編程中最基本的一些函數、結構體和枚舉。
SHGetDesktopFolder
擷取案頭的 IShellFolder 介面

[DllImport("shell32.dll")]
public static extern Int32 SHGetDesktopFolder(out IntPtr ppshf);

要使用這個函數,必須先定義一個 IntPtr 指標。然後通過指標,使用 GetObjectForIUnknown 返回通過指向 COM 物件的 IShellFolder 介面的指標執行個體。於是需要編寫以下函數:

public static IShellFolder GetDesktopFolder(out IntPtr ppshf)
{
            SHGetDesktopFolder(out ppshf);
            Object obj = Marshal.GetObjectForIUnknown(ppshf);
return (IShellFolder)obj;
        }

ParseDisplayName
獲得對象的PIDL,即便對象在分類樹中處於目前的目錄下一層或更多層。例如,對於檔案對象來說,它的解析名就是它的路徑,我們用檔案系統對象的完全路徑名來調用案頭的IshellFolder介面的 ParseDisplayName 方法,它會返回這個對象的完全PIDL。定義:

void ParseDisplayName(
            IntPtr hwnd,
            IntPtr pbc,
            [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName,
out uint pchEaten,
out IntPtr ppidl,
ref uint pdwAttributes);

裡面最重要的參數就是 out IntPtr ppidl 了,它返回 pszDisplayName 指定路徑對應的 PIDL。然而僅僅是 PIDL 並不能讓你做更多的事情。這時候還需要調用 BindToObject 來返回 IShellFolder 介面。
BindToObject
根據 PIDL 建立和初始化 IShellFolder 對象。定義:

void BindToObject(
            IntPtr pidl,
            IntPtr pbc,
            [In()] ref Guid riid,
out IShellFolder ppv);

裡面有一個 [In()] ref Guid riid 參數,表示介面的介面標識符 (IID)。GUID其實就是一個唯一的標識符。世界上的任何兩台電腦都不會產生重複的 GUID 值。GUID 主要用於在擁有多個節點、多台電腦的網路或系統中,分配必須具有唯一性的標識符。我們這裡使用 IID_IShellFolder 表示它擷取的是一個 IShellFolder 介面。

public static Guid IID_IShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}");

另外介紹 IEnumIDList 介面。IEnumIDList 介面使資源管理員獲得檔案夾包含的全部對象的PIDL,PIDL然後可以用來獲得這些對象的資訊。
因此,我們使用 EnumObjects 函數返回的將是 IEnumIDList 的指標:

int EnumObjects(IntPtr hWnd, SHCONTF flags, out IntPtr enumIDList);

其中 flags 是 SHCONTF 枚舉類型,它決定了枚舉的內容:

SHCONTF
public enum SHCONTF
{
        FOLDERS = 0x20,
        NONFOLDERS = 0x40,
        INCLUDEHIDDEN = 0x80,
        INIT_ON_FIRST_NEXT = 0x100,
        NETPRINTERSRCH = 0x200,
        SHAREABLE = 0x400,
        STORAGE = 0x800
    }

因此,我們可以通過 flags 的不同來分別列舉子檔案和子目錄。這裡會遇到一個問題,怎麼擷取 PIDL 對象的名稱呢。這裡編寫了2個函數,可以通過 PIDL 或者 IShellFolder 返回對象的名稱(詳細解釋留到下一節):

擷取名稱
/**//// <summary>
/// 擷取顯示名稱
/// </summary>
public static string GetNameByIShell(IShellFolder Root, IntPtr pidlSub)
{
            IntPtr strr = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
            Marshal.WriteInt32(strr, 0, 0);
            StringBuilder buf = new StringBuilder(MAX_PATH);
            Root.GetDisplayNameOf(pidlSub, SHGNO.INFOLDER, strr);
            API.StrRetToBuf(strr, pidlSub, buf, MAX_PATH);
return buf.ToString();
        }

/**//// <summary>
/// 根據 PIDL 擷取顯示名稱
/// </summary>
public static string GetNameByPIDL(IntPtr pidl)
{
            SHFILEINFO info = new SHFILEINFO();
            API.SHGetFileInfo(pidl, 0, ref info, Marshal.SizeOf(typeof(SHFILEINFO)),
                SHGFI.PIDL | SHGFI.DISPLAYNAME | SHGFI.TYPENAME);
return info.szDisplayName;
        }

例子二,從“案頭”開始展開
這個例子將使你深入理解之前的內容。它是這樣的一個例子,允許你從“案頭”開始,一直展開到最深層的對象。

例2
public partial class Sample2 : Form
{
private IShellFolder deskTop;

public Sample2()
{
            InitializeComponent();
        }

private void Form1_Load(object sender, EventArgs e)
{
//獲得案頭 PIDL
            IntPtr deskTopPtr;
            deskTop = API.GetDesktopFolder(out deskTopPtr);

//添加 案頭 節點
            TreeNode tnDesktop = new TreeNode("案頭");
            tnDesktop.Tag = deskTop;
            tnDesktop.Nodes.Add("");

//把節點添加到樹中
            Tree1.Nodes.Add(tnDesktop);
            tnDesktop.Expand();
        }

private void Tree1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
判斷節點是否已經展開#region 判斷節點是否已經展開
if (e.Node.Nodes.Count != 1)
{
return;
            }
else
{
if (e.Node.FirstNode.Text != "")
{
return;
                }
            }

            e.Node.Nodes.Clear(); 
#endregion

            IShellFolder root = (IShellFolder)e.Node.Tag;

//迴圈尋找子項
            IEnumIDList Enum = null;
            IntPtr EnumPtr = IntPtr.Zero;
            IntPtr pidlSub;
int celtFetched;

if (root.EnumObjects(this.Handle, SHCONTF.FOLDERS, out EnumPtr) == API.S_OK)
{
                Enum = (IEnumIDList)Marshal.GetObjectForIUnknown(EnumPtr);
while (Enum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
{
string name = API.GetNameByIShell(root, pidlSub);
                    IShellFolder iSub;
                    root.BindToObject(pidlSub, IntPtr.Zero, ref Guids.IID_IShellFolder, out iSub);

                    TreeNode nodeSub = new TreeNode(name);
                    nodeSub.Tag = iSub;
                    nodeSub.Nodes.Add("");
                    e.Node.Nodes.Add(nodeSub);
                }
            }
        }

private void Sample2_FormClosing(object sender, FormClosingEventArgs e)
{
//釋放資源
            Marshal.ReleaseComObject(deskTop);
        }

    }

照例,附圖片和原始碼:

原始碼:/Files/lemony/WinShell2.rar

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.