2010-08-26 9 views
5

Ich habe eine Situation, wo ich spezielle Verarbeitung auf einigen Windows-Shell spezielle Ordner (die entsprechenden Werte in Die CSIDL-Enum.) (Meine Lösung muss WinXP-kompatibel sein.) Das Problem, das ich habe, ist, dass ich keine Möglichkeit finde, die IShellFolder mit ihrer CSIDL abzugleichen, wenn ich auf IShellFolders stoße.Erkennen Sie Windows Shell Special Folder (dh seine CSIDL) über seine pIDL (Jetzt bestimmen, ob pIDLs gleich C# sind)

Dies ist mein aktueller Ansatz:

initialisieren eine statische Eins-zu-Eins-Datenstruktur (csidlToFromFullPIdl) alle CSIDLs ihrer pIDLs von SHGetSpecialFolderLocation zurückgegeben.

foreach (CSIDL csidl in Enum.GetValues(typeof(CSIDL)) 
{ 
    IntPtr fullPIdl = IntPtr.Zero; 
    int hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, csidl, ref fullPIdl); 
    if (hResult != 0) 
     Marshal.ThrowExceptionForHR(hResult); 
    csidlToFromFullPIdl.Add(csidl, fullPIdl); 
} 

die Hierarchie mit dem IShellFolder Desktop-Start:

int hResult = ShellApi.SHGetDesktopFolder(ref _shellFolder); 
hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, CSIDL.CSIDL_DESKTOP, ref _fullPIdl); 

Kinder Retrieve wie so:

hResult = _shellFolder.EnumObjects(IntPtr.Zero, SHCONTF.SHCONTF_FOLDERS, out pEnum); 

// Then repeatedly call: 
pEnum.Next(1, out childRelativePIdl, out numberGotten); 

neue Konstrukt vollständig qualifizierte pIDLs für die Kinder wie so:

_fullPIdl = ShellApi.ILCombine(parentFullPIdl, childRelativePIdl); 

(Schließlich rufen Sie die IShellFolder für das Kind :)

hResultUint = parentShellItem.ShellFolder.BindToObject(childRelativePIdl, IntPtr.Zero, ShellApi.IID_IShellFolder, out _shellFolder); 

Das Problem ist, ich habe mit, dass weder der childRelativePIdl noch die _fullPIdl zu irgendwelchen pIDLs in csidlToFromFullPIdl entsprechen.

TIA.

FYI auf Maschinen Vista die GUID corresponding to KNOWNFOLDERIDs kann eine Lösung (aber nicht für mich, wie ich WinXP kompatibel sein muss.) Sein

ich auch sagen soll, dass ich denke, die Pfade des speziellen Ordners mit (via SHGetSpecialFolderPath) ist ungenügend, da einige der speziellen Ordner, an denen ich interessiert bin, keine Pfade haben. (Zum Beispiel CSIDL_DRIVES und CSIDL_NETWORK.)


Ich versuche, zwei Ansätze. Die erste ist SHGetDataFromIDList zu verwenden, um die Clsid abzurufen, die ich dann zu den bekannten CLSIDs vergleichen kann:

public static Guid GetClsidFromFullPIdl(IntPtr fullPIdl) 
{ 
    // Get both parent's IShellFolder and pIDL relative to parent from full pIDL 
    IntPtr pParentShellFolder; 
    IntPtr relativePIdl = IntPtr.Zero; 
    int hResultInt = ShellApi.SHBindToParent(fullPIdl, ShellGuids.IID_IShellFolder, out pParentShellFolder, ref relativePIdl); 
    if (hResultInt != (int)HRESULT.S_OK) 
     Marshal.ThrowExceptionForHR(hResultInt); 
    object parentShellFolderObj = System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown(
     pParentShellFolder, typeof(IShellFolder)); 
    IShellFolder parentShellFolder = (IShellFolder)parentShellFolderObj; 

    SHDESCRIPTIONID descriptionId = new SHDESCRIPTIONID(); 
    IntPtr pDescriptionId = MarshalToPointer(descriptionId); 
    // Next line returns hResult corresponding to NotImplementedException 
    hResultInt = ShellApi.SHGetDataFromIDList(parentShellFolder, ref relativePIdl, SHGDFIL.SHGDFIL_DESCRIPTIONID, pDescriptionId, 
     Marshal.SizeOf(typeof(SHDESCRIPTIONID))); 
    if (hResultInt != (int)HRESULT.S_OK) 
     Marshal.ThrowExceptionForHR(hResultInt); 

    if (parentShellFolder != null) 
     Marshal.ReleaseComObject(parentShellFolder); 

    return descriptionId.Clsid; 
} 

static IntPtr MarshalToPointer(object data) 
{ 
    IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(data)); 
    Marshal.StructureToPtr(data, pData, false); 
    return pData; 
} 

Das Problem bei diesem Ansatz besteht darin, dass der Aufruf von SHGetDataFromIDList eine hResult zurückgibt, der zu werfen einen NotImplementedException entspricht. Bedeutet dies, dass SHGetDataFromIDList auf meinem System nicht verfügbar ist? (WinXP SP3.)

Mein zweiter Ansatz besteht darin, die Elementbezeichnerlisten, auf die durch zwei Zeiger verwiesen wird, mit Objektbezeichnerlisten zu vergleichen und festzustellen, ob sie gleich sind. Ich bin der Umsetzung einer Technik here in C codiert Dies ist, was ich bisher:

Antwort

0
static bool pIdlsAreEquivalent(IntPtr pIdl1, IntPtr pIdl2) 
{ 
    if (pIdl1 == pIdl2) return true; 
    if (pIdl1 == IntPtr.Zero || pIdl2 == IntPtr.Zero) return false; 
    int pIdl1Size = GetItemIDSize(pIdl1); 
    if (pIdl1Size != GetItemIDSize(pIdl2)) return false; 
    byte[] byteArray1 = new byte[pIdl1Size]; 
    byte[] byteArray2 = new byte[pIdl1Size]; 
    Marshal.Copy(pIdl1, byteArray1, 0, pIdl1Size); 
    Marshal.Copy(pIdl2, byteArray2, 0, pIdl1Size); 
    for (int i = 0; i < pIdl1Size; i++) 
    { 
     if (byteArray1[i] != byteArray2[i]) 
      return false; 
    } 
    return true; 
} 

static int GetItemIdSize(IntPtr pIdl) 
{ 
    if (pIdl == IntPtr.Zero) return 0; 
    int size = 0; 
    // Next line throws AccessViolationException 
    ITEMIDLIST idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST)); 
    while (idl.mkid.cb > 0) 
    { 
     size += idl.mkid.cb; 
     pIdl = (IntPtr)((int)pIdl + idl.mkid.cb); 
     idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST)); 
    } 
    return size; 
} 

public struct ITEMIDLIST 
{ 
    public SHITEMID mkid; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct SHITEMID 
{ 
    public ushort cb; // The size of identifier, in bytes, including cb itself. 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
    public byte[] abID; // A variable-length item identifier. 
} 
+1

ITEMIDLISTs können äquivalent sein, ohne Byte für Byte identisch zu sein. Verwenden Sie IShellFolder :: CompareIDs, um die Äquivalenz zu testen. –

+0

Danke @RaymondChen. –