Click here to Skip to main content
11,644,731 members (64,541 online)
Click here to Skip to main content
Add your own
alternative version

A Utility to Clean Up Compiler Temp Files

, 24 Dec 2002 279.9K 3.2K 94
A shell extension that deletes compiler temp and intermediate files.
dirclean_dll.zip
DirClean.dll
dirclean_src.zip
dirclean
DirCleanps.def
DirCleanps.mk
DirClean.dsp
bitmap1.bmp
DirCleanShlExt.rgs
DirClean.def
DirClean.clw
//////////////////////////////////////////////////////////////////////
// 
// This utility written and copyright by Michael Dunn (mdunn at inreach
// dot com).  You may freely use and redistribute this source code and
// binary as long as this notice is retained.
//
// Contact me if you have any questions, comments, or bug reports. Get
// the latest updates at http://home.inreach.com/mdunn/code/
//
//////////////////////////////////////////////////////////////////////
// 
// Revision history:
//  Feb 28, 2000: Version 1.0: First release.
//
//  June 5, 2000: Version 1.1: Fixed (un)registration so the DLL works
//      on NT/2000.
//
//  Oct 28, 2001: Version 1.1.1: Added 4 default wildcards, *.ncb, *.aps,
//      *.bsc, *.sbr.
//
//////////////////////////////////////////////////////////////////////

// DirCleanShlExt.cpp : Implementation of CDirCleanShlExt
#include "stdafx.h"
#include "DirClean.h"
#include "DirCleanShlExt.h"
#include "OptionsDlg.h"
#include <afxole.h>

/////////////////////////////////////////////////////////////////////////////
// CDirCleanShlExt construction/destruction

CDirCleanShlExt::CDirCleanShlExt()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWinApp* pApp = AfxGetApp();

    m_bNT = 0 == (GetVersion() & 0x80000000);

    m_hbmp = LoadBitmap ( AfxGetResourceHandle(), 
                          MAKEINTRESOURCE(IDB_BITMAP1) );

    // Read in the recycle file option.
    m_bRecycleFiles = pApp->GetProfileInt ( _T("Options"), _T("bRecycle"), 1 );

    // Read in the list of wildcards.
UINT uNumPatterns;
CString sPattern;
TCHAR szValueName [32];

    uNumPatterns = pApp->GetProfileInt ( _T("Options"), _T("uNumPatterns"), 0 );

    if ( 0 == uNumPatterns )
        {
        // No settings were found in the registry, so init the list with a
        // default set of wildcards.

        m_lsPatternsToDelete.AddTail ( _T("*.obj") );
        m_lsPatternsToDelete.AddTail ( _T("*.pch") );
        m_lsPatternsToDelete.AddTail ( _T("*.pdb") );
        m_lsPatternsToDelete.AddTail ( _T("*.ilk") );
        m_lsPatternsToDelete.AddTail ( _T("*.exp") );
        m_lsPatternsToDelete.AddTail ( _T("*.res") );
        m_lsPatternsToDelete.AddTail ( _T("*.trg") );
        m_lsPatternsToDelete.AddTail ( _T("*.idb") );
        m_lsPatternsToDelete.AddTail ( _T("*.tmp") );
        m_lsPatternsToDelete.AddTail ( _T("*.plg") );
        m_lsPatternsToDelete.AddTail ( _T("*.log") );
        m_lsPatternsToDelete.AddTail ( _T("*.ncb") );
        m_lsPatternsToDelete.AddTail ( _T("*.aps") );
        m_lsPatternsToDelete.AddTail ( _T("*.bsc") );
        m_lsPatternsToDelete.AddTail ( _T("*.sbr") );
        }
    else
        {
        // Read in the wildcards.

        for ( UINT uPattern = 0; uPattern < uNumPatterns; uPattern++ )
            {
            wsprintf ( szValueName, _T("Pattern%u"), uPattern );

            sPattern = pApp->GetProfileString ( _T("Options"), szValueName,
                                                _T("*") );

            if ( sPattern != _T("*") )
                {
                m_lsPatternsToDelete.AddTail ( sPattern );
                }
            }
        }
}

CDirCleanShlExt::~CDirCleanShlExt()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWinApp* pApp = AfxGetApp();

    // Save the recycle-files setting.
    pApp->WriteProfileInt ( _T("Options"), _T("bRecycle"), 
                            m_bRecycleFiles ? 1 : 0 );

    // Store the wildcard list.
    pApp->WriteProfileInt ( _T("Options"), _T("uNumPatterns"),
                            m_lsPatternsToDelete.GetCount() );

POSITION pos;
UINT     uPatternNum = 0;
TCHAR    szValueName [32];

    for ( pos = m_lsPatternsToDelete.GetHeadPosition(); NULL != pos; )
        {
        wsprintf ( szValueName, _T("Pattern%u"), uPatternNum++ );

        pApp->WriteProfileString ( _T("Options"), szValueName,
                                   m_lsPatternsToDelete.GetNext ( pos ));
        }
}


/////////////////////////////////////////////////////////////////////////////
// CDirCleanShlExt IShellExtInit methods


//////////////////////////////////////////////////////////////////////////
//
// Function:    Initialize()
//
// Description:
//  Reads in the list of selected folders and stores them for later use.
//
//////////////////////////////////////////////////////////////////////////

HRESULT CDirCleanShlExt::Initialize ( LPCITEMIDLIST pidlFolder,
                                      LPDATAOBJECT  pDO,
                                      HKEY          hProgID )
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

COleDataObject data;
TCHAR          szDir [MAX_PATH];
UINT           uNumFiles;
HGLOBAL        hg;
HDROP          hdrop;

    m_lsDirs.RemoveAll();
    data.Attach ( pDO, FALSE );         // attach to the IDataObject, don't auto-release it

    // Read the list of folders from the data object.  They're stored in HDROP
    // form, so just get the HDROP handle and then use the drag 'n' drop APIs
    // on it.

    hg = data.GetGlobalData ( CF_HDROP );

    if ( NULL != hg )
        {
        hdrop = (HDROP) GlobalLock ( hg );

        uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 );

        for ( UINT uFile = 0; uFile < uNumFiles; uFile++ )
            {
            if ( 0 != DragQueryFile ( hdrop, uFile, szDir, MAX_PATH ))
                {
                m_lsDirs.AddTail ( szDir );
                }
            }

        GlobalUnlock ( hg );

        return S_OK;
        }

    return E_INVALIDARG;
}


/////////////////////////////////////////////////////////////////////////////
// CDirCleanShlExt IContextMenu methods


//////////////////////////////////////////////////////////////////////////
//
// Function:    QueryContextMenu()
//
// Description:
//  Adds our items to the supplied menu.
//
//////////////////////////////////////////////////////////////////////////

HRESULT CDirCleanShlExt::QueryContextMenu ( HMENU hmenu,       UINT uMenuIndex, 
                                            UINT  uidFirstCmd, UINT uidLastCmd,
                                            UINT  uFlags )
{
UINT uCmdID = uidFirstCmd;

    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        {
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
        }

    // Add our clean-up menu item.
    InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++,
                 _T("Clean up temp files") );

    // Set the bitmap for the clean up item.

    if ( NULL != m_hbmp )
        {
        SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_hbmp, NULL );
        }

    uMenuIndex++;

    // Add the options menu item.
    InsertMenu ( hmenu, uMenuIndex++, MF_STRING | MF_BYPOSITION, uCmdID++,
                 _T("DirClean Options") );

    // Return 2 to tell the shell that we added 2 top-level menu items.

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 2 );
}


//////////////////////////////////////////////////////////////////////////
//
// Function:    GetCommandString()
//
// Description:
//  Sets the flyby help string for the Explorer status bar.
//
//////////////////////////////////////////////////////////////////////////

HRESULT CDirCleanShlExt::GetCommandString ( UINT uCmdID,      UINT uFlags, 
                                            UINT* puReserved, LPSTR szName,
                                            UINT  cchMax )
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString sPrompt;

    USES_CONVERSION;

    if ( uFlags & GCS_HELPTEXT )
        {
        switch ( uCmdID )
            {
            case 0:
                if ( m_bRecycleFiles )
                    sPrompt = _T("Recycle all compiler intermediate files (hold Shift to permanently delete)");
                else
                    sPrompt = _T("Delete all compiler intermediate files (hold Shift to recycle)");
            break;

            case 1:
                sPrompt = _T("Change DirClean options");
            break;

            default:
                ASSERT(0);
            break;
            }

        // Copy the string into the supplied buffer.  Note that on NT we
        // always convert the string to Unicode, since the shell needs the
        // string in Unicode.

        if ( m_bNT )
            {
            lstrcpynW ( (LPWSTR) szName, T2CW((LPCTSTR) sPrompt), cchMax );
            }
        else
            {
            lstrcpynA ( szName, T2CA((LPCTSTR) sPrompt), cchMax );
            }
        }
    else if ( uFlags & GCS_VERB )
        {
        CString sVerb = _T("DirClean");

        if ( m_bNT )
            {
            lstrcpynW ( (LPWSTR) szName, T2CW((LPCTSTR) sVerb), cchMax );
            }
        else
            {
            lstrcpynA ( szName, T2CA((LPCTSTR) sVerb), cchMax );
            }
        }

    return S_OK;
}


//////////////////////////////////////////////////////////////////////////
//
// Function:    InvokeCommand()
//
// Description:
//  Carries out the selected command.
//
//////////////////////////////////////////////////////////////////////////

HRESULT CDirCleanShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pInfo )
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWnd* pParentWnd = CWnd::FromHandle ( pInfo->hwnd );

    switch ( LOWORD( pInfo->lpVerb ))
        {
        case 0:                         // clean up files
            {
            SHORT shShiftState = GetKeyState ( VK_SHIFT );
            BOOL  bRecycle     = m_bRecycleFiles;

            if ( 0 != ( shShiftState & 0x8000U ))
                bRecycle = !bRecycle;

            return CleanFiles ( bRecycle, pParentWnd );
            }
        break;

        case 1:                         // show the options dialog
            DoOptionsDlg ( pParentWnd );
            return S_OK;
        break;

        default:
            ASSERT(0);
            return E_INVALIDARG;
        break;
        }
}


/////////////////////////////////////////////////////////////////////////////
// CDirCleanShlExt other functions


//////////////////////////////////////////////////////////////////////////
//
// Function:    CleanFiles()
//
// Description:
//  Sets up the list of files to delete, and deletes 'em.
//
// Input:
//  bRecycle: [in] TRUE if the files should be put in the Recycle Bin,
//            FALSE if they should be immediately deleted.
//  pParentWnd: [in] Window to be used as the parent window for the
//              progress UI.
//
// Returns:
//  S_OK on success, E_FAIL on failure.
//
//////////////////////////////////////////////////////////////////////////

HRESULT CDirCleanShlExt::CleanFiles ( BOOL bRecycle, CWnd* pParentWnd )
{
CShellFileOp op;
CString      sNextSpec;
POSITION     pos;
BOOL         bFileOpStarted;
FILEOP_FLAGS flags = FOF_FILESONLY | FOF_NOCONFIRMATION;

    // For each directory that was selected...

    pos = m_lsDirs.GetHeadPosition();
    
    while ( NULL != pos )
        {
        sNextSpec = m_lsDirs.GetNext ( pos );

        if ( sNextSpec.Right(1) != _T("\\") )
            sNextSpec += '\\';

        m_bFoundFilesToDelete = FALSE;

        // ...search it and its subdirs for files to delete.

        AddDirsToFileOp ( &op, sNextSpec );
        }

    // If we don't have any files to delete, we can bail.

    if ( !m_bFoundFilesToDelete )
        return S_OK;

    if ( bRecycle )
        flags |= FOF_ALLOWUNDO;

    op.SetOperationFlags ( FO_DELETE, pParentWnd, flags );

    // Kick off the delete operation!

    if ( op.Go ( &bFileOpStarted ))
        return S_OK;
    else
        return E_FAIL;
}


//////////////////////////////////////////////////////////////////////////
//
// Function:    AddDirsToFileOp()
//
// Description:
//  Searches the specified directory for files to delete, and adds the
//  dir to the CShellFileOp object if any files are found.
//
// Input:
//  pOp: [in] Pointer to the CShellFileOp object to use.
//  sDir: [in] The directory to search.
//
// Returns:
//  Nothing.
//
//////////////////////////////////////////////////////////////////////////

void CDirCleanShlExt::AddDirsToFileOp ( CShellFileOp* pOp, CString sDir )
{
CString   sNextPattern;
CString   sNextSearchSpec;
CFileFind find;
BOOL      bWorking;
POSITION  pos;

    ASSERT ( sDir.Right(1) == _T("\\") );

    // Search the current dir for files matching each pattern.

    for ( pos = m_lsPatternsToDelete.GetHeadPosition(); NULL != pos; )
        {
        sNextPattern = m_lsPatternsToDelete.GetNext ( pos );

        sNextSearchSpec = sDir + sNextPattern;

        if ( find.FindFile ( sNextSearchSpec ))
            {
            m_bFoundFilesToDelete = TRUE;
            pOp->AddSourceFile ( sNextSearchSpec );
            find.Close();
            }
        }

    // Now recurse into subdirectories and continue searching.

    if ( find.FindFile ( sDir + _T("*.*") ))
        {
        do
            {
            bWorking = find.FindNextFile();

            if ( !find.IsDirectory() || find.IsDots() )
                continue;

            AddDirsToFileOp ( pOp, find.GetFilePath() + '\\' );
            } while ( bWorking );
        }
}


//////////////////////////////////////////////////////////////////////////
//
// Function:    DoOptionsDlg()
//
// Description:
//  Shows our options dialog.
//
// Input:
//  pParentWnd: [in] Parent window for the dialog.
//
// Returns:
//  Nothing.
//
//////////////////////////////////////////////////////////////////////////

void CDirCleanShlExt::DoOptionsDlg ( CWnd* pParentWnd )
{
COptionsDlg dlg ( pParentWnd, m_lsPatternsToDelete );

    dlg.m_bRecycle = m_bRecycleFiles;

    if ( IDOK == dlg.DoModal() )
        {
        m_bRecycleFiles = dlg.m_bRecycle;

        m_lsPatternsToDelete.RemoveAll();
        m_lsPatternsToDelete.AddTail ( &dlg.m_lsPatterns );
        }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Michael Dunn
Software Developer (Senior) VMware
United States United States
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.

Mike was a VC MVP from 2005 to 2009.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150731.1 | Last Updated 25 Dec 2002
Article Copyright 2000 by Michael Dunn
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid