Click here to Skip to main content
15,892,697 members
Articles / Desktop Programming / ATL

The Complete Idiot's Guide to Writing Shell Extensions - Part II

Rate me:
Please Sign up or sign in to vote.
4.97/5 (46 votes)
15 May 2006 606K   6.3K   179  
A tutorial on writing a shell extension that operates on multiple files at once.
// ProgressDlg.cpp : Implementation of CProgressDlg

#include "stdafx.h"
#include "resource.h"
#include "ProgressDlg.h"

/////////////////////////////////////////////////////////////////////////////
// CProgressDlg

LRESULT CProgressDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LVCOLUMN rCol = { LVCF_FMT | LVCF_TEXT, LVCFMT_LEFT, 0, _T("Filename") };

    // List control initialization.
    m_hwndList = GetDlgItem ( IDC_LIST );

    ListView_InsertColumn ( m_hwndList, 0, &rCol );

    rCol.mask |= LVCF_SUBITEM;
    rCol.pszText = _T("Result");
    rCol.iSubItem = 1;

    ListView_InsertColumn ( m_hwndList, 1, &rCol );

    ListView_SetColumnWidth ( m_hwndList, 0, LVSCW_AUTOSIZE_USEHEADER );
    ListView_SetColumnWidth ( m_hwndList, 1, LVSCW_AUTOSIZE_USEHEADER );

    // Show the window.
    CenterWindow ( m_pCmdInfo->hwnd );
    ShowWindow ( SW_SHOW );

    // Process all the files in the string list passed in to our constructor.
    DoWork();

    // Enable the Close button, and disable the Stop button.
    ::EnableWindow ( GetDlgItem ( IDCANCEL ), TRUE );
    ::EnableWindow ( GetDlgItem ( IDC_STOP ), FALSE );

    return 1;  // Let the system set the focus
}

// work area - this needs to be outside DoWork() so I can use try/finally in that function.
std::basic_string<TCHAR> strMsg;

void CProgressDlg::DoWork()
{
USES_CONVERSION;
HRESULT (STDAPICALLTYPE* pfn)();
HINSTANCE hinst;
TCHAR     szMsg [512];
LPCSTR    pszFnName;
WORD      wCmd;
LVITEM    rItem;
int       nIndex = 0;
bool      bSuccess;

    wCmd = LOWORD(m_pCmdInfo->lpVerb);

    // We only support 2 commands, so check the value passed in lpVerb.
    if ( 0 != wCmd && 1 != wCmd )
        {
        ATLASSERT(0);   // should never get here
        return;
        }

    // Determine which function we'll be calling.  Note that these strings are
    // not enclosed in the _T macro, since GetProcAddress() only takes an
    // ANSI string for the function name.
    pszFnName = wCmd ? "DllUnregisterServer" : "DllRegisterServer";

    for ( string_list::const_iterator it = m_pFileList->begin();
          it != m_pFileList->end(); it++ )
        {
        bSuccess = false;
        hinst    = NULL;
        *szMsg   = '\0';

        // We will print a status message into szMsg, which will eventually
        // be stored in the LPARAM of a listview control item.
        __try
            {
            // Try to load the next file.
            hinst = LoadLibraryEx ( it->c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH );

            // If it failed, construct a friendly error message.
            if ( NULL == hinst )
                {
                void* pvMsgBuf = NULL;
                DWORD dwLastErr = GetLastError();

                FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                                  FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL, dwLastErr, 
                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                (LPTSTR) &pvMsgBuf, 0, NULL );

                wsprintf ( szMsg, _T("LoadLibraryEx failed on this module.\nError 0x%08lX (%s)"),
                           dwLastErr, pvMsgBuf ? pvMsgBuf : _T("No description available") );

                LocalFree ( pvMsgBuf );
                continue;
                }

            // Get the address of the register/unregister function.
            (FARPROC&) pfn = GetProcAddress ( hinst, pszFnName );

            // If it wasn't found, construct an error message.
            if ( NULL == pfn )
                {
                wsprintf ( szMsg, _T("%hs not found in this module."), pszFnName );
                continue;
                }

            // Call the function!
            HRESULT hr = pfn();

            // Construct a message listing the result of the function call.
            if ( SUCCEEDED(hr) )
                {
                bSuccess = true;
                wsprintf ( szMsg, _T("%hs succeeded on %s"), pszFnName, it->c_str() );
                }
            else
                {
                void* pvMsgBuf = NULL;

                FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                                  FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL, hr, 
                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                (LPTSTR) &pvMsgBuf, 0, NULL );

                wsprintf ( szMsg, _T("%hs failed on %s\nError 0x%08lX: %s"),
                           pszFnName, it->c_str(), (DWORD) hr,
                           NULL != pvMsgBuf ? pvMsgBuf : _T("(No description available)") );
                
                LocalFree ( pvMsgBuf );
                }
            }   // end __try
        __finally
            {
            // Fill in the LVITEM struct.  The item text will be the filename,
            // and the LPARAM will point to a copy of the szMsg status message.
            strMsg = szMsg;
            m_lsStatusMessages.push_back ( strMsg );

            ZeroMemory ( &rItem, sizeof(LVITEM) );
            rItem.mask     = LVIF_PARAM | LVIF_TEXT;
            rItem.iItem    = nIndex;
            rItem.pszText  = const_cast<LPTSTR>( it->c_str() );
            rItem.lParam   = (LPARAM)(DWORD_PTR) m_lsStatusMessages.back().c_str();

            ListView_InsertItem ( m_hwndList, &rItem );

            // Set the text in column 2 to "succeeded" or "failed" depending 
            // on the outcome of the function call.
            ListView_SetItemText ( m_hwndList, nIndex++, 1,
                                   bSuccess ? _T("Succeeded") : _T("Failed") );

            if ( NULL != hinst )
                FreeLibrary ( hinst );
            }

        // Process messages in our queue - this is much easier than doing it
        // "the real way" with a worker thread. This is how we detect a click
        // on the Stop button.
        MSG  msg;

        while ( PeekMessage ( &msg, m_hWnd, 0, 0, PM_REMOVE ) )
            {
            TranslateMessage ( &msg );
            DispatchMessage ( &msg );
            }

        // Resize the list columns.
        ListView_SetColumnWidth ( m_hwndList, 0, LVSCW_AUTOSIZE_USEHEADER );
        ListView_SetColumnWidth ( m_hwndList, 1, LVSCW_AUTOSIZE_USEHEADER );
        }   // end for
}


LRESULT CProgressDlg::OnItemchangedList ( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
NMLISTVIEW* pNMLV = (NMLISTVIEW*) pnmh;
LVITEM      rItem = { 0 };

    // This handler is called when something about a list item changes.
    // We first check if there is a selection.  If not, ignore the message.
    if ( -1 == pNMLV->iItem )
        {
        bHandled = false;
        return 0;
        }

    // If the item changing is becoming selected, display its associated
    // status message in the dialog.
    if ( pNMLV->uNewState & LVIS_SELECTED )
        {
        rItem.mask  = LVIF_PARAM;
        rItem.iItem = pNMLV->iItem;

        ListView_GetItem ( m_hwndList, &rItem );
        SetDlgItemText ( IDC_STATUS, (LPCTSTR) rItem.lParam );
        }

    return 0;
}

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


Written By
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.

Comments and Discussions