www.pudn.com > ShellExtGuide4_demo.zip > HardLinkShlExt.cpp


// HardLinkShlExt.cpp : Implementation of CHardLinkShlExt 
#include "stdafx.h" 
#include "HardLink.h" 
#include "HardLinkShlExt.h" 
#include  
#include  
 
///////////////////////////////////////////////////////////////////////////// 
// CHardLinkShlExt IShellExtInit methods 
 
HRESULT CHardLinkShlExt::Initialize ( LPCITEMIDLIST pidlFolder, 
                                      LPDATAOBJECT  pDO, 
                                      HKEY          hProgID ) 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
COleDataObject dataobj; 
HGLOBAL        hglobal; 
HDROP          hdrop; 
TCHAR          szRoot [MAX_PATH]; 
TCHAR          szFileSystemName [256]; 
TCHAR          szFile [MAX_PATH]; 
UINT           uFile, uNumFiles; 
 
    m_bitmap.LoadBitmap ( IDB_LINKBITMAP ); 
 
    dataobj.Attach ( pDO, FALSE );      // FALSE = don't release IDataObject interface when destroyed 
 
    // Get the name of the directory where the files were dropped. 
 
    if ( !SHGetPathFromIDList ( pidlFolder, m_szFolderDroppedIn )) 
        { 
        return E_FAIL; 
        } 
 
    // Get the root of the target folder, and see if it's on an NTFS volume. 
 
    lstrcpy ( szRoot, m_szFolderDroppedIn ); 
    PathStripToRoot ( szRoot ); 
 
    if ( !GetVolumeInformation ( szRoot, NULL, 0, NULL, NULL, NULL,  
                                 szFileSystemName, 256 )) 
        { 
        // Couldn't determine file system type. 
        return E_FAIL; 
        } 
 
#ifndef NOT_ON_WIN2K 
    if ( 0 != lstrcmpi ( szFileSystemName, _T("ntfs") )) 
        { 
        // The file system isn't NTFS, so it doesn't support hard links. 
        return E_FAIL; 
        } 
#endif 
 
    // Make sure the target dir ends in a backslash, to make later processing easier. 
 
    PathAddBackslash ( m_szFolderDroppedIn ); 
 
    // Get a list of all the objects that were dropped. 
 
    hglobal = dataobj.GetGlobalData ( CF_HDROP ); 
 
    if ( NULL == hglobal ) 
        return E_INVALIDARG; 
 
    hdrop = (HDROP) GlobalLock ( hglobal ); 
 
    if ( NULL == hdrop ) 
        return E_INVALIDARG; 
 
    // Determine how many files were dropped. 
 
    uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 ); 
 
    // Check each dropped item.  If there are any directories present, 
    // we have to bail out, since a directory can't be linked to. 
    // We also have to check that the dropped items reside on the same 
    // volume as the directory where they were dropped - hard links can only 
    // be made on the same volume. 
 
    for ( uFile = 0; uFile < uNumFiles; uFile++ ) 
        { 
        if ( DragQueryFile ( hdrop, uFile, szFile, MAX_PATH )) 
            { 
            if ( PathIsDirectory ( szFile )) 
                { 
                // We found a directory!  Bail out. 
                m_lsDroppedFiles.RemoveAll(); 
                break; 
                } 
 
            if ( !PathIsSameRoot ( szFile, m_szFolderDroppedIn )) 
                { 
                // Dropped files came from a different volume - bail out. 
                m_lsDroppedFiles.RemoveAll(); 
                break; 
                } 
 
            // Add the file to our list of dropped files. 
            m_lsDroppedFiles.AddTail ( szFile ); 
            } 
        }   // end for 
 
    GlobalUnlock ( hglobal ); 
 
    return ( m_lsDroppedFiles.GetCount() > 0 ) ? S_OK : E_FAIL; 
} 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CHardLinkShlExt IContextMenu methods 
 
HRESULT CHardLinkShlExt::QueryContextMenu ( HMENU hmenu,       UINT uMenuIndex, 
                                            UINT  uidFirstCmd, UINT uidLastCmd, 
                                            UINT  uFlags ) 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
    // 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 the hard link menu item. 
 
    InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uidFirstCmd, 
                 _T("Create &Hard Link(s) Here") ); 
 
    if ( NULL != m_bitmap.GetSafeHandle() ) 
        { 
        SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_bitmap, NULL ); 
        } 
 
    // Return 1 to tell the shell that we added 1 top-level menu item. 
 
    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 ); 
} 
 
 
HRESULT CHardLinkShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pInfo ) 
{ 
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
TCHAR    szNewFilename [MAX_PATH+32];   // +32 in case a path overruns MAX_PATH (we'll handle this case and error out) 
CString  sSrcFile; 
TCHAR    szSrcFileTitle [MAX_PATH]; 
CString  sMessage; 
UINT     uLinkNum; 
POSITION pos; 
 
    // Double-check that we're getting called for our own menu item - lpVerb 
    // must be 0. 
 
    if ( 0 != pInfo->lpVerb ) 
        { 
        return E_INVALIDARG; 
        } 
 
    // Start enumerating the files we're going to make links for. 
 
    pos = m_lsDroppedFiles.GetHeadPosition(); 
 
    // Sanity check - the list must not be empty. 
 
    ASSERT ( NULL != pos ); 
 
 
    while ( NULL != pos ) 
        { 
        // Get the next source filename. 
     
        sSrcFile = m_lsDroppedFiles.GetNext ( pos ); 
 
        // Remove the path - this reduces "C:\xyz\foo\stuff.exe" to "stuff.exe" 
 
        lstrcpy ( szSrcFileTitle, sSrcFile ); 
        PathStripPath ( szSrcFileTitle ); 
 
        // Make the filename for the hard link - we'll first try  
        // "Hard link to stuff.exe" 
 
        wsprintf ( szNewFilename, _T("%sHard link to %s"), m_szFolderDroppedIn, 
                   szSrcFileTitle ); 
 
        // Check if that filename already exists.  If so, we'll try 
        // "Hard link (2) to stuff.exe", incrementing the number up to an 
        // arbitrary limit of 99. 
 
        for ( uLinkNum = 2; 
              PathFileExists ( szNewFilename )  &&  uLinkNum < 100;  
              uLinkNum++ ) 
            { 
            // Try another filename for the link. 
 
            wsprintf ( szNewFilename, _T("%sHard link (%u) to %s"), 
                       m_szFolderDroppedIn, uLinkNum, szSrcFileTitle ); 
 
            // If the resulting filename is longer than MAX_PATH, show an  
            // error message. 
 
            if ( lstrlen ( szNewFilename ) >= MAX_PATH ) 
                { 
                sMessage.Format ( _T("Failed to make a link to %s. The resulting filename would be too long.\n\nDo you want to continue making links?"), 
                                  (LPCTSTR) sSrcFile ); 
 
                if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), 
                                          MB_ICONQUESTION | MB_YESNO )) 
                    { 
                    break; 
                    } 
                else 
                    { 
                    continue; 
                    } 
                } 
            } 
 
        // If we hit our limit of 100 links, tell the user what's up. 
 
        if ( 100 == uLinkNum ) 
            { 
            sMessage.Format ( _T("Failed to make a link to %s. Reached limit of 100 links in a single directory.\n\nDo you want to continue making links?"), 
                              (LPCTSTR) sSrcFile ); 
 
            if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), 
                                      MB_ICONQUESTION | MB_YESNO )) 
                { 
                break; 
                } 
            else 
                { 
                continue; 
                } 
            } 
 
        // Make the link! 
 
#ifdef NOT_ON_WIN2K 
        MessageBox(pInfo->hwnd, szNewFilename, "The new link would be:", MB_OK); 
#else 
        if ( !CreateHardLink ( szNewFilename, sSrcFile, NULL )) 
            { 
            LPVOID pvMsgBuf = NULL; 
            DWORD  dwLastErr = GetLastError(); 
            int    nChoice; 
 
            FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |  
                              FORMAT_MESSAGE_IGNORE_INSERTS, 
                            NULL, dwLastErr, 
                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                            (LPTSTR) &pvMsgBuf, 0, NULL ); 
 
            sMessage.Format ( _T("Failed to make a link to %s.\n\nError 0x%08lX - %s\nDo you want to continue making links?"), 
                              (LPCTSTR) sSrcFile, dwLastErr, 
                              pvMsgBuf ? pvMsgBuf : _T("(No description available)") ); 
 
            nChoice = MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), 
                                   MB_ICONQUESTION | MB_YESNO ); 
 
            if ( NULL != pvMsgBuf ) 
                { 
                LocalFree ( pvMsgBuf ); 
                } 
 
            if ( IDNO == nChoice ) 
                { 
                break; 
                } 
            } 
#endif 
        }   // end while (pos != NULL) 
 
    return S_OK; 
}