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.

1337 lines
42 KiB

  1. #include "precomp.h"
  2. #include "shimgvw.h"
  3. #include "cowsite.h"
  4. #include "prevwnd.h"
  5. #include "shutil.h"
  6. #include "prwiziid.h"
  7. #pragma hdrstop
  8. // Context menu offset IDs
  9. enum
  10. {
  11. OFFSET_OPEN = 0,
  12. OFFSET_PRINTTO,
  13. OFFSET_ROT90,
  14. OFFSET_ROT270,
  15. OFFSET_SETWALL,
  16. OFFSET_ZOOMIN,
  17. OFFSET_ZOOMOUT,
  18. OFFSET_ACTUALSIZE,
  19. OFFSET_BESTFIT,
  20. OFFSET_NEXTPAGE,
  21. OFFSET_PREVPAGE,
  22. OFFSET_MAX
  23. };
  24. #define PHOTOVERBS_THUMBNAIL 0x1
  25. #define PHOTOVERBS_ICON 0x2
  26. #define PHOTOVERBS_FILMSTRIP 0x3
  27. #define PHOTOVERBS_SLIDESHOW 0x4
  28. #define PHOTOVERBS_IMGPREVIEW 0x5
  29. class CPhotoVerbs : public IContextMenu,
  30. public IShellExtInit,
  31. public IDropTarget,
  32. public CObjectWithSite,
  33. public NonATLObject
  34. {
  35. public:
  36. CPhotoVerbs();
  37. // IUnknown
  38. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  39. STDMETHOD_(ULONG, AddRef)();
  40. STDMETHOD_(ULONG, Release)();
  41. // IShellExtInit
  42. STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, IDataObject *pdtobj, HKEY hKeyID);
  43. // IContextMenu
  44. STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags);
  45. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI);
  46. STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT *res, LPSTR pName, UINT ccMax);
  47. // IDropTarget ***
  48. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  49. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  50. STDMETHODIMP DragLeave(void);
  51. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  52. private:
  53. ~CPhotoVerbs();
  54. void _RotatePictures(int iAngle, UINT idPrompt);
  55. void _OpenPictures();
  56. void _SetWallpaper();
  57. // HRESULT _InvokePrintToInPPW(LPCMINVOKECOMMANDINFO pCMI, IDataObject *pdtobj);
  58. BOOL _ImageOptionExists(IQueryAssociations *pqa, DWORD dwOption);
  59. HRESULT _QueryAssociations();
  60. DWORD _GetMode();
  61. BOOL _CheckForcePreview(IQueryAssociations *pqa);
  62. HRESULT _MapVerb(LPCMINVOKECOMMANDINFO pici, int *pidVerb);
  63. LONG _cRef;
  64. IDataObject *_pdtobj;
  65. BOOL _fForcePreview;
  66. BOOL _fAcceptPreview;
  67. BOOL _fIncludeRotate;
  68. BOOL _fIncludeSetWallpaper;
  69. IImgCmdTarget * _pict; // if hosted in image preview, this allows us to delegate commands to it
  70. BOOL _fImgMode; // TRUE if we are hosted in defview and defview is in thumbnail or filmstip mode
  71. BOOL _fReadOnly; // TRUE if one or more items selected are SFGAO_READONLY
  72. };
  73. CPhotoVerbs::CPhotoVerbs() : _cRef(1)
  74. {
  75. ASSERT(_pdtobj == NULL);
  76. ASSERT(_fForcePreview == FALSE);
  77. ASSERT(_fIncludeRotate == FALSE);
  78. ASSERT(_fIncludeSetWallpaper == FALSE);
  79. }
  80. CPhotoVerbs::~CPhotoVerbs()
  81. {
  82. IUnknown_Set(&_punkSite, NULL);
  83. IUnknown_Set((IUnknown**)&_pdtobj, NULL);
  84. ATOMICRELEASE(_pict);
  85. }
  86. STDAPI CPhotoVerbs_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  87. {
  88. CPhotoVerbs *psid = new CPhotoVerbs();
  89. if (!psid)
  90. {
  91. *ppunk = NULL; // incase of failure
  92. return E_OUTOFMEMORY;
  93. }
  94. HRESULT hr = psid->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  95. psid->Release();
  96. return hr;
  97. }
  98. STDMETHODIMP CPhotoVerbs::QueryInterface(REFIID riid, void **ppv)
  99. {
  100. static const QITAB qit[] =
  101. {
  102. QITABENT(CPhotoVerbs, IShellExtInit),
  103. QITABENT(CPhotoVerbs, IContextMenu),
  104. QITABENT(CPhotoVerbs, IDropTarget),
  105. QITABENT(CPhotoVerbs, IObjectWithSite),
  106. { 0 },
  107. };
  108. return QISearch(this, qit, riid, ppv);
  109. }
  110. STDMETHODIMP_(ULONG) CPhotoVerbs::AddRef()
  111. {
  112. return InterlockedIncrement(&_cRef);
  113. }
  114. STDMETHODIMP_(ULONG) CPhotoVerbs::Release()
  115. {
  116. if (InterlockedDecrement(&_cRef))
  117. return _cRef;
  118. delete this;
  119. return 0;
  120. }
  121. // IShellExtInit
  122. STDMETHODIMP CPhotoVerbs::Initialize(LPCITEMIDLIST pIDFolder, IDataObject *pdtobj, HKEY hKeyID)
  123. {
  124. IUnknown_Set((IUnknown**)&_pdtobj, pdtobj);
  125. _fImgMode = FALSE;
  126. DWORD dwAttributes = 0;
  127. SHGetAttributesFromDataObject(pdtobj, SFGAO_READONLY, &dwAttributes, NULL);
  128. _fReadOnly = BOOLIFY(dwAttributes);
  129. return S_OK;
  130. }
  131. BOOL CPhotoVerbs::_ImageOptionExists(IQueryAssociations *pqa, DWORD dwOption)
  132. {
  133. BOOL fRetVal = FALSE;
  134. DWORD dwFlags = 0;
  135. DWORD cbFlags = sizeof(dwFlags);
  136. if (SUCCEEDED(pqa->GetData(0, ASSOCDATA_VALUE, TEXT("ImageOptionFlags"), &dwFlags, &cbFlags)))
  137. {
  138. fRetVal = (dwFlags & dwOption);
  139. }
  140. return fRetVal;
  141. }
  142. BOOL _VerbExists(IQueryAssociations *pqa, LPCTSTR pszVerb)
  143. {
  144. DWORD cch;
  145. return SUCCEEDED(pqa->GetString(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, pszVerb, NULL, &cch)) && cch;
  146. }
  147. BOOL CPhotoVerbs::_CheckForcePreview(IQueryAssociations *pqa)
  148. {
  149. // we force if the app has no a preview and the user has not customized
  150. // and we are not the current default (we install on open)
  151. BOOL fRet = FALSE;
  152. if (!_VerbExists(pqa, TEXT("preview")))
  153. {
  154. // if nobody owns we always accept
  155. // this is for when somebody does an InvokeCommand("preview");
  156. _fAcceptPreview = TRUE;
  157. if (S_FALSE == pqa->GetData(0, ASSOCDATA_HASPERUSERASSOC, NULL, NULL, NULL))
  158. {
  159. WCHAR sz[MAX_PATH];
  160. DWORD cch = ARRAYSIZE(sz);
  161. _fForcePreview = FAILED(pqa->GetString(0, ASSOCSTR_COMMAND, NULL, sz, &cch));
  162. if (!_fForcePreview)
  163. {
  164. // there is a default handler
  165. // if its us hide the preview verb
  166. // because the static menu will do it for us
  167. if (StrStrIW(sz, L"shimgvw.dll"))
  168. {
  169. _fAcceptPreview = FALSE;
  170. }
  171. else
  172. _fForcePreview = TRUE;
  173. }
  174. }
  175. }
  176. return fRet;
  177. }
  178. HRESULT CPhotoVerbs::_QueryAssociations()
  179. {
  180. IQueryAssociations *pqa;
  181. HRESULT hr = IUnknown_QueryService(_punkSite, SID_CtxQueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  182. if (SUCCEEDED(hr))
  183. {
  184. // dont do preview if the user has customized
  185. _CheckForcePreview(pqa);
  186. _fIncludeRotate = _ImageOptionExists(pqa, IMAGEOPTION_CANROTATE);
  187. _fIncludeSetWallpaper = _ImageOptionExists(pqa, IMAGEOPTION_CANWALLPAPER);
  188. pqa->Release();
  189. return S_OK;
  190. }
  191. else
  192. {
  193. // we may have been invoked directly instead of via ShellExecute or right-click
  194. _fAcceptPreview = TRUE;
  195. }
  196. return S_FALSE;
  197. }
  198. DWORD CPhotoVerbs::_GetMode()
  199. {
  200. DWORD dwMode, dw;
  201. if (_pict)
  202. {
  203. _pict->GetMode(&dw);
  204. switch (dw)
  205. {
  206. case SLIDESHOW_MODE:
  207. dwMode = PHOTOVERBS_SLIDESHOW;
  208. break;
  209. case WINDOW_MODE:
  210. dwMode = PHOTOVERBS_IMGPREVIEW;
  211. break;
  212. case CONTROL_MODE:
  213. dwMode = PHOTOVERBS_FILMSTRIP;
  214. break;
  215. default:
  216. dwMode = PHOTOVERBS_ICON;
  217. break;
  218. }
  219. }
  220. else
  221. {
  222. dwMode = (_fImgMode) ? PHOTOVERBS_THUMBNAIL : PHOTOVERBS_ICON;
  223. }
  224. return dwMode;
  225. }
  226. // IContextMenu
  227. STDMETHODIMP CPhotoVerbs::QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags)
  228. {
  229. TCHAR szBuffer[128];
  230. HRESULT hr = _QueryAssociations();
  231. DWORD dwMultiPage = MPCMD_HIDDEN;
  232. hr = IUnknown_QueryService(_punkSite, SID_SImageView, IID_PPV_ARG(IImgCmdTarget, &_pict));
  233. if (SUCCEEDED(hr))
  234. {
  235. _pict->GetPageFlags(&dwMultiPage);
  236. }
  237. IFolderView * pfv = NULL;
  238. hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  239. if (SUCCEEDED(hr))
  240. {
  241. UINT uViewMode;
  242. hr = pfv->GetCurrentViewMode(&uViewMode);
  243. if (SUCCEEDED(hr) &&
  244. ((FVM_THUMBNAIL == uViewMode) || (FVM_THUMBSTRIP == uViewMode)))
  245. {
  246. _fImgMode = TRUE;
  247. }
  248. pfv->Release();
  249. }
  250. DWORD dwMode = _GetMode();
  251. // always load the Open verb if no static Open verb is registered
  252. if (_fAcceptPreview)
  253. {
  254. if (PHOTOVERBS_SLIDESHOW != dwMode && PHOTOVERBS_IMGPREVIEW != dwMode)
  255. {
  256. LoadString(_Module.GetModuleInstance(), IDS_PREVIEW_CTX, szBuffer, ARRAYSIZE(szBuffer));
  257. InsertMenu(hMenu, uIndex, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_OPEN, szBuffer);
  258. // only set to default if there isnt a preview already there
  259. if (_fForcePreview)
  260. SetMenuDefaultItem(hMenu, uIndex, MF_BYPOSITION);
  261. uIndex++;
  262. }
  263. }
  264. if (!(uFlags & CMF_DEFAULTONLY))
  265. {
  266. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  267. if (_fIncludeRotate)
  268. {
  269. if (PHOTOVERBS_ICON != dwMode)
  270. {
  271. UINT uFlags = MF_BYPOSITION | MF_STRING;
  272. if (_fReadOnly && PHOTOVERBS_THUMBNAIL == dwMode)
  273. {
  274. uFlags |= MF_GRAYED;
  275. }
  276. else
  277. {
  278. uFlags |= MF_ENABLED; // in all modes by thumbnails, we allow temporary rotation of readonly images
  279. }
  280. LoadString(_Module.GetModuleInstance(), IDS_ROTATE90_CTX, szBuffer, ARRAYSIZE(szBuffer));
  281. InsertMenu(hMenu, uIndex++, uFlags, uIDFirst + OFFSET_ROT90, szBuffer);
  282. LoadString(_Module.GetModuleInstance(), IDS_ROTATE270_CTX, szBuffer, ARRAYSIZE(szBuffer));
  283. InsertMenu(hMenu, uIndex++, uFlags, uIDFirst + OFFSET_ROT270, szBuffer);
  284. }
  285. }
  286. if (PHOTOVERBS_IMGPREVIEW == dwMode)
  287. {
  288. LoadString(_Module.GetModuleInstance(), IDS_ZOOMIN_CTX, szBuffer, ARRAYSIZE(szBuffer));
  289. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_ZOOMIN, szBuffer);
  290. LoadString(_Module.GetModuleInstance(), IDS_ZOOMOUT_CTX, szBuffer, ARRAYSIZE(szBuffer));
  291. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_ZOOMOUT, szBuffer);
  292. if (dwMultiPage != MPCMD_HIDDEN && dwMultiPage != MPCMD_DISABLED)
  293. {
  294. if (MPCMD_LASTPAGE != dwMultiPage)
  295. {
  296. LoadString(_Module.GetModuleInstance(), IDS_NEXTPAGE_CTX, szBuffer, ARRAYSIZE(szBuffer));
  297. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_NEXTPAGE, szBuffer);
  298. }
  299. if (MPCMD_FIRSTPAGE != dwMultiPage)
  300. {
  301. LoadString(_Module.GetModuleInstance(), IDS_PREVPAGE_CTX, szBuffer, ARRAYSIZE(szBuffer));
  302. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_PREVPAGE, szBuffer);
  303. }
  304. }
  305. }
  306. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  307. if (_fIncludeSetWallpaper)
  308. {
  309. if (PHOTOVERBS_ICON != dwMode)
  310. {
  311. LoadString(_Module.GetModuleInstance(), IDS_WALLPAPER_CTX, szBuffer, ARRAYSIZE(szBuffer));
  312. InsertMenu(hMenu, uIndex++, MF_BYPOSITION | MF_STRING, uIDFirst + OFFSET_SETWALL, szBuffer);
  313. }
  314. }
  315. }
  316. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, OFFSET_MAX);
  317. }
  318. // IDropTarget::DragEnter
  319. HRESULT CPhotoVerbs::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  320. {
  321. *pdwEffect = DROPEFFECT_COPY;
  322. return S_OK;;
  323. }
  324. // IDropTarget::DragOver
  325. HRESULT CPhotoVerbs::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  326. {
  327. *pdwEffect = DROPEFFECT_COPY;
  328. return S_OK;;
  329. }
  330. // IDropTarget::DragLeave
  331. HRESULT CPhotoVerbs::DragLeave(void)
  332. {
  333. return S_OK;
  334. }
  335. // IDropTarget::DragDrop
  336. HRESULT CPhotoVerbs::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  337. {
  338. *pdwEffect = DROPEFFECT_COPY;
  339. HRESULT hr = Initialize(NULL, pdtobj, NULL);
  340. if (SUCCEEDED(hr))
  341. {
  342. // we may need to get the verb.
  343. _OpenPictures();
  344. }
  345. return hr;
  346. }
  347. class VerbThreadProc : public NonATLObject
  348. {
  349. public:
  350. STDMETHOD_(ULONG, AddRef)();
  351. STDMETHOD_(ULONG, Release)();
  352. BOOL CreateVerbThread();
  353. VerbThreadProc(IDataObject *pdo, IUnknown *punk, HRESULT *phr);
  354. protected:
  355. virtual DWORD VerbWithThreadRefCB() PURE;
  356. virtual DWORD VerbWithThreadRef() PURE;
  357. virtual DWORD VerbWithoutThreadRefCB() PURE;
  358. virtual DWORD VerbWithoutThreadRef() PURE;
  359. virtual ~VerbThreadProc();
  360. IDataObject *_pdo; // the un-marshalled versions...
  361. IFolderView *_pfv;
  362. private:
  363. static DWORD s_WithThreadRef(void *pv);
  364. static DWORD s_WithThreadRefCB(void *pv);
  365. static DWORD s_WithoutThreadRef(void *pv);
  366. static DWORD s_WithoutThreadRefCB(void *pv);
  367. void Unmarshall();
  368. LONG _cRef;
  369. IStream *_pstmDataObj; // the marshalled IDataObject stream
  370. IStream *_pstmFolderView; // the marshalled IFolderView stream
  371. };
  372. VerbThreadProc::VerbThreadProc(IDataObject* pdo, IUnknown *punk, HRESULT *phr)
  373. {
  374. _cRef = 1;
  375. if (punk)
  376. {
  377. IFolderView *pfv = NULL;
  378. if (SUCCEEDED(IUnknown_QueryService(punk, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv))))
  379. {
  380. CoMarshalInterThreadInterfaceInStream(IID_IFolderView, pfv, &_pstmFolderView);
  381. pfv->Release();
  382. }
  383. }
  384. if (pdo)
  385. {
  386. CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdo, &_pstmDataObj);
  387. }
  388. *phr = (_pstmDataObj || _pstmFolderView) ? S_OK : E_OUTOFMEMORY;
  389. }
  390. VerbThreadProc::~VerbThreadProc()
  391. {
  392. ATOMICRELEASE(_pstmDataObj);
  393. ATOMICRELEASE(_pstmFolderView);
  394. ATOMICRELEASE(_pdo);
  395. ATOMICRELEASE(_pfv);
  396. }
  397. STDMETHODIMP_(ULONG) VerbThreadProc::AddRef()
  398. {
  399. return InterlockedIncrement(&_cRef);
  400. }
  401. STDMETHODIMP_(ULONG) VerbThreadProc::Release()
  402. {
  403. if (InterlockedDecrement(&_cRef))
  404. return _cRef;
  405. delete this;
  406. return 0;
  407. }
  408. DWORD VerbThreadProc::s_WithThreadRefCB(void *pv)
  409. {
  410. VerbThreadProc *potd = (VerbThreadProc *)pv;
  411. potd->AddRef();
  412. potd->Unmarshall();
  413. return potd->VerbWithThreadRefCB();
  414. }
  415. DWORD VerbThreadProc::s_WithThreadRef(void *pv)
  416. {
  417. VerbThreadProc *potd = (VerbThreadProc *)pv;
  418. DWORD dw = potd->VerbWithThreadRef();
  419. potd->Release();
  420. return dw;
  421. }
  422. DWORD VerbThreadProc::s_WithoutThreadRefCB(void *pv)
  423. {
  424. VerbThreadProc *potd = (VerbThreadProc *)pv;
  425. potd->AddRef();
  426. potd->Unmarshall();
  427. return potd->VerbWithoutThreadRefCB();
  428. }
  429. DWORD VerbThreadProc::s_WithoutThreadRef(void *pv)
  430. {
  431. VerbThreadProc *potd = (VerbThreadProc *)pv;
  432. DWORD dw = potd->VerbWithoutThreadRef();
  433. potd->Release();
  434. return dw;
  435. }
  436. void VerbThreadProc::Unmarshall()
  437. {
  438. if (_pstmDataObj)
  439. {
  440. CoGetInterfaceAndReleaseStream(_pstmDataObj, IID_PPV_ARG(IDataObject, &_pdo));
  441. _pstmDataObj = NULL;
  442. }
  443. if (_pstmFolderView)
  444. {
  445. CoGetInterfaceAndReleaseStream(_pstmFolderView, IID_PPV_ARG(IFolderView, &_pfv));
  446. _pstmFolderView = NULL;
  447. }
  448. }
  449. BOOL VerbThreadProc::CreateVerbThread()
  450. {
  451. BOOL bRet;
  452. // The thread ref is the more efficient start-up method, but we need to
  453. // handle the case where the caller doesn't have one.
  454. bRet = SHCreateThread(s_WithThreadRef, this, CTF_COINIT | CTF_THREAD_REF, s_WithThreadRefCB);
  455. if (!bRet)
  456. {
  457. bRet = SHCreateThread(s_WithoutThreadRef, this, CTF_COINIT | CTF_WAIT_ALLOWCOM, s_WithoutThreadRefCB);
  458. }
  459. return bRet;
  460. }
  461. class OpenThreadProc : public VerbThreadProc
  462. {
  463. public:
  464. DWORD VerbWithThreadRefCB();
  465. DWORD VerbWithThreadRef();
  466. DWORD VerbWithoutThreadRefCB();
  467. DWORD VerbWithoutThreadRef();
  468. OpenThreadProc(IDataObject *pdo, IUnknown *punk, HRESULT *phr) : VerbThreadProc(pdo, punk, phr) {};
  469. private:
  470. HRESULT Walk();
  471. void Preview();
  472. CPreviewWnd* _pPreview;
  473. };
  474. DWORD OpenThreadProc::VerbWithThreadRefCB()
  475. {
  476. return 0;
  477. }
  478. DWORD OpenThreadProc::VerbWithThreadRef()
  479. {
  480. HRESULT hr = Walk();
  481. SHReleaseThreadRef();
  482. if (S_OK == hr)
  483. {
  484. Preview();
  485. }
  486. return 0;
  487. }
  488. DWORD OpenThreadProc::VerbWithoutThreadRefCB()
  489. {
  490. Walk();
  491. return 0;
  492. }
  493. DWORD OpenThreadProc::VerbWithoutThreadRef()
  494. {
  495. Preview();
  496. return 0;
  497. }
  498. HRESULT OpenThreadProc::Walk()
  499. {
  500. HRESULT hr = E_OUTOFMEMORY;
  501. if (_pdo)
  502. {
  503. _pPreview = new CPreviewWnd();
  504. if (_pPreview)
  505. {
  506. if (!_pPreview->TryWindowReuse(_pdo))
  507. {
  508. hr = _pPreview->Initialize(NULL, WINDOW_MODE, FALSE);
  509. if (SUCCEEDED(hr))
  510. {
  511. // create the viewer window before doing the expensive namespace walk
  512. // so if a second instance is created it will find the window
  513. if (_pPreview->CreateViewerWindow())
  514. {
  515. hr = _pPreview->WalkItemsToPreview(_pfv ? (IUnknown *)_pfv: (IUnknown *)_pdo);
  516. if (_pfv && FAILED(hr))
  517. {
  518. hr = _pPreview->WalkItemsToPreview((IUnknown *)_pdo);
  519. }
  520. }
  521. else
  522. {
  523. DWORD dw = GetLastError();
  524. hr = HRESULT_FROM_WIN32(dw);
  525. }
  526. }
  527. }
  528. else
  529. {
  530. hr = S_FALSE;
  531. }
  532. }
  533. // We're done with these
  534. ATOMICRELEASE(_pdo);
  535. ATOMICRELEASE(_pfv);
  536. }
  537. return hr;
  538. }
  539. void OpenThreadProc::Preview()
  540. {
  541. if (_pPreview)
  542. {
  543. // viewer window should have been created by now
  544. _pPreview->PreviewItems();
  545. MSG msg;
  546. while (GetMessage(&msg, NULL, 0, 0) > 0)
  547. {
  548. TranslateMessage(&msg);
  549. DispatchMessage(&msg);
  550. }
  551. delete _pPreview;
  552. _pPreview = NULL;
  553. }
  554. }
  555. void CPhotoVerbs::_OpenPictures()
  556. {
  557. if (_pdtobj)
  558. {
  559. HRESULT hr;
  560. OpenThreadProc *potd = new OpenThreadProc(_pdtobj, _punkSite, &hr);
  561. if (potd)
  562. {
  563. if (SUCCEEDED(hr))
  564. {
  565. potd->CreateVerbThread();
  566. }
  567. potd->Release();
  568. }
  569. }
  570. }
  571. // implement the rotate verb, this is a lengthy operation so put it onto a background
  572. // thread if we can, marshall the IDataObject and let it do its thing...
  573. class CRotateThreadProc : public VerbThreadProc
  574. {
  575. public:
  576. DWORD VerbWithThreadRefCB() { return 0; }
  577. DWORD VerbWithThreadRef() { return _Rotate(); }
  578. DWORD VerbWithoutThreadRefCB() { return _Rotate(); }
  579. DWORD VerbWithoutThreadRef() { return 0; }
  580. CRotateThreadProc(IDataObject* pdo, int iAngle, UINT idPrompt, HRESULT *phr);
  581. private:
  582. DWORD _Rotate();
  583. int _iAngle;
  584. UINT _idPrompt;
  585. };
  586. CRotateThreadProc::CRotateThreadProc(IDataObject* pdo, int iAngle, UINT idPrompt, HRESULT *phr) :
  587. VerbThreadProc(pdo, NULL, phr)
  588. {
  589. _iAngle = iAngle;
  590. _idPrompt = idPrompt;
  591. }
  592. DWORD CRotateThreadProc::_Rotate()
  593. {
  594. FORMATETC fmt = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  595. STGMEDIUM medium = {0};
  596. if (_pdo)
  597. {
  598. HRESULT hr = _pdo->GetData(&fmt, &medium);
  599. if (SUCCEEDED(hr))
  600. {
  601. IProgressDialog *ppd;
  602. hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC, IID_PPV_ARG(IProgressDialog, &ppd));
  603. if (SUCCEEDED(hr))
  604. {
  605. TCHAR szBuffer[MAX_PATH];
  606. TCHAR szFile[MAX_PATH];
  607. HDROP hd = (HDROP)medium.hGlobal;
  608. UINT cItems = DragQueryFile(hd, (UINT)-1, NULL, 0);
  609. // prime the progress dialog
  610. if (cItems > 1)
  611. {
  612. LoadString(_Module.GetModuleInstance(), IDS_ROTATETITLE, szBuffer, ARRAYSIZE(szBuffer));
  613. ppd->SetLine(1, T2W(szBuffer), FALSE, NULL);
  614. LoadString(_Module.GetModuleInstance(), _idPrompt, szBuffer, ARRAYSIZE(szBuffer));
  615. ppd->SetTitle(T2W(szBuffer));
  616. ppd->SetAnimation(_Module.GetModuleInstance(), IDA_ROTATEAVI);
  617. ppd->StartProgressDialog(NULL, NULL, PROGDLG_AUTOTIME, NULL);
  618. ppd->SetProgress(1, cItems);
  619. }
  620. // lets get GDI+, the encoder array and start messing with the bits. this is a
  621. // sync operation so check for the user cancelling the UI accordingly.
  622. IShellImageDataFactory *pif;
  623. hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC, IID_PPV_ARG(IShellImageDataFactory, &pif));
  624. if (SUCCEEDED(hr))
  625. {
  626. for (UINT i = 0; (i != cItems) && !((cItems > 1) ? ppd->HasUserCancelled() : FALSE); i++)
  627. {
  628. if (DragQueryFile(hd, i, szFile, ARRAYSIZE(szFile)))
  629. {
  630. if (cItems > 1)
  631. {
  632. ppd->SetLine(2, T2W(szFile), TRUE, NULL);
  633. ppd->SetProgress(i+1, cItems);
  634. }
  635. // construct an image object from the file, rotate it and save it back
  636. IShellImageData *pid;
  637. hr = pif->CreateImageFromFile(szFile, &pid);
  638. if (SUCCEEDED(hr))
  639. {
  640. hr = pid->Decode(SHIMGDEC_DEFAULT,0,0);
  641. if (SUCCEEDED(hr))
  642. {
  643. if (!((cItems > 1) ? ppd->HasUserCancelled() : FALSE))
  644. {
  645. GUID guidFormat;
  646. SIZE sz;
  647. if ( SUCCEEDED(pid->GetRawDataFormat(&guidFormat)) &&
  648. SUCCEEDED(pid->GetSize(&sz)))
  649. {
  650. if (S_OK == pid->IsEditable())
  651. {
  652. hr = S_OK;
  653. if (::IsEqualGUID(ImageFormatJPEG, guidFormat))
  654. {
  655. if ((sz.cx % 16) || (sz.cy % 16))
  656. {
  657. if (cItems > 1)
  658. {
  659. LoadString(_Module.GetModuleInstance(), IDS_ROTATEDLGTITLE, szBuffer, ARRAYSIZE(szBuffer));
  660. ppd->SetLine(1, T2W(szBuffer), FALSE, NULL);
  661. }
  662. TCHAR szTitle[MAX_PATH];
  663. TCHAR szText[1024];
  664. LoadString(_Module.GetModuleInstance(), IDS_ROTATE_LOSS, szText, ARRAYSIZE(szText));
  665. LoadString(_Module.GetModuleInstance(), IDS_ROTATE_CAPTION, szTitle, ARRAYSIZE(szTitle));
  666. // Set default to return IDOK so we know if the user selected something or
  667. // if the "don't show me this again" bit was respected
  668. int nResult = SHMessageBoxCheck(NULL, szText, szTitle,
  669. MB_YESNO|MB_ICONWARNING, IDOK, REGSTR_LOSSYROTATE);
  670. CRegKey Key;
  671. if (ERROR_SUCCESS != Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
  672. {
  673. Key.Create(HKEY_CURRENT_USER, REGSTR_SHIMGVW);
  674. }
  675. if (Key.m_hKey != NULL)
  676. {
  677. if (nResult == IDOK) // If hidden, then load last result from registry
  678. {
  679. DWORD dwResult = 0;
  680. Key.QueryValue(dwResult, REGSTR_LOSSYROTATE);
  681. nResult = (int)dwResult;
  682. }
  683. else // Otherwise, write this as last result to registry
  684. {
  685. DWORD dwResult = (DWORD)nResult;
  686. Key.SetValue(dwResult, REGSTR_LOSSYROTATE);
  687. }
  688. }
  689. if (nResult == IDNO)
  690. hr = S_FALSE; // User said No, Don't make any other noise.
  691. if (cItems > 1)
  692. {
  693. LoadString(_Module.GetModuleInstance(), IDS_ROTATETITLE, szBuffer, ARRAYSIZE(szBuffer));
  694. ppd->SetLine(1, T2W(szBuffer), FALSE, NULL);
  695. }
  696. }
  697. }
  698. if (hr == S_OK)
  699. {
  700. CAnnotationSet Annotations;
  701. Annotations.SetImageData(pid);
  702. INT_PTR nCount = Annotations.GetCount();
  703. for (INT_PTR ix = 0; ix < nCount; ix++)
  704. {
  705. CAnnotation* pAnnotation = Annotations.GetAnnotation(ix);
  706. pAnnotation->Rotate(sz.cy, sz.cx, (_iAngle == 90));
  707. }
  708. Annotations.CommitAnnotations(pid);
  709. hr = pid->Rotate(_iAngle);
  710. if (SUCCEEDED(hr))
  711. {
  712. IPersistFile *ppf;
  713. hr = pid->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  714. if (SUCCEEDED(hr))
  715. {
  716. hr = ppf->Save(NULL, TRUE);
  717. ppf->Release();
  718. }
  719. }
  720. }
  721. }
  722. else
  723. {
  724. // Animated GIFs are not editable even though
  725. // normal GIFs are. This can cause a lot of
  726. // confusion, so provide some feedback if the
  727. // user tries to rotate an animated image.
  728. if (S_OK == pid->IsAnimated())
  729. {
  730. // Make some noise.
  731. ShellMessageBox(_Module.GetModuleInstance(), NULL, MAKEINTRESOURCE(IDS_ROTATE_MESSAGE), MAKEINTRESOURCE(IDS_PROJNAME), MB_OK | MB_ICONERROR, szFile);
  732. // Don't make any other noise.
  733. hr = S_FALSE;
  734. }// we can't safely rotate images with > 8 bits per channel either; we'd lose the extra bits
  735. else if (S_OK != pid->IsEditable())
  736. {
  737. TCHAR szMsg[MAX_PATH];
  738. // silently fail if the string isn't available
  739. if (LoadSPString(IDS_SHIMGVW_ROTATE_MESSAGE_EXT, szMsg, ARRAYSIZE(szMsg)))
  740. {
  741. ShellMessageBox(_Module.GetModuleInstance(), NULL, szMsg, MAKEINTRESOURCE(IDS_PROJNAME), MB_OK | MB_ICONERROR, szFile);
  742. }
  743. // Don't make any other noise.
  744. hr = S_FALSE;
  745. }
  746. }
  747. }
  748. }
  749. }
  750. pid->Release();
  751. }
  752. if (FAILED(hr))
  753. {
  754. if (cItems > 1)
  755. {
  756. LoadString(_Module.GetModuleInstance(), IDS_ROTATEDLGTITLE, szBuffer, ARRAYSIZE(szBuffer));
  757. ppd->SetLine(1, T2W(szBuffer), FALSE, NULL);
  758. }
  759. ShellMessageBox(_Module.GetModuleInstance(), NULL, MAKEINTRESOURCE(IDS_ROTATE_ERROR), MAKEINTRESOURCE(IDS_ROTATE_CAPTION), MB_OK|MB_ICONERROR);
  760. if (cItems > 1)
  761. {
  762. LoadString(_Module.GetModuleInstance(), IDS_ROTATETITLE, szBuffer, ARRAYSIZE(szBuffer));
  763. ppd->SetLine(1, T2W(szBuffer), FALSE, NULL);
  764. }
  765. }
  766. }
  767. }
  768. pif->Release();
  769. }
  770. if (cItems > 1)
  771. {
  772. ppd->StopProgressDialog();
  773. }
  774. // Since we always create it, we must always Release it.
  775. ppd->Release();
  776. }
  777. ReleaseStgMedium(&medium);
  778. }
  779. }
  780. return 0;
  781. }
  782. void CPhotoVerbs::_RotatePictures(int iAngle, UINT idPrompt)
  783. {
  784. if (_pict)
  785. {
  786. _pict->Rotate(iAngle);
  787. }
  788. else if (_pdtobj)
  789. {
  790. HRESULT hr;
  791. CRotateThreadProc *potd = new CRotateThreadProc(_pdtobj, iAngle, idPrompt, &hr);
  792. if (potd)
  793. {
  794. if (SUCCEEDED(hr))
  795. {
  796. potd->CreateVerbThread();
  797. }
  798. potd->Release();
  799. }
  800. }
  801. }
  802. DWORD CALLBACK _WallpaperThreadProc(void *pv)
  803. {
  804. IStream *pstm = (IStream*)pv;
  805. IDataObject *pdtobj;
  806. HRESULT hr = CoGetInterfaceAndReleaseStream(pstm, IID_PPV_ARG(IDataObject, &pdtobj));
  807. if (SUCCEEDED(hr))
  808. {
  809. FORMATETC fmt = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  810. STGMEDIUM medium = {0};
  811. hr = pdtobj->GetData(&fmt, &medium);
  812. if (SUCCEEDED(hr))
  813. {
  814. TCHAR szPath[MAX_PATH];
  815. HDROP hd = (HDROP)medium.hGlobal;
  816. if (DragQueryFile(hd, 0, szPath, ARRAYSIZE(szPath))) // only set the first one selected as the background
  817. {
  818. SetWallpaperHelper(szPath);
  819. }
  820. ReleaseStgMedium(&medium);
  821. }
  822. pdtobj->Release();
  823. }
  824. return 0;
  825. }
  826. void CPhotoVerbs::_SetWallpaper()
  827. {
  828. if (_pdtobj)
  829. {
  830. IStream *pstm;
  831. if (FAILED(CoMarshalInterThreadInterfaceInStream(IID_IDataObject, _pdtobj, &pstm)) ||
  832. !SHCreateThread(_WallpaperThreadProc, pstm, CTF_COINIT, NULL))
  833. {
  834. ATOMICRELEASE(pstm);
  835. }
  836. }
  837. }
  838. HRESULT _InvokePrintToInPPW(LPCMINVOKECOMMANDINFO pCMI,IDataObject * pdtobj)
  839. {
  840. HRESULT hr = E_FAIL;
  841. HMODULE hDll = LoadLibrary( TEXT("photowiz.dll") );
  842. if (hDll)
  843. {
  844. LPFNPPWPRINTTO pfnPrintTo = (LPFNPPWPRINTTO)GetProcAddress( hDll, PHOTO_PRINT_WIZARD_PRINTTO_ENTRY );
  845. if (pfnPrintTo)
  846. {
  847. hr = pfnPrintTo( pCMI, pdtobj );
  848. }
  849. FreeLibrary( hDll );
  850. }
  851. return hr;
  852. }
  853. const struct
  854. {
  855. LPCSTR pszVerb;
  856. int idVerb;
  857. }
  858. c_szVerbs[] =
  859. {
  860. { "preview", OFFSET_OPEN},
  861. { "printto", OFFSET_PRINTTO},
  862. { "rotate90", OFFSET_ROT90},
  863. { "rotate270", OFFSET_ROT270},
  864. };
  865. HRESULT CPhotoVerbs::_MapVerb(LPCMINVOKECOMMANDINFO pici, int *pidVerb)
  866. {
  867. HRESULT hr = S_OK;
  868. if (IS_INTRESOURCE(pici->lpVerb))
  869. {
  870. *pidVerb = LOWORD(pici->lpVerb);
  871. }
  872. else
  873. {
  874. hr = E_INVALIDARG;
  875. for (int i = 0; i < ARRAYSIZE(c_szVerbs); i++)
  876. {
  877. if (0 == lstrcmpiA(pici->lpVerb, c_szVerbs[i].pszVerb))
  878. {
  879. hr = S_OK;
  880. *pidVerb = c_szVerbs[i].idVerb;
  881. break;
  882. }
  883. }
  884. }
  885. return hr;
  886. }
  887. STDMETHODIMP CPhotoVerbs::InvokeCommand(LPCMINVOKECOMMANDINFO pCMI)
  888. {
  889. int idVerb;
  890. HRESULT hr = _MapVerb(pCMI, &idVerb);
  891. if (SUCCEEDED(hr))
  892. {
  893. switch (idVerb)
  894. {
  895. case OFFSET_OPEN:
  896. if (_fAcceptPreview)
  897. _OpenPictures();
  898. else
  899. hr = E_FAIL;
  900. break;
  901. case OFFSET_PRINTTO:
  902. hr = _InvokePrintToInPPW(pCMI,_pdtobj);
  903. break;
  904. case OFFSET_ROT90:
  905. _RotatePictures(90, IDS_ROTATE90);
  906. break;
  907. case OFFSET_ROT270:
  908. _RotatePictures(270, IDS_ROTATE270);
  909. break;
  910. case OFFSET_ZOOMIN:
  911. if (_pict)
  912. {
  913. _pict->ZoomIn();
  914. }
  915. break;
  916. case OFFSET_ZOOMOUT:
  917. if (_pict)
  918. {
  919. _pict->ZoomOut();
  920. }
  921. break;
  922. case OFFSET_ACTUALSIZE:
  923. if (_pict)
  924. {
  925. _pict->ActualSize();
  926. }
  927. break;
  928. case OFFSET_BESTFIT:
  929. if (_pict)
  930. {
  931. _pict->BestFit();
  932. }
  933. break;
  934. case OFFSET_NEXTPAGE:
  935. if (_pict)
  936. {
  937. _pict->NextPage();
  938. }
  939. break;
  940. case OFFSET_PREVPAGE:
  941. if (_pict)
  942. {
  943. _pict->PreviousPage();
  944. }
  945. break;
  946. case OFFSET_SETWALL:
  947. _SetWallpaper();
  948. break;
  949. default:
  950. hr = E_INVALIDARG;
  951. break;
  952. }
  953. }
  954. return hr;
  955. }
  956. STDMETHODIMP CPhotoVerbs::GetCommandString(UINT_PTR uID, UINT uFlags, UINT *res, LPSTR pName, UINT cchMax)
  957. {
  958. HRESULT hr = S_OK;
  959. UINT idSel = (UINT)uID;
  960. switch (uFlags)
  961. {
  962. case GCS_VERBW:
  963. case GCS_VERBA:
  964. if (idSel < ARRAYSIZE(c_szVerbs))
  965. {
  966. if (uFlags == GCS_VERBW)
  967. {
  968. SHAnsiToUnicode(c_szVerbs[idSel].pszVerb, (LPWSTR)pName, cchMax);
  969. }
  970. else
  971. {
  972. StrCpyNA(pName, c_szVerbs[idSel].pszVerb, cchMax);
  973. }
  974. }
  975. break;
  976. case GCS_HELPTEXTW:
  977. LoadStringW(_Module.GetResourceInstance(), idSel+IDH_HELP_FIRST, (LPWSTR)pName, cchMax);
  978. break;
  979. case GCS_HELPTEXTA:
  980. LoadStringA(_Module.GetResourceInstance(), idSel+IDH_HELP_FIRST, (LPSTR)pName, cchMax);
  981. break;
  982. case GCS_VALIDATEA:
  983. case GCS_VALIDATEW:
  984. hr = E_NOTIMPL;
  985. break;
  986. }
  987. return hr;
  988. }
  989. void WINAPI ImageView_Fullscreen(HWND hwnd, HINSTANCE hAppInstance, LPTSTR pszCmdLine, int nCmdShow)
  990. {
  991. HRESULT hr = SHCoInitialize(); // suppress OLE1 DDE window
  992. if (SUCCEEDED(hr))
  993. {
  994. OleInitialize(NULL); // needed to get drag and drop to work
  995. IDataObject *pdtobj;
  996. hr = GetUIObjectFromPath(pszCmdLine, IID_PPV_ARG(IDataObject, &pdtobj));
  997. if (SUCCEEDED(hr))
  998. {
  999. // this scope is required to make sure cwndPreview gets destroyed before we call SHCoUninitialize
  1000. // the preview wnd will init GDI+ too
  1001. CPreviewWnd cwndPreview;
  1002. if (!cwndPreview.TryWindowReuse(pszCmdLine))
  1003. {
  1004. if (SUCCEEDED(cwndPreview.Initialize(NULL, WINDOW_MODE, FALSE)) && cwndPreview.CreateViewerWindow())
  1005. {
  1006. cwndPreview.PreviewItemsFromUnk(pdtobj);
  1007. MSG msg;
  1008. while (GetMessage(&msg, NULL, 0, 0))
  1009. {
  1010. TranslateMessage(&msg);
  1011. DispatchMessage(&msg);
  1012. }
  1013. }
  1014. }
  1015. pdtobj->Release();
  1016. }
  1017. OleUninitialize();
  1018. }
  1019. SHCoUninitialize(hr);
  1020. }
  1021. void WINAPI ImageView_FullscreenA(HWND hwnd, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  1022. {
  1023. TCHAR szCmdLine[MAX_PATH*2];
  1024. SHAnsiToTChar(pszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  1025. ImageView_Fullscreen(hwnd, hAppInstance, szCmdLine, nCmdShow);
  1026. }
  1027. void WINAPI ImageView_FullscreenW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR pszCmdLine, int nCmdShow)
  1028. {
  1029. ImageView_Fullscreen(hwnd, hAppInstance, pszCmdLine, nCmdShow);
  1030. }
  1031. // To work around ACDSEE lower cases the shell command stuff causing us to need this
  1032. // export an all lowercase version of this function. The short is, case matters for RunDLL32 exports.
  1033. void WINAPI imageview_fullscreenW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR pszCmdLine, int nCmdShow)
  1034. {
  1035. ImageView_FullscreenW(hwnd, hAppInstance, pszCmdLine, nCmdShow);
  1036. }
  1037. LPTSTR ParseCmdLine( LPTSTR pInput, LPTSTR pOutput, BOOL bStripQuotes )
  1038. {
  1039. // copies the next token on the line to pOutput and returns
  1040. // the first white space character after the processed token
  1041. if (!pInput || (!*pInput) || !pOutput)
  1042. {
  1043. return pInput;
  1044. }
  1045. // first, skip any leading whitespace
  1046. while (*pInput == TEXT(' '))
  1047. {
  1048. pInput++;
  1049. }
  1050. if (!(*pInput))
  1051. {
  1052. return pInput;
  1053. }
  1054. // next, start copying token
  1055. // if the token starts with a
  1056. // quote, note that and copy it
  1057. BOOL bStartedWithQuote = FALSE;
  1058. if (*pInput == TEXT('\"'))
  1059. {
  1060. bStartedWithQuote = TRUE;
  1061. if (bStripQuotes)
  1062. {
  1063. pInput++;
  1064. }
  1065. else
  1066. {
  1067. *pOutput++ = *pInput++;
  1068. }
  1069. }
  1070. // figure out what to stop on
  1071. TCHAR cStopChar;
  1072. if (bStartedWithQuote)
  1073. {
  1074. cStopChar = TEXT('\"');
  1075. }
  1076. else
  1077. {
  1078. cStopChar = TEXT(' ');
  1079. }
  1080. // copy up to the delimeter
  1081. while( *pInput && (*pInput != cStopChar))
  1082. {
  1083. *pOutput++ = *pInput++;
  1084. }
  1085. // if the delimeter was a quote
  1086. // we need to copy it into the output
  1087. if (bStartedWithQuote && (*pInput == TEXT('\"')))
  1088. {
  1089. if (bStripQuotes)
  1090. {
  1091. pInput++;
  1092. }
  1093. else
  1094. {
  1095. *pOutput++ = *pInput++;
  1096. }
  1097. }
  1098. *pOutput = 0;
  1099. return pInput;
  1100. }
  1101. void WINAPI ImageView_PrintTo(HWND hwnd, HINSTANCE hAppInstance, LPTSTR pszCmdLine, int nCmdShow)
  1102. {
  1103. // The command line comes to us like this (everything inside the <>):
  1104. // </pt filename printer_name>
  1105. TCHAR szFileName[ 1024 ];
  1106. TCHAR szPrinterName[ 1024 ];
  1107. LPTSTR psz = pszCmdLine;
  1108. if (*psz == TEXT('/'))
  1109. {
  1110. // skip the "/pt"
  1111. psz = ParseCmdLine( psz, szFileName, TRUE );
  1112. }
  1113. // Get the filename
  1114. psz = ParseCmdLine( psz, szFileName, TRUE );
  1115. // Get the printer name
  1116. psz = ParseCmdLine( psz, szPrinterName, TRUE );
  1117. // create a dataobject for the file in question, and then call
  1118. // into photowiz to print it out...
  1119. HRESULT hrInit = SHCoInitialize();
  1120. if (SUCCEEDED(hrInit))
  1121. {
  1122. IDataObject *pdtobj;
  1123. HRESULT hr = GetUIObjectFromPath(szFileName, IID_PPV_ARG(IDataObject, &pdtobj));
  1124. if (SUCCEEDED(hr))
  1125. {
  1126. // Create CMINVOKECAMMANDINFO to pass to photowiz
  1127. CMINVOKECOMMANDINFOEX cmi = {0};
  1128. cmi.cbSize = sizeof(cmi);
  1129. cmi.fMask = CMIC_MASK_UNICODE;
  1130. cmi.lpVerbW = L"printto";
  1131. cmi.lpParametersW = szPrinterName;
  1132. hr = _InvokePrintToInPPW((LPCMINVOKECOMMANDINFO )&cmi, pdtobj);
  1133. pdtobj->Release();
  1134. }
  1135. }
  1136. SHCoUninitialize(hrInit);
  1137. }
  1138. void WINAPI ImageView_PrintToA(HWND hwnd, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
  1139. {
  1140. TCHAR szCmdLine[1024];
  1141. SHAnsiToTChar(pszCmdLine, szCmdLine, ARRAYSIZE(szCmdLine));
  1142. ImageView_PrintTo(hwnd, hAppInstance, szCmdLine, nCmdShow);
  1143. }
  1144. void WINAPI ImageView_PrintToW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR pszCmdLine, int nCmdShow)
  1145. {
  1146. ImageView_PrintTo( hwnd, hAppInstance, pszCmdLine, nCmdShow );
  1147. }