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