//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN
#include <vcl.h>
#include <shlwapi.h>
#pragma hdrstop

#include "Unit1.h"
#include "Unit2.h"
#include "Utils.h"
#include "pidl.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

struct TNoteItem
{
        int severity;
        AnsiString data;
};

struct TTreeItemData
{
    IShellFolder *pParentFolder;
    IShellFolder2 *pParentFolder2;
    IShellFolder *pFolder;
    LPITEMIDLIST pidl;
    LPITEMIDLIST relpidl;
    TList *pNotes;
};

GUID IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, 0xbc55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe};

#define SFGAO_STORAGE         0x00000008
#define SFGAO_ENCRYPTED       0x00002000
#define SFGAO_ISSLOW          0x00004000
#define SFGAO_STREAM          0x00400000
#define SFGAO_STORAGEANCESTOR 0x00800000

#define PT_DESKTOP_LINK 0x3a

char tmpbuffer[200];

#define CHECK_HRESULT(x)   case x: return #x
const char *HRESULT_name(HRESULT res)
{
        switch (res)
        {
                CHECK_HRESULT(S_OK);
                CHECK_HRESULT(S_FALSE);
                CHECK_HRESULT(E_NOINTERFACE);
                CHECK_HRESULT(E_NOTIMPL);
                CHECK_HRESULT(E_FAIL);
                CHECK_HRESULT(REGDB_E_CLASSNOTREG);
                CHECK_HRESULT(OLE_E_ADVF);
                CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
                CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
        }

        wsprintf(tmpbuffer, "%x", res);
        return tmpbuffer;
}

void addNote(TTreeItemData *pData, int severity, const char *string)
{
        TNoteItem *item = new TNoteItem;
        item->data = AnsiString(string);
        item->severity = severity;
        pData->pNotes->Add(item);
}

typedef void (*ParseFunc)(TTreeView *tree, TTreeNode *node, SHITEMID *id);

int HexDump(char *buf, BYTE *data, int count)
{
    buf[0] = 0;
    for (int i=0; i<16; i++)
        if (i<count)
            wsprintf(buf+strlen(buf), "%02x ", data[i]);
    for (int i=strlen(buf); i<50; i++)
        buf[i]=' ';
    for (int i=0; i<16; i++)
        if (i<count)
            buf[50+i] = (data[i]>=32 && data[i]<127? data[i]:'.');
        else
            buf[50+i] = 0;
    buf[66] = 0;
    return (count<16 ? count : 16);
}

void ParseID_GUID(TTreeView *tree, TTreeNode *node, SHITEMID *id)
{
    CHAR buffer[200];
    LPWSTR clsid;
    if (id->cb-3 < sizeof(GUIDStruct))
    {
        tree->Items->AddChild(node, "Error: item too small");
        return;
    }

    wsprintf(buffer, "Type:    %02x", id->abID[0]);
    tree->Items->AddChild(node, AnsiString(buffer));
    wsprintf(buffer, "Unknown: %02x", id->abID[1]);
    tree->Items->AddChild(node, AnsiString(buffer));
    OleCheck(StringFromCLSID(*(CLSID *)(id->abID+2), &clsid));
    wsprintf(buffer, "CLSID:   %ws", clsid);
    CoTaskMemFree(clsid);
    tree->Items->AddChild(node, AnsiString(buffer));

    if (id->cb-3 > sizeof(GUIDStruct))
    {
        wsprintf(buffer, "Extra bytes: %d\n", id->cb - sizeof(GUIDStruct));
        tree->Items->AddChild(node, AnsiString(buffer));
    }
}

bool HasZero(BYTE *pos, BYTE *end)
{
    for (BYTE *b = pos; b<end; b++)
        if (*b == 0)
            return true;
    return false;
}

bool HasWideZero(BYTE *pos, BYTE *end)
{
    for (WCHAR *ch = (WCHAR *)pos; (end-(BYTE *)ch)>=2; ch++)
        if (*ch == 0)
            return true;
    return false;
}


void AddTimeNode(TTreeView *tree, TTreeNode *node, const char *name, WORD uDate, WORD uTime)
{
    FILETIME time;
    SYSTEMTIME systime;
    CHAR buffer[200];
    DosDateTimeToFileTime(uDate, uTime, &time);
    FileTimeToSystemTime(&time, &systime);
    wsprintf(buffer, "File %s: %d.%02d.%d, %d:%02d:%02d", name,
        systime.wDay, systime.wMonth, systime.wYear,
        systime.wHour, systime.wMinute, systime.wSecond);
    tree->Items->AddChild(node, buffer);
}

void ParseID_Value(TTreeView *tree, TTreeNode *node, SHITEMID *id)
{
    CHAR buffer[200];
    FileStruct *fs = (FileStruct *)(id->abID+1);
    LPWSTR clsid;
    if (id->cb-3 < sizeof(FileStruct))
    {
        tree->Items->AddChild(node, "Error: item too small");
        return;
    }

    wsprintf(buffer, "Type:       %02x", id->abID[0]);
    tree->Items->AddChild(node, AnsiString(buffer));
    wsprintf(buffer, "Zero:       %02x", fs->dummy);
    tree->Items->AddChild(node, AnsiString(buffer));
    wsprintf(buffer, "File size:  %d", fs->dwFileSize);
    tree->Items->AddChild(node, AnsiString(buffer));
    AddTimeNode(tree, node, "mtime", fs->uFileDate, fs->uFileTime);
    wsprintf(buffer, "Attributes: %04x", fs->uFileAttribs);
    tree->Items->AddChild(node, AnsiString(buffer));

    BYTE *pos = fs->szNames;
    BYTE *end = (BYTE *)id+id->cb;
    WORD NTOffset = *(WORD *)(end-2);

    if (HasZero(pos, end))
    {
        wsprintf(buffer, "Filename1: \"%hs\"", pos);
        tree->Items->AddChild(node, AnsiString(buffer));
        pos += lstrlen(pos)+1;

        if ((NTOffset != pos-(BYTE *)id) && HasZero(pos, end))
        {
            wsprintf(buffer, "Filename2: \"%hs\"", pos);
            tree->Items->AddChild(node, AnsiString(buffer));
            pos += lstrlen(pos)+1;
        }

        if (end-pos>0 && NTOffset == pos-(BYTE *)id)
        {
            TTreeNode *ntnode = tree->Items->AddChild(node, "NT extra information");
            if (end-pos < sizeof(FileStructW))
            {
                tree->Items->AddChild(ntnode, "Error: item too small");
            }
            else
            {
                FileStructW *fsW = (FileStructW *)pos;
                wsprintf(buffer, "Len:        %d", fsW->cbLen);
                tree->Items->AddChild(ntnode, buffer);
                wsprintf(buffer, "Unknown:    %02x %02x %02x %02x %02x %02x",
                    fsW->dummy1[0], fsW->dummy1[1], fsW->dummy1[2],
                    fsW->dummy1[3], fsW->dummy1[4], fsW->dummy1[5]);
                tree->Items->AddChild(ntnode, buffer);
                AddTimeNode(tree, ntnode, "ctime", fsW->uCreationDate, fsW->uCreationTime);
                AddTimeNode(tree, ntnode, "atime", fsW->uCreationDate, fsW->uLastAccessDate);
                wsprintf(buffer, "Unknown:    %02x %02x %02x %02x",
                    fsW->dummy2[0], fsW->dummy2[1], fsW->dummy2[2], fsW->dummy2[3]);
                tree->Items->AddChild(ntnode, buffer);
                pos = (BYTE *)&fsW->wszName;
                if (HasWideZero(pos, end))
                {
                    wsprintf(buffer, "Wide name:  \"%ws\"", (WCHAR *)pos);
                    pos += sizeof(WCHAR)*(lstrlenW((WCHAR *)pos)+1);
                    tree->Items->AddChild(ntnode, buffer);
                }
                if (HasWideZero(pos, end))
                {
                    wsprintf(buffer, "Wide name2: \"%ws\"", (WCHAR *)pos);
                    pos += sizeof(WCHAR)*(lstrlenW((WCHAR *)pos)+1);
                    tree->Items->AddChild(ntnode, buffer);
                }
            }
            pos += 2;
        }
    }

    if (end-pos > 0)
    {
        wsprintf(buffer, "Extra bytes: %d\n", end-pos);
        tree->Items->AddChild(node, AnsiString(buffer));
    }
}

void ParseID_Unknown(TTreeView *tree, TTreeNode *node, SHITEMID *id)
{
    BYTE *data = id->abID;
    int count = id->cb-2;
    CHAR buf[200];

    while (count > 0)
    {
        int written = HexDump(buf, data, count);
        tree->Items->AddChild(node, buf);
        count -= written;
        data += written;
    }
}

void DumpPIDLToTree(TTreeView *tree, LPITEMIDLIST pidl)
{
    if (pidl == NULL)
        return;

    while (pidl->mkid.cb != 0)
    {
        BYTE *data = pidl->mkid.abID;
        const char *name;
        ParseFunc pParse;
        TTreeNode *node;
        TCHAR buf[200];

        switch (data[0])
        {
            case PT_GUID:     name = "PT_GUID";        pParse = ParseID_GUID;    break;
            case PT_SHELLEXT: name = "PT_SHELLEXT";    pParse = ParseID_GUID;    break;
            case PT_DRIVE:    name = "PT_DRIVE";       pParse = ParseID_Unknown; break;
            case PT_DRIVE1:   name = "PT_DRIVE1";      pParse = ParseID_Unknown; break;
            case PT_DESKTOP_LINK:   name = "PT_DESKTOP_LINK"; pParse = ParseID_Value; break;
            case PT_VALUE:    name = "PT_VALUE";       pParse = ParseID_Value;   break;
            case PT_FOLDER:   name = "PT_FOLDER";      pParse = ParseID_Value;   break;
            default:          name = "Unknown";        pParse = ParseID_Unknown; break;
        }

        wsprintf(buf, "%s (%02x) [len=%d]", name, data[0], pidl->mkid.cb);
        node = tree->Items->Add(NULL, buf);
        node->Data = &pidl->mkid;
        pParse(tree, node, &pidl->mkid);
        pidl = (LPITEMIDLIST)((char *)pidl + pidl->mkid.cb);
    }

    tree->Items->Add(NULL, "Ending record [len=0]");
}

void PIDLTreeOnSelect(TTreeNode *node, TListView *dump)
{
    while (node->Parent != NULL)
        node = node->Parent;
    SHITEMID *id = (SHITEMID *)node->Data;
    dump->Items->Clear();
    if (id == NULL)
        return;

    BYTE *data = id->abID;
    int count = id->cb-2;
    CHAR buf[200];

    while (count > 0)
    {
        int written = HexDump(buf, data, count);
        count -= written;
        data += written;
        TListItem *item = dump->Items->Add();
        item->Caption = buf;
    }
}

AnsiString StrRetToAnsiString(STRRET *str, LPITEMIDLIST pidl, HRESULT res)
{
    LPSTR buf;
    AnsiString ret;

    if (FAILED(res))
    {
        TVarRec rec = HRESULT_name(res);
        return ret.Format("Error: %s", &rec, 0);
    }

    OleCheck(StrRetToStr(str, pidl, &buf));
    ret = buf;
    CoTaskMemFree(buf);
    return ret;
}

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
        TIcon *icon = new TIcon;
        icon->Handle = ExtractIcon(HInstance, "shell32.dll", -4);
        ImageList1->AddIcon(icon);

        icon = new TIcon;
        icon->Handle = ExtractIcon(HInstance, "shell32.dll", -2);
        ImageList1->AddIcon(icon);

        icon = new TIcon;
        icon->Handle = LoadIcon(NULL, IDI_INFORMATION);
        ImageListSeverity->AddIcon(icon);

        icon = new TIcon;
        icon->Handle = LoadIcon(NULL, IDI_WARNING);
        ImageListSeverity->AddIcon(icon);

        icon = new TIcon;
        icon->Handle = LoadIcon(NULL, IDI_ERROR);
        ImageListSeverity->AddIcon(icon);

        TTreeItemData *pDesktopData = new TTreeItemData();
        pDesktopData->pParentFolder = NULL;
        OleCheck(SHGetDesktopFolder(&pDesktopData->pFolder));
        pDesktopData->pidl = NULL;
        pDesktopData->relpidl = NULL;
        pDesktopData->pNotes = new TList();
        ShellTree->Items->GetFirstNode()->Data = pDesktopData;
}
//---------------------------------------------------------------------------

void AddStrRetType(TListItem *row, STRRET *strret)
{
        switch (strret->uType)
        {
                case STRRET_CSTR:
                        row->SubItems->Add("CSTR");
                        return;
                case STRRET_WSTR:
                        row->SubItems->Add("WSTR");
                        return;
                case STRRET_OFFSET:
                        row->SubItems->Add("OFFSET");
                        return;
        }

        TCHAR buf[11];
        wsprintf(buf, "0x%x", strret->uType);
        buf[10]=0;
        row->SubItems->Add(AnsiString(buf));
}

void TForm1::AddName(TTreeItemData *pData, UINT shgno, const char *shgnoName)
{
        STRRET name;
        LPTSTR str;
        TListItem *row;

        OleCheck(pData->pParentFolder->GetDisplayNameOf(pData->relpidl, shgno, &name));
        OleCheck(StrRetToStr(&name, pData->relpidl, &str));
        row = NamesList->Items->Add();
        row->Caption = AnsiString(shgnoName);
        AddStrRetType(row, &name);
        row->SubItems->Add(str);
        CoTaskMemFree(str);
}

void TForm1::DumpDetails(TTreeItemData *pData)
{
        SHELLDETAILS details;
        SHCOLUMNID colid;
        LPSTR name;
        if (pData->pParentFolder2 == NULL)
            return;
        int col=0;
        while (SUCCEEDED(pData->pParentFolder2->GetDetailsOf(NULL, col, &details)))
        {
            TListItem *item=DetailsList->Items->Add();
            OleCheck(StrRetToStr(&details.str, NULL, &name));
            item->Caption = col;
            item->SubItems->Add(name);
            CoTaskMemFree(name);

            HRESULT res = pData->pParentFolder2->GetDetailsOf(pData->relpidl, col, &details);
            item->SubItems->Add(StrRetToAnsiString(&details.str, pData->relpidl, res));
            col++;
        }
}

#define CHECK_ALL_INTERFACES \
            CHECK_INTERFACE(IUnknown) \
            CHECK_INTERFACE(IContextMenu) \
            CHECK_INTERFACE(IExtractIconA) \
            CHECK_INTERFACE(IExtractIconW) \
            CHECK_INTERFACE(IShellDetails) \
            CHECK_INTERFACE(IShellExtInit) \
            CHECK_INTERFACE(IShellFolder) \
            CHECK_INTERFACE(IShellFolder2) \
            CHECK_INTERFACE(IShellFolderViewCB) \
            CHECK_INTERFACE(IShellIcon) \
            CHECK_INTERFACE(IShellItem) \
            CHECK_INTERFACE(IShellLinkA) \
            CHECK_INTERFACE(IShellLinkW) \
            CHECK_INTERFACE(IShellView) \
            CHECK_INTERFACE(IShellView2) \
            CHECK_INTERFACE(IPersist) \
            CHECK_INTERFACE(IPersistFolder) \
            CHECK_INTERFACE(IPersistFolder2) \
            CHECK_INTERFACE(IPersistFolder3) \
            CHECK_INTERFACE(IPersistFile) \
            CHECK_INTERFACE(IPersistStorage) \
            CHECK_INTERFACE(IPersistStream) \
            CHECK_INTERFACE(IStream) \


#define CHECK_INTERFACE(x) \
        { \
            IUnknown *unk; \
            HRESULT ret = METHOD(IID_##x, (void **)&unk); \
            if (SUCCEEDED(ret)) \
            { \
                InterfacesTree->Items->AddChild(currnode, #x)->Data = (void *)&IID_##x; \
                unk->Release(); \
            } \
        }

void TForm1::CheckInterfaces(TTreeItemData *pData)
{
        TTreeNode *currnode = InterfacesTree->Items->Add(NULL, "Obtained through BindToObject");
#define METHOD(x, unk) pData->pParentFolder->BindToObject(pData->relpidl, NULL, x, unk)
        CHECK_ALL_INTERFACES;
        currnode->Expand(true);

        currnode = InterfacesTree->Items->Add(NULL, "Obtained through BindToStorage");
#undef METHOD
#define METHOD(x, unk) pData->pParentFolder->BindToStorage(pData->relpidl, NULL, x, unk)
        CHECK_ALL_INTERFACES;
        currnode->Expand(true);

        LPCITEMIDLIST cpidl = pData->relpidl;
        currnode = InterfacesTree->Items->Add(NULL, "Obtained through GetUIObjectOf");
#undef METHOD
#define METHOD(x, unk) pData->pParentFolder->GetUIObjectOf(Handle, 1, &cpidl, x, NULL, unk)
        CHECK_ALL_INTERFACES;
        currnode->Expand(true);

        if (pData->pFolder)
        {
                currnode = InterfacesTree->Items->Add(NULL, "Obtained through CreateViewObject");
#undef METHOD
#define METHOD(x, unk) pData->pFolder->CreateViewObject(Handle, x, unk)
                CHECK_ALL_INTERFACES;
                currnode->Expand(true);
        }
}

#undef METHOD
#undef CHECK_INTERFACE

#define CHECK_INTERFACE(x) \
            { \
                void *p; \
                HRESULT ret = unk->QueryInterface(IID_##x, &p); \
                if (SUCCEEDED(ret)) \
                { \
                    ObjInterfacesList->Items->Add(#x); \
                    ((IUnknown *)p)->Release(); \
                } \
            }
void TForm1::QueryInterfaces(IUnknown *unk)
{
      CHECK_ALL_INTERFACES
}

#undef CHECK_ALL_INTERFACES

#define CHECK_ATTRIBUTE(x)  if (attrs & x) AttributesListBox->Items->Add(#x)

void __fastcall TForm1::ShellTreeClick(TObject *Sender)
{
        TTreeNode *node = ShellTree->Selected;
        TTreeItemData *data = (TTreeItemData *)node->Data;
        NamesList->Items->Clear();
        NotesList->Items->Clear();
        AttributesListBox->Items->Clear();
        InterfacesTree->Items->Clear();
        ObjInterfacesList->Items->Clear();
        PIDLTree->Items->Clear();
        PIDLDump->Items->Clear();
        ActionButton->Enabled = false;
        ActionButton->Caption = "Action";
        DetailsList->Items->Clear();
        ColumnDetails->Items->Clear();

        if (data->pParentFolder == NULL)
                return;
        AddName(data, SHGDN_NORMAL, "NORMAL");
        AddName(data, SHGDN_FOREDITING, "FOREDITING");
        AddName(data, SHGDN_FOREDITING|SHGDN_INFOLDER, "FOREDITING, INFOLDER");
        AddName(data, SHGDN_FORADDRESSBAR, "FORADDRESSBAR");
        AddName(data, SHGDN_FORADDRESSBAR|SHGDN_INFOLDER, "FORADDRESSBAR, INFOLDER");
        AddName(data, SHGDN_FORPARSING, "FORPARSING");
        AddName(data, SHGDN_FORPARSING|SHGDN_INFOLDER, "FORPARSING, INFOLDER");

        for (int i=0; i<data->pNotes->Count; i++)
        {
                TListItem *item = NotesList->Items->Add();
                TNoteItem *note = (TNoteItem *)data->pNotes->Items[i];
                item->Caption = note->data;
                item->ImageIndex = note->severity;
        }

        CheckInterfaces(data);

        ULONG attrs;
        attrs = 0xffffffff;
        OleCheck(data->pParentFolder->GetAttributesOf(1, &((LPCITEMIDLIST)data->relpidl), &attrs));
        CHECK_ATTRIBUTE(SFGAO_CANCOPY);
        CHECK_ATTRIBUTE(SFGAO_CANMOVE);
        CHECK_ATTRIBUTE(SFGAO_CANLINK);
        CHECK_ATTRIBUTE(SFGAO_STORAGE);
        CHECK_ATTRIBUTE(SFGAO_CANRENAME);
        CHECK_ATTRIBUTE(SFGAO_CANDELETE);
        CHECK_ATTRIBUTE(SFGAO_HASPROPSHEET);
        CHECK_ATTRIBUTE(SFGAO_DROPTARGET);
        CHECK_ATTRIBUTE(SFGAO_ENCRYPTED);
        CHECK_ATTRIBUTE(SFGAO_ISSLOW);
        CHECK_ATTRIBUTE(SFGAO_GHOSTED);
        CHECK_ATTRIBUTE(SFGAO_LINK);
        CHECK_ATTRIBUTE(SFGAO_SHARE);
        CHECK_ATTRIBUTE(SFGAO_READONLY);
        CHECK_ATTRIBUTE(SFGAO_HIDDEN);
        CHECK_ATTRIBUTE(SFGAO_FILESYSANCESTOR);
        CHECK_ATTRIBUTE(SFGAO_FOLDER);
        CHECK_ATTRIBUTE(SFGAO_FILESYSTEM);
        CHECK_ATTRIBUTE(SFGAO_HASSUBFOLDER);
        CHECK_ATTRIBUTE(SFGAO_VALIDATE);
        CHECK_ATTRIBUTE(SFGAO_COMPRESSED);
        CHECK_ATTRIBUTE(SFGAO_BROWSABLE);
        CHECK_ATTRIBUTE(SFGAO_NONENUMERATED);
        CHECK_ATTRIBUTE(SFGAO_NEWCONTENT);
        CHECK_ATTRIBUTE(SFGAO_STREAM);
        CHECK_ATTRIBUTE(SFGAO_STORAGEANCESTOR);

        DumpPIDLToTree(PIDLTree, data->pidl);

        DumpDetails(data);
}
#undef CHECK_ATTRIBUTE
//---------------------------------------------------------------------------
void __fastcall TForm1::ShellTreeExpanding(TObject *Sender,
      TTreeNode *Node, bool &AllowExpansion)
{
        AllowExpansion = false;
        TTreeItemData *data = (TTreeItemData *)Node->Data;
        IEnumIDList *enumid;
        HRESULT res;

        OleCheck(data->pFolder->EnumObjects(Handle,
                SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN,
                &enumid));
        Node->DeleteChildren();
        while (true)
        {
                ULONG fetched;
                LPITEMIDLIST relpidl;
                STRRET name_ret;
                LPTSTR str = NULL;
                TTreeItemData *newdata;
                TTreeNode *newnode;

                OleCheck(enumid->Next(0, &relpidl, &fetched));
                if (fetched == 0)
                        break;
                newdata = new TTreeItemData;
                newdata->pParentFolder = data->pFolder;
                newdata->pParentFolder->AddRef();
                newdata->pParentFolder2 = NULL;
                newdata->pParentFolder->QueryInterface(IID_IShellFolder2, (void **)&newdata->pParentFolder2);
                newdata->relpidl = relpidl;
                newdata->pidl = ILCombine(data->pidl, newdata->relpidl);
                newdata->pNotes = new TList();
                HRESULT folderres = data->pFolder->BindToObject(relpidl, NULL, IID_IShellFolder,
                        (void **)&newdata->pFolder);
                OleCheck(data->pFolder->GetDisplayNameOf(relpidl, SHGDN_NORMAL, &name_ret));
                OleCheck(StrRetToStr(&name_ret, newdata->relpidl, &str));
                newnode = ShellTree->Items->AddChild(Node, AnsiString(str));
                newnode->Data = newdata;
                if (SUCCEEDED(folderres))
                {
                        ShellTree->Items->AddChild(newnode, "Fake child");
                }
                else
                {
                        newnode->ImageIndex = 1;
                        newnode->SelectedIndex = 1;
                }
                if (folderres != S_OK && folderres != E_NOINTERFACE)
                {
                        TCHAR buffer[200];
                        wsprintf(buffer, "pFolder->BindToObject(..., IID_IShellFodler) returned %s", HRESULT_name(folderres));
                        addNote(newdata, 0, buffer);
                }
                CoTaskMemFree(str);
        }
        enumid->Release();
        AllowExpansion = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ShellTreeCollapsing(TObject *Sender,
      TTreeNode *Node, bool &AllowCollapse)
{
        TTreeNode *child = Node->getFirstChild();
        while (child != NULL)
        {
                TTreeItemData *data = (TTreeItemData *)child->Data;
                if (data->pFolder)
                        data->pFolder->Release();
                if (data->pParentFolder)
                        data->pParentFolder->Release();
                if (data->pParentFolder2)
                        data->pParentFolder2->Release();
                CoTaskMemFree(data->relpidl);
                CoTaskMemFree(data->pidl);
                while (data->pNotes->Count > 0)
                {
                        TNoteItem *item = (TNoteItem *)data->pNotes->Items[0];
                        delete item;
                        data->pNotes->Delete(0);
                }
                child = child->getNextSibling();
        }
        Node->DeleteChildren();
        ShellTree->Items->AddChild(Node, "FakeChild");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::PIDLTreeClick(TObject *Sender)
{
        PIDLTreeOnSelect(PIDLTree->Selected, PIDLDump);
}
//---------------------------------------------------------------------------

void Action_IPF2GetPidl(IUnknown *unk)
{
        IPersistFolder2 *pf2 = (IPersistFolder2 *)unk;
        LPITEMIDLIST pidl=NULL;
        OleCheck(pf2->GetCurFolder(&pidl));
        Form2->PIDLTree->Items->Clear();
        DumpPIDLToTree(Form2->PIDLTree, pidl);
        Form2->Show();
//        CoTaskMemFree(pidl);
}

void Action_ICMShow(IUnknown *unk)
{
        IContextMenu *menu = (IContextMenu *)unk;
        HMENU hMenu = CreatePopupMenu();
        POINT mouse;

        GetCursorPos(&mouse);
        OleCheck(menu->QueryContextMenu(hMenu, 0, 1000, 1200, CMF_CANRENAME|CMF_NORMAL));
        TrackPopupMenu(hMenu, TPM_NONOTIFY, mouse.x, mouse.y, 0, Form1->Handle, NULL);
}

typedef void (*actionfunc)(IUnknown *unk);

struct IIDAction
{
        const IID *pIID;
        const char *name;
        actionfunc func;
};

IIDAction actions[] = {
        {&IID_IPersistFolder2, "Get PIDL", Action_IPF2GetPidl},
        {&IID_IContextMenu,    "Show Menu", Action_ICMShow}
};

const int actionsCount = sizeof(actions)/sizeof(actions[0]);


void __fastcall TForm1::InterfacesTreeClick(TObject *Sender)
{
        TTreeNode *sel = InterfacesTree->Selected;
        TTreeItemData *pData = (TTreeItemData *)ShellTree->Selected->Data;
        void *unk = NULL;
        ObjInterfacesList->Items->Clear();

        if (InterfacesTree->Tag!=0)
        {
                ((IUnknown *)InterfacesTree->Tag)->Release();
                InterfacesTree->Tag = 0;
        }

        if (sel->Data == NULL || pData == NULL)
                return;
        IID *pIID = (IID *)sel->Data;
        int type = sel->Parent->Index;
        LPCITEMIDLIST cpidl = pData->relpidl;
        switch (type)
        {
            case 0:
                OleCheck(pData->pParentFolder->BindToObject(pData->relpidl, NULL, *pIID, &unk));
                break;
            case 1:
                OleCheck(pData->pParentFolder->BindToStorage(pData->relpidl, NULL, *pIID, &unk));
                break;
            case 2:
                OleCheck(pData->pParentFolder->GetUIObjectOf(Handle, 1, &cpidl, *pIID, NULL, &unk));
                break;
            case 3:
                OleCheck(pData->pFolder->CreateViewObject(Handle, *pIID, &unk));
                break;
            default:
                OleCheck(E_UNEXPECTED);
        }
        QueryInterfaces((IUnknown *)unk);
        InterfacesTree->Tag = (int)unk;

        int i;
        for (i=0; i<actionsCount; i++)
            if (IsEqualIID(*pIID, *actions[i].pIID))
            {
                ActionButton->Caption = actions[i].name;
                ActionButton->Tag = (int)actions[i].func;
                ActionButton->Enabled = true;
                break;
            }
        if (i==actionsCount)
            ActionButton->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ActionButtonClick(TObject *Sender)
{
        if (ActionButton->Tag==0 || InterfacesTree->Tag==0)
            return;
        actionfunc func = (actionfunc)ActionButton->Tag;
        IUnknown *unk = (IUnknown *)InterfacesTree->Tag;
        func(unk);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::DetailsListClick(TObject *Sender)
{
        if (DetailsList->Selected == NULL)
                return;
        int col = DetailsList->Selected->Index;
        TTreeItemData *pData = (TTreeItemData *)ShellTree->Selected->Data;
        ColumnDetails->Items->Clear();

        if (pData == NULL || col==-1 || pData->pParentFolder2 == NULL)
                return;

        SHELLDETAILS details;
        if (SUCCEEDED(pData->pParentFolder2->GetDetailsOf(NULL, col, &details)))
        {
            TTreeNode *node = ColumnDetails->Items->AddChild(NULL, "GetDetailsOf");
            ColumnDetails->Items->AddChild(node, AnsiString("Width (chars): ")+details.cxChar);
            ColumnDetails->Items->AddChild(node, AnsiString("Name: ")+StrRetToAnsiString(&details.str, NULL, S_OK));
            ColumnDetails->Items->AddChild(node, AnsiString("Format: ")+AnsiString::IntToHex(details.fmt, 8));
            node->Expand(true);
        }

        SHCOLUMNID colid;
        if (SUCCEEDED(pData->pParentFolder2->MapColumnToSCID(col, &colid)))
        {
            TTreeNode *node = ColumnDetails->Items->AddChild(NULL, "MapColumnToSCID");
            LPWSTR clsid;
            OleCheck(StringFromCLSID(colid.fmtid, &clsid));
            ColumnDetails->Items->AddChild(node, AnsiString("CLSID: ")+clsid);
            CoTaskMemFree(clsid);
            ColumnDetails->Items->AddChild(node, AnsiString("PID: ")+colid.pid);
            node->Expand(true);
        }

        DWORD flags;
        if (SUCCEEDED(pData->pParentFolder2->GetDefaultColumnState(col, &flags)))
        {
            TTreeNode *node = ColumnDetails->Items->AddChild(NULL, "GetDefaultColumnState");
            ColumnDetails->Items->AddChild(node, AnsiString("Flags: ")+AnsiString::IntToHex(flags, 8));
            node->Expand(true);
        }
}
//---------------------------------------------------------------------------

