#include "precomp.h" #include "shimgdata.h" #include using namespace Gdiplus; #pragma hdrstop class CResizePhotos : public IShellExtInit, IContextMenu, INamespaceWalkCB { public: CResizePhotos(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IShellExtInit STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdto, HKEY hkProgID); // IContextMenu STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI); STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT *res, LPSTR pName, UINT ccMax) { return E_NOTIMPL; } // INamespaceWalkCB STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_FALSE; } STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; } STDMETHODIMP InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel); private: ~CResizePhotos(); HRESULT _ResizeItems(HWND hwnd, IDataObject *pdo); long _cRef; IDataObject *_pdo; int _iDestSize; }; CResizePhotos::CResizePhotos() : _cRef(1), _iDestSize(0) { DllAddRef(); } CResizePhotos::~CResizePhotos() { IUnknown_Set((IUnknown**)&_pdo, NULL); DllRelease(); } STDAPI CResizePhotos_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CResizePhotos *prp = new CResizePhotos(); if (!prp) { *ppunk = NULL; // incase of failure return E_OUTOFMEMORY; } HRESULT hr = prp->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); prp->Release(); return hr; } // IUnknown ULONG CResizePhotos::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CResizePhotos::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CResizePhotos::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CResizePhotos, IShellExtInit), QITABENT(CResizePhotos, IContextMenu), QITABENT(CResizePhotos, INamespaceWalkCB), {0, 0 }, }; return QISearch(this, qit, riid, ppv); } // IShellExtInit HRESULT CResizePhotos::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdto, HKEY hkProgID) { IUnknown_Set((IUnknown**)&_pdo, pdto); return S_OK; } // IContextMenu HRESULT CResizePhotos::QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags) { if (!_pdo) return E_UNEXPECTED; if (!(uFlags & CMF_DEFAULTONLY)) { TCHAR szBuffer[64]; LoadString(g_hinst, IDS_RESIZEPICTURES, szBuffer, ARRAYSIZE(szBuffer)); InsertMenu(hMenu, uIndex++, MF_BYPOSITION|MF_STRING, uIDFirst + 0x0, szBuffer); } return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1); } HRESULT CResizePhotos::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { HRESULT hr = E_FAIL; if (IS_INTRESOURCE(pici->lpVerb)) { switch(LOWORD(pici->lpVerb)) { case 0: hr = _ResizeItems(pici->hwnd, _pdo); break; } } return hr; } // Walk the list of files and generate resized versions of them struct { int cx, cy, idsSuffix; } _aSizes[] = { { 640, 480, IDS_RESIZESMALLSUFFIX }, { 800, 600, IDS_RESIZEMEDIUMSUFFIX }, { 1024, 768, IDS_RESIZELARGESUFFIX }, }; HRESULT CResizePhotos::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl) { TCHAR szName[MAX_PATH]; HRESULT hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr) && PathIsImage(szName)) { // create the decoder for this file and decode it so we know we can scale and // persist it again. IShellImageDataFactory *psidf; hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellImageDataFactory, &psidf)); if (SUCCEEDED(hr)) { IShellImageData *psid; if (SUCCEEDED(psidf->CreateImageFromFile(szName, &psid))) { hr = psid->Decode(SHIMGDEC_DEFAULT, 0, 0); if (SUCCEEDED(hr)) { SIZE szImage; hr = psid->GetSize(&szImage); if (SUCCEEDED(hr)) { // lets scale based on the largest axis to keep its aspect ratio if (szImage.cx > szImage.cy) { if (szImage.cx >= _aSizes[_iDestSize].cx) hr = psid->Scale(_aSizes[_iDestSize].cx, 0, InterpolationModeHighQuality); } else { if (szImage.cy >= _aSizes[_iDestSize].cy) hr = psid->Scale(0, _aSizes[_iDestSize].cy, InterpolationModeHighQuality); } // format up a new name for the based on its current name, and the size we // are generating TCHAR szNewName[MAX_PATH]; lstrcpyn(szNewName, szName, ARRAYSIZE(szNewName)); TCHAR szSuffix[MAX_PATH]; LoadString(g_hinst, _aSizes[_iDestSize].idsSuffix, szSuffix, ARRAYSIZE(szSuffix)); PathRemoveExtension(szNewName); PathRemoveBlanks(szNewName); StrCatBuff(szNewName, szSuffix, ARRAYSIZE(szNewName)); StrCatBuff(szNewName, PathFindExtension(szName), ARRAYSIZE(szNewName)); PathYetAnotherMakeUniqueName(szNewName, szNewName, NULL, NULL); IPersistFile *ppf; hr = psid->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); if (SUCCEEDED(hr)) { hr = ppf->Save(szNewName, FALSE); ppf->Release(); } } } psid->Release(); } psidf->Release(); } } return hr; } HRESULT CResizePhotos::InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel) { *ppszCancel = NULL; // use default TCHAR szMsg[128]; LoadString(g_hinst, IDS_RESIZEPICTURES, szMsg, ARRAYSIZE(szMsg)); return SHStrDup(szMsg, ppszTitle); } INT_PTR _ResizeDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: Button_SetCheck(GetDlgItem(hwnd, IDC_RESIZE_SMALL), BST_CHECKED); return TRUE; case WM_COMMAND: { switch (wParam) { case IDCANCEL: EndDialog(hwnd, -1); break; case IDOK: { int iResponse; if (IsDlgButtonChecked(hwnd, IDC_RESIZE_SMALL)) iResponse = 0; else if (IsDlgButtonChecked(hwnd, IDC_RESIZE_MEDIUM)) iResponse = 1; else iResponse = 2; EndDialog(hwnd, iResponse); } } } } return FALSE; } HRESULT CResizePhotos::_ResizeItems(HWND hwnd, IDataObject *pdo) { HRESULT hr = S_OK; _iDestSize = (int)DialogBox(g_hinst, MAKEINTRESOURCE(DLG_RESIZEPICTURES), hwnd, _ResizeDlgProc); if (_iDestSize >= 0) { INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { hr = pnsw->Walk(pdo, NSWF_SHOW_PROGRESS|NSWF_DONT_ACCUMULATE_RESULT|NSWF_FILESYSTEM_ONLY|NSWF_FLAG_VIEWORDER, 1, SAFECAST(this, INamespaceWalkCB *)); pnsw->Release(); } } return hr; }