// handles the downlevel slideshow extension #include "precomp.h" #include typedef struct { INT idPage; INT idHeading; INT idSubHeading; DWORD dwFlags; DLGPROC dlgproc; } WIZPAGE; HPROPSHEETPAGE _CreatePropPageFromInfo(const WIZPAGE *pwp, LPARAM lParam) { PROPSHEETPAGE psp = { 0 }; psp.dwSize = sizeof(psp); psp.hInstance = g_hinst; psp.lParam = lParam; psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | pwp->dwFlags; psp.pszTemplate = MAKEINTRESOURCE(pwp->idPage); psp.pfnDlgProc = pwp->dlgproc; psp.pszTitle = MAKEINTRESOURCE(IDS_BURN_WIZTITLE); psp.pszHeaderTitle = MAKEINTRESOURCE(pwp->idHeading); psp.pszHeaderSubTitle = MAKEINTRESOURCE(pwp->idSubHeading); return CreatePropertySheetPage(&psp); } HRESULT _CreateDataObject(IDataObject **ppdo) { LPITEMIDLIST pidl; HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidl); if (SUCCEEDED(hr)) { LPCITEMIDLIST pidlChild; IShellFolder* psf; hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { hr = psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_X_PPV_ARG(IDataObject, NULL, ppdo)); psf->Release(); } ILFree(pidl); } return hr; } STDAPI CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi); class CSlideshowExtension : public CObjectWithSite, public ICDBurnExt, public IDropTarget, public IWizardExtension, public INamespaceWalkCB { public: // IUnknown methods STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // ICDBurnExt methods STDMETHOD(GetSupportedActionTypes)(DWORD *pdwActions); // IDropTarget methods STDMETHOD(DragEnter)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { return E_NOTIMPL; } STDMETHOD(DragLeave)(void) { return E_NOTIMPL; } STDMETHOD(Drop)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); // IWizardExtension STDMETHOD(AddPages)(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages); STDMETHOD(GetFirstPage)(HPROPSHEETPAGE *phPage); STDMETHOD(GetLastPage)(HPROPSHEETPAGE *phPage); // INamespaceWalkCB STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; } STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; } STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel) { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; } static INT_PTR s_SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CSlideshowExtension *pse = s_GetSlideshowExtension(hwnd, uMsg, lParam); return pse->_SlideshowDlgProc(hwnd, uMsg, wParam, lParam); } private: CSlideshowExtension(); ~CSlideshowExtension(); LONG _cRef; UINT _cTotalFiles, _cPictureFiles; HPROPSHEETPAGE _hpage; BOOL _fSelectPictures; BOOL _DataObjectHasPictureFiles(IDataObject *pdo); // wizard page static CSlideshowExtension* s_GetSlideshowExtension(HWND hwnd, UINT uMsg, LPARAM lParam); INT_PTR _SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void _SetCompletionState(); // copy stuff BOOL _FileExistsInStaging(LPCWSTR pszFile); BOOL _SafeToWrite(); HRESULT _CopyGDIPLUS(LPCWSTR pszBase); HRESULT _CopySSHOW(LPCWSTR pszBase); HRESULT _CreateAutorunINF(LPCWSTR pszBase); friend HRESULT CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi); }; CSlideshowExtension::CSlideshowExtension() : _cRef(1) { _fSelectPictures = TRUE; } CSlideshowExtension::~CSlideshowExtension() { ASSERT(!_punkSite); } // IUnknown STDMETHODIMP_(ULONG) CSlideshowExtension::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CSlideshowExtension::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CSlideshowExtension::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CSlideshowExtension, ICDBurnExt), QITABENT(CSlideshowExtension, IDropTarget), QITABENT(CSlideshowExtension, IWizardExtension), QITABENT(CSlideshowExtension, IObjectWithSite), QITABENT(CSlideshowExtension, INamespaceWalkCB), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDAPI CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { *ppunk = NULL; if (pUnkOuter) return CLASS_E_NOAGGREGATION; CSlideshowExtension *pse = new CSlideshowExtension(); if (!pse) return E_OUTOFMEMORY; HRESULT hr = pse->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); pse->Release(); return hr; } CSlideshowExtension* CSlideshowExtension::s_GetSlideshowExtension(HWND hwnd, UINT uMsg, LPARAM lParam) { if (uMsg == WM_INITDIALOG) { PROPSHEETPAGE *ppsp = (PROPSHEETPAGE*)lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, ppsp->lParam); return (CSlideshowExtension*)ppsp->lParam; } return (CSlideshowExtension*)GetWindowLongPtr(hwnd, GWLP_USERDATA); } HRESULT CSlideshowExtension::GetSupportedActionTypes(DWORD *pdwActions) { *pdwActions = CDBE_TYPE_DATA; return S_OK; } HRESULT CSlideshowExtension::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl) { _cTotalFiles++; WCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName))) && PathIsImage(szName)) { _cPictureFiles++; } return S_OK; } BOOL CSlideshowExtension::_DataObjectHasPictureFiles(IDataObject *pdo) { _cTotalFiles = 0; _cPictureFiles = 0; INamespaceWalk *pnsw; if (SUCCEEDED(CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)))) { pnsw->Walk(pdo, NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, 4, this); pnsw->Release(); } // avoid div by zero and check if it's more than 60% pictures return _cTotalFiles ? (_cPictureFiles >= (UINT)MulDiv(_cTotalFiles, 3, 5)) : FALSE; } BOOL CSlideshowExtension::_FileExistsInStaging(LPCWSTR pszFile) { BOOL fExists = TRUE; // default to true so we dont overwrite in error cases WCHAR szFullPath[MAX_PATH]; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_CDBURN_AREA, NULL, 0, szFullPath))) { if (PathCombine(szFullPath, szFullPath, pszFile) && !PathFileExists(szFullPath)) { fExists = FALSE; } } return fExists; } BOOL CSlideshowExtension::_SafeToWrite() { // these filenames aren't localizable or anything. return !_FileExistsInStaging(L"autorun.inf") && !_FileExistsInStaging(L"autorun.exe") && !_FileExistsInStaging(L"gdiplus.dll"); } HRESULT CSlideshowExtension::DragEnter(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_NONE; ICDBurnPriv *pcdbp; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_CDBurn, NULL, IID_PPV_ARG(ICDBurnPriv, &pcdbp)))) { BOOL fOnMedia; if (SUCCEEDED(pcdbp->GetContentState(NULL, &fOnMedia)) && !fOnMedia && _DataObjectHasPictureFiles(pdo) && _SafeToWrite()) { *pdwEffect = DROPEFFECT_COPY; } pcdbp->Release(); } return S_OK; } HRESULT CSlideshowExtension::_CopyGDIPLUS(LPCWSTR pszBase) { HRESULT hr = E_FAIL; WCHAR szGDIPlus[MAX_PATH]; if (PathCombine(szGDIPlus, pszBase, L"gdiplus.dll")) { // this is actually the cleanest way to do it -- we need fusion to go off and get // gdiplus from the right place. // note: dont have to worry about ia64 since burning got punted for it. HMODULE hmodGDI = LoadLibrary(L"gdiplus.dll"); if (hmodGDI) { WCHAR szGDISrc[MAX_PATH]; if (GetModuleFileName(hmodGDI, szGDISrc, ARRAYSIZE(szGDISrc)) && CopyFile(szGDISrc, szGDIPlus, TRUE)) { hr = S_OK; SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szGDIPlus, NULL); } FreeLibrary(hmodGDI); } } return hr; } HRESULT CSlideshowExtension::_CopySSHOW(LPCWSTR pszBase) { // todo: maybe use PathYetAnotherMakeUniqueName here, but maybe nobody will ever care. HRESULT hr = E_FAIL; WCHAR szSSHOW[MAX_PATH]; if (PathCombine(szSSHOW, pszBase, L"autorun.exe")) { // note: dont have to worry about ia64 since burning got punted for it. HRSRC hrsrc = FindResource(g_hinst, MAKEINTRESOURCE(IDR_SSHOW_EXE), L"BINARY"); if (hrsrc) { HGLOBAL hFileHandle = LoadResource(g_hinst, hrsrc); if (hFileHandle) { BYTE *pBuf = (BYTE *)LockResource(hFileHandle); if (pBuf) { HANDLE hNewFile = CreateFile(szSSHOW, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); if (INVALID_HANDLE_VALUE != hNewFile) { DWORD cb, dwSize = SizeofResource(g_hinst, hrsrc); WriteFile(hNewFile, pBuf, dwSize, &cb, NULL); SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szSSHOW, NULL); hr = S_OK; CloseHandle(hNewFile); } } } } } return hr; } HRESULT CSlideshowExtension::_CreateAutorunINF(LPCWSTR pszBase) { HRESULT hr = E_FAIL; WCHAR szAutorun[MAX_PATH]; if (PathCombine(szAutorun, pszBase, L"autorun.inf")) { HANDLE hFile = CreateFile(szAutorun, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); if (INVALID_HANDLE_VALUE != hFile) { const WCHAR c_szText[] = L"[autorun]\r\nopen=autorun.exe\r\nUseAutoPLAY=1"; DWORD dw; WriteFile(hFile, c_szText, sizeof(c_szText), &dw, NULL); CloseHandle(hFile); hr = S_OK; SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szAutorun, NULL); } } return hr; } HRESULT CSlideshowExtension::Drop(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { // bring this in when sshow.exe is in the install // we know the media is blank, so we dont need to worry about a merged namespace. // work on the filesystem. HRESULT hr = E_FAIL; if (_SafeToWrite()) { // if they somehow add files called "autorun.inf", "autorun.exe", or "gdiplus.dll" between now // and when we're done in the next fraction of a second, we wont overwrite those but the autorun // may not work... screw em. WCHAR szStaging[MAX_PATH]; hr = SHGetFolderPath(NULL, CSIDL_CDBURN_AREA, NULL, 0, szStaging); if (SUCCEEDED(hr)) { _CopyGDIPLUS(szStaging); _CopySSHOW(szStaging); _CreateAutorunINF(szStaging); } } return hr; } HRESULT CSlideshowExtension::GetLastPage(HPROPSHEETPAGE *phPage) { *phPage = _hpage; return S_OK; } HRESULT CSlideshowExtension::GetFirstPage(HPROPSHEETPAGE *phPage) { *phPage = _hpage; return S_OK; } HRESULT CSlideshowExtension::AddPages(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages) { *pnPages = 0; WIZPAGE c_wp = {DLG_BURNWIZ_SLIDESHOW, IDS_BURNWIZ_SLIDESHOW_HEAD, IDS_BURNWIZ_SLIDESHOW_SUB, 0, CSlideshowExtension::s_SlideshowDlgProc}; _hpage = _CreatePropPageFromInfo(&c_wp, (LPARAM)this); if (cPages > 0) { aPages[0] = _hpage; *pnPages = 1; } return S_OK; } // pushes the return state back to the main wizard void CSlideshowExtension::_SetCompletionState() { IPropertyBag *ppb; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_CDWizardHost, IID_PPV_ARG(IPropertyBag, &ppb)))) { SHPropertyBag_WriteDWORD(ppb, PROPSTR_EXTENSIONCOMPLETIONSTATE, CDBE_RET_DEFAULT); ppb->Release(); } } INT_PTR CSlideshowExtension::_SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { BOOL fRet = FALSE; switch (uMsg) { case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT); Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNPICTURE), _fSelectPictures ? BST_CHECKED : BST_UNCHECKED); Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNDATA), _fSelectPictures ? BST_UNCHECKED : BST_CHECKED); fRet = TRUE; break; case PSN_WIZBACK: if (_punkSite) { IWizardSite *pws; if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws)))) { HPROPSHEETPAGE hpage; if (SUCCEEDED(pws->GetPreviousPage(&hpage))) { PropSheet_SetCurSel(GetParent(hwnd), hpage, -1); SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1); } pws->Release(); } } fRet = TRUE; break; case PSN_WIZNEXT: if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNPICTURE) == BST_CHECKED) { IDataObject *pdo; if (SUCCEEDED(_CreateDataObject(&pdo))) { POINTL pt = {0}; if (SUCCEEDED(Drop(pdo, 0, pt, NULL))) { _SetCompletionState(); } pdo->Release(); } } if (_punkSite) { IWizardSite *pws; if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws)))) { HPROPSHEETPAGE hpage; if (SUCCEEDED(pws->GetNextPage(&hpage))) { PropSheet_SetCurSel(GetParent(hwnd), hpage, -1); SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1); } pws->Release(); } } fRet = TRUE; break; case PSN_KILLACTIVE: _fSelectPictures = (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNPICTURE) == BST_CHECKED); break; } break; } } return fRet; }