Figure 1

Figure 1

Windows Shell Extensions


Type
Apply to
Version
Interface Involved
Description
Context Menu
File class and shell's object
Windows 95+
IContextMenu, IContextMenu2, or IContextMenu3
Allows you to add new items to a shell object's context menu.
Right drag and drop
File class and shell's object
Windows 95+
IContextMenu, IContextMenu2, or IContextMenu3
Allows you to add new items to the context menu that appears after you right drag and drop files.
Drawing shell Icons
File class and shell's object
Windows 95+
IExtractIcon
Lets you decide at runtime which icon should be displayed for a given file within a file class.
Property Sheet
File class and shell's object
Windows 95+
IShellPropSheetExt
Lets you insert additional property sheet pages to the file class Properties dialog. It also works for Control Panel applets.
Left drag and drop
File class and shell's object
Windows 95+
IDropTarget
Lets you decide what to do when an object is being dropped (using the left mouse button) onto another one within the shell.
Clipboard
File class and shell's object
Windows 95+
IDataObject
Lets you define how an object is to be copied to and extracted from the clipboard.
File Hook

Windows 95+
ICopyHook
Lets you control any file operation that goes through the shell. While you can permit or deny them, you aren't informed about success or failure.
Program Execution
Explorer
Desktop Update
IShellExecuteHook
Lets you hook any program's execution that passes through the shell.
Infotip
File class and shell's object
Desktop Update
IQueryInfo
Lets you display a short text message when the mouse hovers over documents of a certain file type.
Column
Folders
Windows 2000
IColumnProvider
Lets you add a new column to the Details view of Explorer.
Icon Overlay
Explorer
Windows 2000
IShellIconOverlay
Lets you define custom images to be used as icon overlays.
Search
Explorer
Windows 2000
IContextMenu
Lets you add a new entry on the Start menu's Search menu.
Cleanup
Cleanup Manager
Windows 2000
IEmptyVolumeCache2
Lets you add a new entry to the Cleanup Manager to recover disk space.

Figure 3

Implementing the BMP Infotip

// IPersistFileImpl.h


#include <AtlCom.h>

class ATL_NO_VTABLE IPersistFileImpl : public IPersistFile{
public:
    TCHAR m_szFile[MAX_PATH];

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0;
    _ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistFileImpl)

    // IPersistFile
    STDMETHOD(Load)(LPCOLESTR wszFile, DWORD dwMode){
        USES_CONVERSION;
        _tcscpy(m_szFile, OLE2T((WCHAR*)wszFile)); 
        return S_OK;    
    };

    STDMETHOD(GetClassID)(LPCLSID){ return E_NOTIMPL; }

    STDMETHOD(IsDirty)(VOID){ return E_NOTIMPL; }

    STDMETHOD(Save)(LPCOLESTR, BOOL){ return E_NOTIMPL; }

    STDMETHOD(SaveCompleted)(LPCOLESTR){ return E_NOTIMPL; }

    STDMETHOD(GetCurFile)(LPOLESTR FAR*){ return E_NOTIMPL; }
};

// IQueryInfoImpl.h


#include <AtlCom.h>
#include <ShlObj.h>

class ATL_NO_VTABLE IQueryInfoImpl : public IQueryInfo{
public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0;
    _ATL_DEBUG_ADDREF_RELEASE_IMPL(IQueryInfoImpl)

    // IQueryInfo::GetInfoTip
    STDMETHOD(GetInfoTip)(DWORD dwFlags, LPWSTR *ppwszTip){
        wcscpy(*ppwszTip, L"InfoTip");
        return S_OK;
    }

    // IQueryInfo::GetInfoFlags
    STDMETHOD(GetInfoFlags)(LPDWORD pdwFlags){ 
        *pdwFlags = 0;
        return E_NOTIMPL; 
    }
};

// BmpTip.h : Declaration of the CBmpTip coclass


#ifndef __BMPTIP_H_
#define __BMPTIP_H_

#include "resource.h"              // main symbols
#include "comdef.h"                // GUIDs    
#include "IPersistFileImpl.h"      // IPersistFile
#include "IQueryInfoImpl.h"        // IQueryInfo

// CBmpTip
class ATL_NO_VTABLE CBmpTip : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBmpTip, &CLSID_BmpTip>,
    public IQueryInfoImpl,
    public IPersistFileImpl,
    public IDispatchImpl<IBmpTip, &IID_IBmpTip, 
                         &LIBID_BMPEXTLib>{
public:
    CBmpTip(){
        HRESULT hr;
        hr = SHGetMalloc(&m_pAlloc);
        if (FAILED(hr)) m_pAlloc = NULL;
    }
    ~CBmpTip(){
        m_pAlloc->Release();
    }
DECLARE_REGISTRY_RESOURCEID(IDR_BMPTIP)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CBmpTip)
    COM_INTERFACE_ENTRY(IBmpTip)
    COM_INTERFACE_ENTRY(IQueryInfo)
    COM_INTERFACE_ENTRY(IPersistFile)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IQueryInfo
public:
    STDMETHOD(GetInfoTip)(DWORD, LPWSTR*);

private:
    STDMETHOD(GetBitmapInfo)(CComBSTR*);
    LPMALLOC m_pAlloc;
};

#endif //__BMPTIP_H_

Figure 7

A Column Handler Extension

// IColumnProviderImpl.h


#include <AtlCom.h>
#include <ShlObj.h>

class ATL_NO_VTABLE IColumnProviderImpl : public IColumnProvider 
{
protected:
    TCHAR m_szFolder[MAX_PATH];
public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0;
    _ATL_DEBUG_ADDREF_RELEASE_IMPL(IColumnProviderImpl)

    // IColumnProvider 
    // IColumnProvider::Initialize
    STDMETHOD(Initialize)(LPCSHCOLUMNINIT psci){
        USES_CONVERSION;
        _tcscpy(m_szFolder, OLE2T((WCHAR*)psci->wszFolder)); 
        return S_OK;
    }
    // IColumnProvider::GetColumnInfo
    STDMETHOD(GetColumnInfo)(DWORD dwIndex, 
                             SHCOLUMNINFO *psci){ 
        return S_FALSE; 
    }
    // IColumnProvider::GetItemData
    STDMETHOD(GetItemData)(LPCSHCOLUMNID pscid, 
                           LPCSHCOLUMNDATA pscd, 
                           VARIANT *pvarData){ 
        return S_FALSE; 
    }
};

// BmpColInfo.h : Declaration of the CBmpColInfo


#ifndef __BMPCOLINFO_H_
#define __BMPCOLINFO_H_

#include "resource.h"            // main symbols
#include <shlguid.h>             // GUID of IColumnProvider
#include "IColumnProviderImpl.h" // IColumnProvider base impl

const UINT BMPCH_DEFWIDTH = 16;// column default width in chars 
const UINT BMPCH_MAXSIZE = 80;  // max text size 
const DWORD BMPCH_NUMOFCOLS = 1;// number of columns handled 
                                // here

// CBmpColInfo
class ATL_NO_VTABLE CBmpColInfo : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBmpColInfo, &CLSID_BmpColInfo>,
    public IColumnProviderImpl,
    public IDispatchImpl<IBmpColInfo, &IID_IBmpColInfo, 
                         &LIBID_BMPCOLLib>{
public:
    CBmpColInfo(){}

DECLARE_REGISTRY_RESOURCEID(IDR_BMPCOLINFO)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CBmpColInfo)
    COM_INTERFACE_ENTRY(IBmpColInfo)
    COM_INTERFACE_ENTRY_IID(IID_IColumnProvider, CBmpColInfo)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IColumnProvider
public:
    STDMETHOD(GetColumnInfo)(DWORD, SHCOLUMNINFO *);
    STDMETHOD(GetItemData)(LPCSHCOLUMNID, LPCSHCOLUMNDATA, 
                           VARIANT *);
private:
    STDMETHOD(GetBitmapInfo)(LPCWSTR, LPTSTR);
};

#endif //__BMPCOLINFO_H_

// BmpColInfo.cpp : Implementation of CBmpColInfo


#include "stdafx.h"
#include "BmpCol.h"
#include "BmpColInfo.h"

HRESULT CBmpColInfo::GetColumnInfo(DWORD dwIndex, 
                                   SHCOLUMNINFO *psci){ 
    // Since this extension might provide more columns, the
    // index is used to enumerate them. The shell calls 
    // this method repeatedly with progressive indexes, until
    // you return S_FALSE. Return S_FALSE only when you've
    // finished with all your columns. dwIndex is a 0-based 
    // index. Notice that without this checking you enter
    // an infinite loop!
    if (dwIndex >= BMPCH_NUMOFCOLS)
        return S_FALSE;

    // Now fills out the SHCOLUMNINFO structure to let the 
    // shell know about general-purpose features of the column

    // Identifies the column with a FMTID/PID pair. You'd
    // define one of these pairs for each column you're
    // adding here.
    psci->scid.fmtid = *_Module.pguidVer; // use object's CLSID
    psci->scid.pid = 1;

    // Sets type, alignment and default width
    psci->vt = VT_LPSTR;              // data is LPSTR
    psci->fmt = LVCFMT_LEFT;          // left alignment
    psci->cChars = BMPCH_DEFWIDTH;    // default width in chars
    
    // Other flags
    psci->csFlags = SHCOLSTATE_TYPE_STR;
        
    // Caption and description
    wcsncpy(psci->wszTitle, L"Dimensions", MAX_COLUMN_NAME_LEN);
    wcsncpy(psci->wszDescription, 
            L"Provides dimensions and colors for BMPs", 
            MAX_COLUMN_DESC_LEN);

    return S_OK; 
}

// IColumnProvider::GetItemData
HRESULT CBmpColInfo::GetItemData(LPCSHCOLUMNID pscid,
                                 LPCSHCOLUMNDATA pscd, 
                                 VARIANT *pvarData){ 
    USES_CONVERSION;
    // The shell calls this method for each file displayed
    // in the folder where this column has been selected. 
    // The SHCOLUMNID structure identifies the column
    // unequivocally just in case you're handling more than one.

    // In this case, I'm managing just one column so I'll
    // ignore the SHCOLUMNID param.

    // Information about the specific file is contained in the 
    // SHCOLUMNDATA structure. I'm interested only in .BMP.
    if (wcsicmp(pscd->pwszExt, L".bmp")) return S_FALSE;

    // Reads dimensions and palette size from the BMP file
    TCHAR szBuf[BMPCH_MAXSIZE];
    GetBitmapInfo(pscd->wszFile, szBuf);

    // The return value (a string in this case) must be 
    // packed as a Variant.
    CComVariant cv(szBuf);
    cv.Detach(pvarData); 

    return S_OK; 
}

HRESULT CBmpColInfo::GetBitmapInfo(LPCWSTR wszFile, LPTSTR p){
    USES_CONVERSION;
    BITMAPFILEHEADER bf;
    BITMAPINFOHEADER bi;
    HFILE h;

    // Reads the file header
    h = _lopen(OLE2T(wszFile), OF_READ);
    if (h==HFILE_ERROR) return S_OK;

    _lread(h, (LPBITMAPFILEHEADER)&bf, sizeof(BITMAPFILEHEADER));
    _lread(h, (LPBITMAPINFOHEADER)&bi, sizeof(BITMAPINFOHEADER));
    _lclose(h);

    // Formats the string 
    wsprintf(p, _T("%d x %d x %d"), bi.biWidth, bi.biHeight, 
             bi.biBitCount);  

    return S_OK;
}

Figure 11

Cleanup Extension

// IEmptyVolumeCache2Impl.h


#include <AtlCom.h>
#include <emptyvc.h>

class ATL_NO_VTABLE IEmptyVolumeCache2Impl : public IEmptyVolumeCache2{

public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 
        0;
    _ATL_DEBUG_ADDREF_RELEASE_IMPL(IEmptyVolumeCache2Impl)

    // IEmptyVolumeCache::Initialize
    STDMETHOD(Initialize)(HKEY hkRegKey, LPCWSTR pcwszVolume, 
                          LPWSTR *ppwszDisplayName, 
                          LPWSTR *ppwszDescription, 
                          DWORD *pdwFlags){
        // Allows to initialize a Windows 98 handler
        MessageBox(0, _T("Initialize"), 0, 0);
        return S_OK;
    }

    // IEmptyVolumeCache::Deactivate 
    STDMETHOD(Deactivate)(DWORD *pdwFlags){ 
        // Called when the handler is going to be unloaded
        MessageBox(0, _T("Deactivate"), 0, 0);
        return S_OK; 
    }

    // IEmptyVolumeCache::GetSpaceUsed
    STDMETHOD(GetSpaceUsed)(DWORDLONG *pdwSpaceUsed, 
                            IEmptyVolumeCacheCallBack *picb){ 
        // Returns the amount of space the handler can free
        MessageBox(0, _T("GetSpaceUsed"), 0, 0);
        return S_OK;  
    }

    // IEmptyVolumeCache::Purge
    STDMETHOD(Purge)(DWORDLONG dwSpaceToFree, 
                     IEmptyVolumeCacheCallBack *picb){ 
        // Actually deletes the files
        MessageBox(0, _T("Purge"), 0, 0);
        return S_OK; 
    }

    // IEmptyVolumeCache::ShowProperties
    STDMETHOD(ShowProperties)(HWND hwnd){ 
        // Provides a UI 
        MessageBox(0, _T("ShowProperties"), 0, 0);
        return S_OK; 
    }

    // IEmptyVolumeCache2::InitializeEx
    STDMETHOD(InitializeEx)(HKEY hkRegKey, LPCWSTR pcwszVolume, 
                          LPCWSTR pcwszKeyName, 
                          LPWSTR *ppwszDisplayName, 
                          LPWSTR *ppwszDescription, 
                          LPWSTR *ppwszBtnText, 
                          DWORD *pdwFlags){
        // Initializes the handler under Windows 2000
        MessageBox(0, _T("InitializeEx"), 0, 0);
        return S_OK;
    }
};

// CleanSomething.h : Declaration of the CCleanSomething


#ifndef __CLEANSOMETHING_H_
#define __CLEANSOMETHING_H_

#include "resource.h"               // main symbols
#include "IEmptyVolumeCache2Impl.h" // IEmptyVolumeCache2

/////////////////////////////////////////////////////////////////
// CCleanSomething
class ATL_NO_VTABLE CCleanSomething : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CCleanSomething, &CLSID_CleanSomething>,
    public IEmptyVolumeCache2Impl,
    public IDispatchImpl<ICleanSomething, &IID_ICleanSomething, 
                         &LIBID_DCHDEMOLib>{
public:
    CCleanSomething(){}

DECLARE_REGISTRY_RESOURCEID(IDR_CLEANSOMETHING)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CCleanSomething)
    COM_INTERFACE_ENTRY(IEmptyVolumeCache)
    COM_INTERFACE_ENTRY(IEmptyVolumeCache2)
    COM_INTERFACE_ENTRY(ICleanSomething)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IEmptyVolumeCache2
public:
    STDMETHOD(InitializeEx)(HKEY hkRegKey, LPCWSTR pcwszVolume, 
                  LPCWSTR pcwszKeyName, 
                  LPWSTR *ppwszDisplayName, 
                  LPWSTR *ppwszDescription, 
                  LPWSTR *ppwszBtnText, 
                  DWORD *pdwFlags);

    STDMETHOD(GetSpaceUsed)(DWORDLONG *pdwSpaceUsed, 
        IEmptyVolumeCacheCallBack *picb);
};

#endif //__CLEANSOMETHING_H_

// CleanSomething.cpp : Implementation of CCleanSomething


#include "stdafx.h"
#include "DCHDemo.h"
#include "CleanSomething.h"

// CCleanSomething

HRESULT CCleanSomething::InitializeEx(HKEY hkRegKey,
        LPCWSTR pcwszVolume, LPCWSTR pcwszKeyName, 
        LPWSTR *ppwszDisplayName, LPWSTR *ppwszDescription, 
        LPWSTR *ppwszBtnText, DWORD *pdwFlags){
    USES_CONVERSION;
    MessageBoxW(0, pcwszVolume, 0, 0);
    *ppwszDisplayName = (LPWSTR)CoTaskMemAlloc(100);
    *ppwszDescription = (LPWSTR)CoTaskMemAlloc(100);
    *ppwszBtnText = (LPWSTR)CoTaskMemAlloc(100);
    lstrcpyW(*ppwszDisplayName, T2OLE("DCHDemo"));
    lstrcpyW(*ppwszDescription, T2OLE("Clean Something"));
    lstrcpyW(*ppwszBtnText, T2OLE("Click Me!"));
    *pdwFlags |= EVCF_HASSETTINGS;
    return S_OK;
}

HRESULT CCleanSomething::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, 
    IEmptyVolumeCacheCallBack *picb){
    *pdwSpaceUsed = 1024000;
    return S_OK;
}

Figure 16

Invoking the Command Prompt



TCHAR                szCommand[MAX_PATH] = TEXT("");
STARTUPINFO          sui;
PROCESS_INFORMATION  pi;
OSVERSIONINFO        osi;

GetEnvironmentVariable(TEXT("ComSpec"), szCommand, 
    ARRAYSIZE(szCommand));

if(!*szCommand){
   osi.dwOSVersionInfoSize = sizeof(osi);
   GetVersionEx(&osi);
   if(VER_PLATFORM_WIN32_NT == osi.dwPlatformId){
      lstrcpy(szCommand, TEXT("cmd.exe"));
   } else{
      lstrcpy(szCommand, TEXT("command.com"));
   }
}

//set up the STARTUPINFO structure
ZeroMemory(&sui, sizeof(sui));
sui.cb = sizeof(sui);

//create the process
CreateProcess( NULL, szCommand, NULL, NULL,
               FALSE, NORMAL_PRIORITY_CLASS,
               NULL, szInitialDirectory,
               &sui, &pi);