Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
15 KiB

  1. // handles the downlevel slideshow extension
  2. #include "precomp.h"
  3. #include <cowsite.h>
  4. typedef struct
  5. {
  6. INT idPage;
  7. INT idHeading;
  8. INT idSubHeading;
  9. DWORD dwFlags;
  10. DLGPROC dlgproc;
  11. } WIZPAGE;
  12. HPROPSHEETPAGE _CreatePropPageFromInfo(const WIZPAGE *pwp, LPARAM lParam)
  13. {
  14. PROPSHEETPAGE psp = { 0 };
  15. psp.dwSize = sizeof(psp);
  16. psp.hInstance = g_hinst;
  17. psp.lParam = lParam;
  18. psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | pwp->dwFlags;
  19. psp.pszTemplate = MAKEINTRESOURCE(pwp->idPage);
  20. psp.pfnDlgProc = pwp->dlgproc;
  21. psp.pszTitle = MAKEINTRESOURCE(IDS_BURN_WIZTITLE);
  22. psp.pszHeaderTitle = MAKEINTRESOURCE(pwp->idHeading);
  23. psp.pszHeaderSubTitle = MAKEINTRESOURCE(pwp->idSubHeading);
  24. return CreatePropertySheetPage(&psp);
  25. }
  26. HRESULT _CreateDataObject(IDataObject **ppdo)
  27. {
  28. LPITEMIDLIST pidl;
  29. HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidl);
  30. if (SUCCEEDED(hr))
  31. {
  32. LPCITEMIDLIST pidlChild;
  33. IShellFolder* psf;
  34. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  35. if (SUCCEEDED(hr))
  36. {
  37. hr = psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_X_PPV_ARG(IDataObject, NULL, ppdo));
  38. psf->Release();
  39. }
  40. ILFree(pidl);
  41. }
  42. return hr;
  43. }
  44. STDAPI CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);
  45. class CSlideshowExtension : public CObjectWithSite,
  46. public ICDBurnExt,
  47. public IDropTarget,
  48. public IWizardExtension,
  49. public INamespaceWalkCB
  50. {
  51. public:
  52. // IUnknown methods
  53. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  54. STDMETHOD_(ULONG, AddRef)();
  55. STDMETHOD_(ULONG, Release)();
  56. // ICDBurnExt methods
  57. STDMETHOD(GetSupportedActionTypes)(DWORD *pdwActions);
  58. // IDropTarget methods
  59. STDMETHOD(DragEnter)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  60. STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  61. { return E_NOTIMPL; }
  62. STDMETHOD(DragLeave)(void)
  63. { return E_NOTIMPL; }
  64. STDMETHOD(Drop)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  65. // IWizardExtension
  66. STDMETHOD(AddPages)(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages);
  67. STDMETHOD(GetFirstPage)(HPROPSHEETPAGE *phPage);
  68. STDMETHOD(GetLastPage)(HPROPSHEETPAGE *phPage);
  69. // INamespaceWalkCB
  70. STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl);
  71. STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  72. { return S_OK; }
  73. STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  74. { return S_OK; }
  75. STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel)
  76. { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
  77. static INT_PTR s_SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  78. { CSlideshowExtension *pse = s_GetSlideshowExtension(hwnd, uMsg, lParam); return pse->_SlideshowDlgProc(hwnd, uMsg, wParam, lParam); }
  79. private:
  80. CSlideshowExtension();
  81. ~CSlideshowExtension();
  82. LONG _cRef;
  83. UINT _cTotalFiles, _cPictureFiles;
  84. HPROPSHEETPAGE _hpage;
  85. BOOL _fSelectPictures;
  86. BOOL _DataObjectHasPictureFiles(IDataObject *pdo);
  87. // wizard page
  88. static CSlideshowExtension* s_GetSlideshowExtension(HWND hwnd, UINT uMsg, LPARAM lParam);
  89. INT_PTR _SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  90. void _SetCompletionState();
  91. // copy stuff
  92. BOOL _FileExistsInStaging(LPCWSTR pszFile);
  93. BOOL _SafeToWrite();
  94. HRESULT _CopyGDIPLUS(LPCWSTR pszBase);
  95. HRESULT _CopySSHOW(LPCWSTR pszBase);
  96. HRESULT _CreateAutorunINF(LPCWSTR pszBase);
  97. friend HRESULT CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);
  98. };
  99. CSlideshowExtension::CSlideshowExtension() :
  100. _cRef(1)
  101. {
  102. _fSelectPictures = TRUE;
  103. }
  104. CSlideshowExtension::~CSlideshowExtension()
  105. {
  106. ASSERT(!_punkSite);
  107. }
  108. // IUnknown
  109. STDMETHODIMP_(ULONG) CSlideshowExtension::AddRef()
  110. {
  111. return InterlockedIncrement(&_cRef);
  112. }
  113. STDMETHODIMP_(ULONG) CSlideshowExtension::Release()
  114. {
  115. if (InterlockedDecrement(&_cRef))
  116. return _cRef;
  117. delete this;
  118. return 0;
  119. }
  120. HRESULT CSlideshowExtension::QueryInterface(REFIID riid, void **ppv)
  121. {
  122. static const QITAB qit[] =
  123. {
  124. QITABENT(CSlideshowExtension, ICDBurnExt),
  125. QITABENT(CSlideshowExtension, IDropTarget),
  126. QITABENT(CSlideshowExtension, IWizardExtension),
  127. QITABENT(CSlideshowExtension, IObjectWithSite),
  128. QITABENT(CSlideshowExtension, INamespaceWalkCB),
  129. { 0 },
  130. };
  131. return QISearch(this, qit, riid, ppv);
  132. }
  133. STDAPI CSlideshowExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  134. {
  135. *ppunk = NULL;
  136. if (pUnkOuter)
  137. return CLASS_E_NOAGGREGATION;
  138. CSlideshowExtension *pse = new CSlideshowExtension();
  139. if (!pse)
  140. return E_OUTOFMEMORY;
  141. HRESULT hr = pse->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  142. pse->Release();
  143. return hr;
  144. }
  145. CSlideshowExtension* CSlideshowExtension::s_GetSlideshowExtension(HWND hwnd, UINT uMsg, LPARAM lParam)
  146. {
  147. if (uMsg == WM_INITDIALOG)
  148. {
  149. PROPSHEETPAGE *ppsp = (PROPSHEETPAGE*)lParam;
  150. SetWindowLongPtr(hwnd, GWLP_USERDATA, ppsp->lParam);
  151. return (CSlideshowExtension*)ppsp->lParam;
  152. }
  153. return (CSlideshowExtension*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  154. }
  155. HRESULT CSlideshowExtension::GetSupportedActionTypes(DWORD *pdwActions)
  156. {
  157. *pdwActions = CDBE_TYPE_DATA;
  158. return S_OK;
  159. }
  160. HRESULT CSlideshowExtension::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
  161. {
  162. _cTotalFiles++;
  163. WCHAR szName[MAX_PATH];
  164. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName))) &&
  165. PathIsImage(szName))
  166. {
  167. _cPictureFiles++;
  168. }
  169. return S_OK;
  170. }
  171. BOOL CSlideshowExtension::_DataObjectHasPictureFiles(IDataObject *pdo)
  172. {
  173. _cTotalFiles = 0;
  174. _cPictureFiles = 0;
  175. INamespaceWalk *pnsw;
  176. if (SUCCEEDED(CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw))))
  177. {
  178. pnsw->Walk(pdo, NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, 4, this);
  179. pnsw->Release();
  180. }
  181. // avoid div by zero and check if it's more than 60% pictures
  182. return _cTotalFiles ? (_cPictureFiles >= (UINT)MulDiv(_cTotalFiles, 3, 5)) : FALSE;
  183. }
  184. BOOL CSlideshowExtension::_FileExistsInStaging(LPCWSTR pszFile)
  185. {
  186. BOOL fExists = TRUE; // default to true so we dont overwrite in error cases
  187. WCHAR szFullPath[MAX_PATH];
  188. if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_CDBURN_AREA, NULL, 0, szFullPath)))
  189. {
  190. if (PathCombine(szFullPath, szFullPath, pszFile) &&
  191. !PathFileExists(szFullPath))
  192. {
  193. fExists = FALSE;
  194. }
  195. }
  196. return fExists;
  197. }
  198. BOOL CSlideshowExtension::_SafeToWrite()
  199. {
  200. // these filenames aren't localizable or anything.
  201. return !_FileExistsInStaging(L"autorun.inf") &&
  202. !_FileExistsInStaging(L"autorun.exe") &&
  203. !_FileExistsInStaging(L"gdiplus.dll");
  204. }
  205. HRESULT CSlideshowExtension::DragEnter(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  206. {
  207. *pdwEffect = DROPEFFECT_NONE;
  208. ICDBurnPriv *pcdbp;
  209. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_CDBurn, NULL, IID_PPV_ARG(ICDBurnPriv, &pcdbp))))
  210. {
  211. BOOL fOnMedia;
  212. if (SUCCEEDED(pcdbp->GetContentState(NULL, &fOnMedia)) && !fOnMedia &&
  213. _DataObjectHasPictureFiles(pdo) &&
  214. _SafeToWrite())
  215. {
  216. *pdwEffect = DROPEFFECT_COPY;
  217. }
  218. pcdbp->Release();
  219. }
  220. return S_OK;
  221. }
  222. HRESULT CSlideshowExtension::_CopyGDIPLUS(LPCWSTR pszBase)
  223. {
  224. HRESULT hr = E_FAIL;
  225. WCHAR szGDIPlus[MAX_PATH];
  226. if (PathCombine(szGDIPlus, pszBase, L"gdiplus.dll"))
  227. {
  228. // this is actually the cleanest way to do it -- we need fusion to go off and get
  229. // gdiplus from the right place.
  230. // note: dont have to worry about ia64 since burning got punted for it.
  231. HMODULE hmodGDI = LoadLibrary(L"gdiplus.dll");
  232. if (hmodGDI)
  233. {
  234. WCHAR szGDISrc[MAX_PATH];
  235. if (GetModuleFileName(hmodGDI, szGDISrc, ARRAYSIZE(szGDISrc)) &&
  236. CopyFile(szGDISrc, szGDIPlus, TRUE))
  237. {
  238. hr = S_OK;
  239. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szGDIPlus, NULL);
  240. }
  241. FreeLibrary(hmodGDI);
  242. }
  243. }
  244. return hr;
  245. }
  246. HRESULT CSlideshowExtension::_CopySSHOW(LPCWSTR pszBase)
  247. {
  248. // todo: maybe use PathYetAnotherMakeUniqueName here, but maybe nobody will ever care.
  249. HRESULT hr = E_FAIL;
  250. WCHAR szSSHOW[MAX_PATH];
  251. if (PathCombine(szSSHOW, pszBase, L"autorun.exe"))
  252. {
  253. // note: dont have to worry about ia64 since burning got punted for it.
  254. HRSRC hrsrc = FindResource(g_hinst, MAKEINTRESOURCE(IDR_SSHOW_EXE), L"BINARY");
  255. if (hrsrc)
  256. {
  257. HGLOBAL hFileHandle = LoadResource(g_hinst, hrsrc);
  258. if (hFileHandle)
  259. {
  260. BYTE *pBuf = (BYTE *)LockResource(hFileHandle);
  261. if (pBuf)
  262. {
  263. HANDLE hNewFile = CreateFile(szSSHOW, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
  264. if (INVALID_HANDLE_VALUE != hNewFile)
  265. {
  266. DWORD cb, dwSize = SizeofResource(g_hinst, hrsrc);
  267. WriteFile(hNewFile, pBuf, dwSize, &cb, NULL);
  268. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szSSHOW, NULL);
  269. hr = S_OK;
  270. CloseHandle(hNewFile);
  271. }
  272. }
  273. }
  274. }
  275. }
  276. return hr;
  277. }
  278. HRESULT CSlideshowExtension::_CreateAutorunINF(LPCWSTR pszBase)
  279. {
  280. HRESULT hr = E_FAIL;
  281. WCHAR szAutorun[MAX_PATH];
  282. if (PathCombine(szAutorun, pszBase, L"autorun.inf"))
  283. {
  284. HANDLE hFile = CreateFile(szAutorun, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
  285. if (INVALID_HANDLE_VALUE != hFile)
  286. {
  287. const WCHAR c_szText[] = L"[autorun]\r\nopen=autorun.exe\r\nUseAutoPLAY=1";
  288. DWORD dw;
  289. WriteFile(hFile, c_szText, sizeof(c_szText), &dw, NULL);
  290. CloseHandle(hFile);
  291. hr = S_OK;
  292. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szAutorun, NULL);
  293. }
  294. }
  295. return hr;
  296. }
  297. HRESULT CSlideshowExtension::Drop(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  298. {
  299. // bring this in when sshow.exe is in the install
  300. // we know the media is blank, so we dont need to worry about a merged namespace.
  301. // work on the filesystem.
  302. HRESULT hr = E_FAIL;
  303. if (_SafeToWrite())
  304. {
  305. // if they somehow add files called "autorun.inf", "autorun.exe", or "gdiplus.dll" between now
  306. // and when we're done in the next fraction of a second, we wont overwrite those but the autorun
  307. // may not work... screw em.
  308. WCHAR szStaging[MAX_PATH];
  309. hr = SHGetFolderPath(NULL, CSIDL_CDBURN_AREA, NULL, 0, szStaging);
  310. if (SUCCEEDED(hr))
  311. {
  312. _CopyGDIPLUS(szStaging);
  313. _CopySSHOW(szStaging);
  314. _CreateAutorunINF(szStaging);
  315. }
  316. }
  317. return hr;
  318. }
  319. HRESULT CSlideshowExtension::GetLastPage(HPROPSHEETPAGE *phPage)
  320. {
  321. *phPage = _hpage;
  322. return S_OK;
  323. }
  324. HRESULT CSlideshowExtension::GetFirstPage(HPROPSHEETPAGE *phPage)
  325. {
  326. *phPage = _hpage;
  327. return S_OK;
  328. }
  329. HRESULT CSlideshowExtension::AddPages(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages)
  330. {
  331. *pnPages = 0;
  332. WIZPAGE c_wp =
  333. {DLG_BURNWIZ_SLIDESHOW, IDS_BURNWIZ_SLIDESHOW_HEAD, IDS_BURNWIZ_SLIDESHOW_SUB, 0, CSlideshowExtension::s_SlideshowDlgProc};
  334. _hpage = _CreatePropPageFromInfo(&c_wp, (LPARAM)this);
  335. if (cPages > 0)
  336. {
  337. aPages[0] = _hpage;
  338. *pnPages = 1;
  339. }
  340. return S_OK;
  341. }
  342. // pushes the return state back to the main wizard
  343. void CSlideshowExtension::_SetCompletionState()
  344. {
  345. IPropertyBag *ppb;
  346. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_CDWizardHost, IID_PPV_ARG(IPropertyBag, &ppb))))
  347. {
  348. SHPropertyBag_WriteDWORD(ppb, PROPSTR_EXTENSIONCOMPLETIONSTATE, CDBE_RET_DEFAULT);
  349. ppb->Release();
  350. }
  351. }
  352. INT_PTR CSlideshowExtension::_SlideshowDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  353. {
  354. BOOL fRet = FALSE;
  355. switch (uMsg)
  356. {
  357. case WM_NOTIFY:
  358. {
  359. LPNMHDR pnmh = (LPNMHDR)lParam;
  360. switch (pnmh->code)
  361. {
  362. case PSN_SETACTIVE:
  363. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
  364. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNPICTURE), _fSelectPictures ? BST_CHECKED : BST_UNCHECKED);
  365. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNDATA), _fSelectPictures ? BST_UNCHECKED : BST_CHECKED);
  366. fRet = TRUE;
  367. break;
  368. case PSN_WIZBACK:
  369. if (_punkSite)
  370. {
  371. IWizardSite *pws;
  372. if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws))))
  373. {
  374. HPROPSHEETPAGE hpage;
  375. if (SUCCEEDED(pws->GetPreviousPage(&hpage)))
  376. {
  377. PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
  378. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  379. }
  380. pws->Release();
  381. }
  382. }
  383. fRet = TRUE;
  384. break;
  385. case PSN_WIZNEXT:
  386. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNPICTURE) == BST_CHECKED)
  387. {
  388. IDataObject *pdo;
  389. if (SUCCEEDED(_CreateDataObject(&pdo)))
  390. {
  391. POINTL pt = {0};
  392. if (SUCCEEDED(Drop(pdo, 0, pt, NULL)))
  393. {
  394. _SetCompletionState();
  395. }
  396. pdo->Release();
  397. }
  398. }
  399. if (_punkSite)
  400. {
  401. IWizardSite *pws;
  402. if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws))))
  403. {
  404. HPROPSHEETPAGE hpage;
  405. if (SUCCEEDED(pws->GetNextPage(&hpage)))
  406. {
  407. PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
  408. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  409. }
  410. pws->Release();
  411. }
  412. }
  413. fRet = TRUE;
  414. break;
  415. case PSN_KILLACTIVE:
  416. _fSelectPictures = (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNPICTURE) == BST_CHECKED);
  417. break;
  418. }
  419. break;
  420. }
  421. }
  422. return fRet;
  423. }