Click here to Skip to main content
15,892,746 members
Articles / Desktop Programming / Win32

Shell Extension with Keyboard Hook

Rate me:
Please Sign up or sign in to vote.
3.83/5 (5 votes)
31 Aug 2008CPOL9 min read 51.2K   882   36  
Shell Extension with Keyboard Hook
// ShellExtension.cpp : Implementation of CShellExtension

#include "stdafx.h"
#include "ShellExtension.h"


// CShellExtension
static HHOOK g_hHook;

CShellExtension::~CShellExtension()
{
	if (m_Subclassed)
	{
		SubclassExplorer(false);
		m_Subclassed = false;
	}
}

STDMETHODIMP CShellExtension::SetSite(IUnknown *pUnkSite)
{
	if (!m_Subclassed)
	{
		HRESULT hr = SubclassExplorer(true);
		if (SUCCEEDED(hr))
		{	
			m_Subclassed = true;
			return S_OK;
		}
		else
		{
			return E_FAIL;
		}
	}
	return S_OK;
}

CShellExtension::CShellExtension()
{
	m_Subclassed = false;
}

STDMETHODIMP CShellExtension::SubclassExplorer(bool bSubclass)
{
	if (bSubclass && !m_Subclassed)
	{
		g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
	}

	if (!bSubclass && m_Subclassed)
	{
		UnhookWindowsHookEx(g_hHook);
	}

	return S_OK;
}


LRESULT CALLBACK CShellExtension::KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode < 0)
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);

	if ((lParam & 0x80000000) || (lParam & 0x40000000))
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);

	if (wParam == NEWFOLDERKEY)
	{
		MoveSelectedFiles((GetAsyncKeyState(VK_CONTROL) & 0x80000000) == 0x80000000);
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

BOOL CALLBACK CShellExtension::WndEnumProc(HWND hwnd, LPARAM lParam)
{
	TCHAR szClassName[MAX_PATH] = {0};

	GetClassName(hwnd, szClassName, MAX_PATH);

	if (!lstrcmpi(szClassName, __TEXT("CabinetWClass")))
	{
		HWND* phWnd = reinterpret_cast<HWND*>(lParam);
		*phWnd = hwnd;
		return FALSE;
	}

	return TRUE;
}

BOOL CShellExtension::FindIShellView(HWND hwnd, IShellView** psv)
{
  BOOL fFound = FALSE;
	 IShellWindows *psw;
 if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
                                IID_IShellWindows, (void**)&psw))) {
  VARIANT v;
  V_VT(&v) = VT_I4;
  IDispatch  *pdisp;
  for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
       V_I4(&v)++) {
   IWebBrowserApp *pwba;
   if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
     HWND hwndWBA;
     if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
       hwndWBA == hwnd) {
       IServiceProvider *psp;
       if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
         IShellBrowser *psb;
         if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
                              IID_IShellBrowser, (void**)&psb))) {
           if (SUCCEEDED(psb->QueryActiveShellView(psv))) {
					fFound = TRUE;
           }
           psb->Release();
         }
         psp->Release();
       }
     }
     pwba->Release();
   }
    pdisp->Release();
  }
  psw->Release();
 }

 return fFound;
}

void CShellExtension::MoveSelectedFiles(bool fCopy)
{
	HWND m_hwndExplorer;

	HWND g_hwndExplorer;

	PWSTR ppszDocumentsPath[MAX_PATH];
	PWSTR ppszMusicPath[MAX_PATH];
	PWSTR ppszPicturesPath[MAX_PATH];
	PWSTR ppszVideosPath[MAX_PATH];


	EnumThreadWindows(GetCurrentThreadId(), WndEnumProc, reinterpret_cast<LPARAM>(&m_hwndExplorer));

	if (!IsWindow(m_hwndExplorer))
	{
		return;
	}
	else
		g_hwndExplorer = m_hwndExplorer;

	SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, ppszDocumentsPath);
	SHGetKnownFolderPath(FOLDERID_Music, 0, NULL, ppszMusicPath);
	SHGetKnownFolderPath(FOLDERID_Pictures, 0, NULL, ppszPicturesPath);
	SHGetKnownFolderPath(FOLDERID_Videos, 0, NULL, ppszVideosPath);

	IShellView* psv;
	if (FindIShellView(g_hwndExplorer, &psv))
	{
		CComPtr<IDataObject> spDataObject;
		if (SUCCEEDED(psv->GetItemObject(SVGIO_SELECTION, 
              IID_PPV_ARGS(&spDataObject))))
		{
			FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT,
							  -1, TYMED_HGLOBAL };
			STGMEDIUM stg;
			stg.tymed =  TYMED_HGLOBAL;

			if (SUCCEEDED(spDataObject->GetData(&fmt, &stg)))
			{
				HDROP hDrop = (HDROP) GlobalLock ( stg.hGlobal );

				UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
				HRESULT hr = S_OK;

		        IFileOperation *pfo;

		        hr = CoCreateInstance(CLSID_FileOperation,
                              NULL,
                              CLSCTX_ALL,
                              IID_PPV_ARGS(&pfo));

				pfo->SetOperationFlags(FOFX_SHOWELEVATIONPROMPT | FOF_ALLOWUNDO);

				for(UINT i = 0; i < uNumFiles; i++)
				{
					TCHAR szPath[MAX_PATH];
					szPath[0] = 0;
					DragQueryFile(hDrop, i, szPath, MAX_PATH);
			        
					if (szPath[0] != 0)
						if (!(PathIsDirectory(szPath) || PathIsRoot(szPath)))
						{
							if (!PathMatchSpecEx(szPath, GRAPHICFILES, PMSF_MULTIPLE))
								AddFileToArray(szPath, *ppszPicturesPath, pfo, fCopy);
							else if (!PathMatchSpecEx(szPath, VIDEOFILES, PMSF_MULTIPLE))
								AddFileToArray(szPath, *ppszVideosPath, pfo, fCopy);
							else if (!PathMatchSpecEx(szPath, MUSICFILES, PMSF_MULTIPLE))
								AddFileToArray(szPath, *ppszMusicPath, pfo, fCopy);
							else 
								AddFileToArray(szPath, *ppszDocumentsPath, pfo, fCopy);
						}

				}

				pfo->PerformOperations();

				pfo->Release();

				GlobalUnlock ( stg.hGlobal );
				ReleaseStgMedium ( &stg );

			}

		}

		psv->Release();
	}

	CoTaskMemFree(*ppszDocumentsPath);
	CoTaskMemFree(*ppszMusicPath);
	CoTaskMemFree(*ppszPicturesPath);
	CoTaskMemFree(*ppszVideosPath);
}

void CShellExtension::AddFileToArray(LPCWSTR szPath, LPWSTR pszDest, IFileOperation* pfo, bool fCopy)
{
    IShellItem *psiFrom = NULL;

    int hr = SHCreateItemFromParsingName(szPath, NULL, IID_PPV_ARGS(&psiFrom));

    if (SUCCEEDED(hr))
    {
        IShellItem *psiTo = NULL;

        if (NULL != pszDest)
            hr = SHCreateItemFromParsingName(pszDest,  NULL, IID_PPV_ARGS(&psiTo));

		if (SUCCEEDED(hr))
        {
			if (fCopy)
				hr = pfo->CopyItem(psiFrom, psiTo, NULL, NULL);
			else
				hr = pfo->MoveItem(psiFrom, psiTo, NULL, NULL);

			if (NULL != psiTo)
                psiTo->Release();
        }

        psiFrom->Release();
    }
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Independent
Argentina Argentina
I have been working as a developer for 15 years now, starting with gwbasic, moving to C and VB and then to VB.Net and later C#. I've used many languages, as languages is what I really like.

Comments and Discussions