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.

687 lines
22 KiB

  1. /*-----------------------------------------------------------------------------
  2. *
  3. * File: wiaproto.cpp
  4. * Author: Samuel Clement (samclem)
  5. * Date: Fri Aug 27 15:16:44 1999
  6. *
  7. * Copyright (c) 1999 Microsoft Corporation
  8. *
  9. * Description:
  10. * This contains the implementation of the "wia" internet protocol. This
  11. * is a pluggable protocol that handles downloading thumbnails from a wia
  12. * device.
  13. *
  14. * History:
  15. * 27 Aug 1999: Created.
  16. *----------------------------------------------------------------------------*/
  17. #include "stdafx.h"
  18. // declare some debugging tags
  19. DeclareTag( tagWiaProto, "!WiaProto", "Wia Protocol debug information" );
  20. const WCHAR* k_wszProtocolName = L"wia";
  21. const WCHAR* k_wszColonSlash = L":///";
  22. const WCHAR* k_wszSeperator = L"?";
  23. const WCHAR* k_wszThumb = L"thumb";
  24. const WCHAR* k_wszExtension = L".bmp";
  25. const int k_cchProtocolName = 3;
  26. const int z_cchThumb = 5;
  27. const WCHAR k_wchSeperator = L'?';
  28. const WCHAR k_wchColon = L':';
  29. const WCHAR k_wchFrontSlash = L'/';
  30. const WCHAR k_wchPeriod = L'.';
  31. const WCHAR k_wchEOS = L'\0';
  32. enum
  33. {
  34. k_dwTransferPending = 0,
  35. k_dwTransferComplete = 1,
  36. };
  37. /*-----------------------------------------------------------------------------
  38. * CWiaProtocol
  39. *
  40. * Create a new CWiaProtocol. This simply initializes all the members to
  41. * a known state so that we can then test against them
  42. *--(samclem)-----------------------------------------------------------------*/
  43. CWiaProtocol::CWiaProtocol()
  44. : m_pFileItem( NULL ), m_ulOffset( 0 )
  45. {
  46. TRACK_OBJECT( "CWiaProtocol" );
  47. m_pd.dwState = k_dwTransferPending;
  48. }
  49. /*-----------------------------------------------------------------------------
  50. * CWiaProtocol::FinalRelease
  51. *
  52. * Called when we are finally released to cleanup any resources that we
  53. * want to cleanup.
  54. *--(samclem)-----------------------------------------------------------------*/
  55. STDMETHODIMP_(void)
  56. CWiaProtocol::FinalRelease()
  57. {
  58. if ( m_pFileItem )
  59. m_pFileItem->Release();
  60. m_pFileItem = NULL;
  61. }
  62. /*-----------------------------------------------------------------------------
  63. *
  64. *--(samclem)-----------------------------------------------------------------*/
  65. STDMETHODIMP
  66. CWiaProtocol::Start( LPCWSTR szUrl, IInternetProtocolSink* pOIProtSink,
  67. IInternetBindInfo* pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved )
  68. {
  69. CWiaCacheManager* pCache= CWiaCacheManager::GetInstance();
  70. CComPtr<IWiaItem> pDevice;
  71. CComBSTR bstrDeviceId ;
  72. CComBSTR bstrItem ;
  73. TTPARAMS* pParams = NULL;
  74. HANDLE hThread = NULL;
  75. DWORD dwThreadId = 0;
  76. LONG lItemType = 0;
  77. BYTE* pbThumb = NULL;
  78. DWORD cbThumb = 0;
  79. HRESULT hr;
  80. // the first thing that we want to do is to attempt to crack the URL,
  81. // this can be an involved process so we have a helper method that
  82. // handles doing this for us.
  83. hr = THR( CrackURL( szUrl, &bstrDeviceId, &bstrItem ) );
  84. if ( FAILED( hr ) )
  85. goto Cleanup;
  86. // do we already have a cached version of this item, if so we can avoid
  87. // having to do anything else
  88. if ( pCache->GetThumbnail( bstrItem, &pbThumb, &cbThumb ) )
  89. {
  90. TraceTag((tagWiaProto, "Using cached thumbnail" ));
  91. m_pd.pData = pbThumb;
  92. m_pd.cbData = cbThumb;
  93. m_pd.dwState = k_dwTransferComplete;
  94. hr = THR( pOIProtSink->ReportData( BSCF_LASTDATANOTIFICATION, cbThumb, cbThumb ) );
  95. if ( FAILED( hr ) )
  96. goto Cleanup;
  97. hr = THR( pOIProtSink->ReportResult( hr, hr, NULL ) );
  98. if ( FAILED( hr ) )
  99. goto Cleanup;
  100. }
  101. else
  102. {
  103. if ( !pCache->GetDevice( bstrDeviceId, &pDevice ) )
  104. {
  105. hr = THR( CreateDevice( bstrDeviceId, &pDevice ) );
  106. if ( FAILED( hr ) )
  107. goto Cleanup;
  108. pCache->AddDevice( bstrDeviceId, pDevice );
  109. }
  110. else
  111. {
  112. TraceTag((tagWiaProto, "Using cached device pointer" ));
  113. }
  114. hr = THR( pDevice->FindItemByName( 0, bstrItem, &m_pFileItem ) );
  115. if ( FAILED( hr ) || S_FALSE == hr )
  116. {
  117. TraceTag((tagWiaProto, "unable to locate item: %S", bstrItem ));
  118. hr = INET_E_RESOURCE_NOT_FOUND;
  119. goto Cleanup;
  120. }
  121. // the last thing we want to verify is that the item is an image
  122. // and a file, otherwise we don't want anything to do with it
  123. hr = THR( m_pFileItem->GetItemType( &lItemType ) );
  124. if ( !( lItemType & WiaItemTypeFile ) &&
  125. !( lItemType & WiaItemTypeImage ) )
  126. {
  127. TraceTag((tagWiaProto, "unsupported wia item type for download" ));
  128. hr = INET_E_INVALID_REQUEST;
  129. goto Cleanup;
  130. }
  131. // at this point everything is happy in our land. we have a valid
  132. // thing to download from. We now need to create the thread which
  133. // will do the main work
  134. pParams = reinterpret_cast<TTPARAMS*>(CoTaskMemAlloc( sizeof( TTPARAMS ) ) );
  135. if ( !pParams )
  136. {
  137. hr = E_OUTOFMEMORY;
  138. goto Cleanup;
  139. }
  140. hr = THR( CoMarshalInterThreadInterfaceInStream(
  141. IID_IWiaItem,
  142. m_pFileItem,
  143. &pParams->pStrm ) );
  144. if ( FAILED( hr ) )
  145. {
  146. TraceTag((tagWiaProto, "error marshalling interface" ));
  147. goto Cleanup;
  148. }
  149. pParams->pInetSink = pOIProtSink;
  150. pParams->pInetSink->AddRef();
  151. hThread = CreateThread( NULL,
  152. 0,
  153. CWiaProtocol::TransferThumbnail,
  154. pParams,
  155. 0,
  156. &dwThreadId );
  157. if ( NULL == hThread )
  158. {
  159. pParams->pInetSink->Release();
  160. pParams->pStrm->Release();
  161. CoTaskMemFree( pParams );
  162. hr = E_FAIL;
  163. goto Cleanup;
  164. }
  165. else
  166. {
  167. CloseHandle(hThread);
  168. }
  169. TraceTag((tagWiaProto, "Started transfer thread: id(%x)", dwThreadId ));
  170. }
  171. Cleanup:
  172. if ( FAILED( hr ) )
  173. {
  174. if ( m_pFileItem )
  175. m_pFileItem->Release();
  176. m_pFileItem = NULL;
  177. }
  178. return hr;
  179. }
  180. /*-----------------------------------------------------------------------------
  181. * CWiaProtocol::Continue
  182. *
  183. * This is called to pass data back from the the other threads. It lets
  184. * the controlling thread know we have data.
  185. *
  186. * Note: Copy the data from the pointer, DON'T use thier pointer, they will
  187. * free it following the return of this call.
  188. *--(samclem)-----------------------------------------------------------------*/
  189. STDMETHODIMP
  190. CWiaProtocol::Continue( PROTOCOLDATA* pProtocolData )
  191. {
  192. if ( k_dwTransferComplete == m_pd.dwState )
  193. return E_UNEXPECTED;
  194. memcpy( &m_pd, pProtocolData, sizeof( PROTOCOLDATA ) );
  195. return S_OK;
  196. }
  197. /*-----------------------------------------------------------------------------
  198. * CWiaProtocl::Abort
  199. *
  200. * This is called to abort our transfer. this is NYI. However, it would
  201. * need to kill our thread if it is still running and free our data. However,
  202. * it is perfectly harmless if the thread keeps running.
  203. *
  204. * hrReason: the reason for the abort
  205. * dwOptions: the options for this abourt
  206. *--(samclem)-----------------------------------------------------------------*/
  207. STDMETHODIMP
  208. CWiaProtocol::Abort( HRESULT hrReason, DWORD dwOptions )
  209. {
  210. TraceTag((tagWiaProto, "NYI: Abort hrReason=%hr", hrReason ));
  211. return E_NOTIMPL;
  212. }
  213. /*-----------------------------------------------------------------------------
  214. * CWiaProtocol::Terminate
  215. *
  216. * This is called when the transfer is finished. This is responsible for
  217. * cleaning anything up that we might need to do. We currently don't have
  218. * anything to clean up. So this simply returns S_OK.
  219. *--(samclem)-----------------------------------------------------------------*/
  220. STDMETHODIMP
  221. CWiaProtocol::Terminate( DWORD dwOptions )
  222. {
  223. // Nothing to do.
  224. return S_OK;
  225. }
  226. /*-----------------------------------------------------------------------------
  227. * CWiaProtocol::Suspend
  228. *
  229. * This is called to suspend the transfer. This is currently not implemenet
  230. * inside of trident, so our methods just return E_NOTIMPL
  231. *--(samclem)-----------------------------------------------------------------*/
  232. STDMETHODIMP
  233. CWiaProtocol::Suspend()
  234. {
  235. TraceTag((tagWiaProto, "NYI: Suspend" ));
  236. return E_NOTIMPL;
  237. }
  238. /*-----------------------------------------------------------------------------
  239. * CWiaProtocol::Resume
  240. *
  241. * This is called to resume a suspended transfer. This is not suppored
  242. * inside of URLMON, so we just return E_NOTIMPL
  243. *--(samclem)-----------------------------------------------------------------*/
  244. STDMETHODIMP
  245. CWiaProtocol::Resume()
  246. {
  247. TraceTag((tagWiaProto, "NYI: Resume" ));
  248. return E_NOTIMPL;
  249. }
  250. /*-----------------------------------------------------------------------------
  251. * CWiaProtocol::Read
  252. *
  253. * This is called to read data from our protocol. this copies cb bytes to
  254. * the buffer passed in. Or it will copy what ever we have.
  255. *
  256. * pv: the buffer that we want to copy the data to
  257. * cb: the size fo buffer, max bytes to copy
  258. * pcbRead: Out, the number of bytes that we actually copied to the buffer
  259. *--(samclem)-----------------------------------------------------------------*/
  260. STDMETHODIMP
  261. CWiaProtocol::Read( void* pv, ULONG cb, ULONG* pcbRead)
  262. {
  263. // validate our arguments
  264. if ( !pv || !pcbRead )
  265. return E_POINTER;
  266. *pcbRead = 0;
  267. // is the transfer currently pending? if so then
  268. // we don't actually want to do anything here.
  269. if ( k_dwTransferPending == m_pd.dwState )
  270. return E_PENDING;
  271. // do we actually have data to copy? if the offset is greater
  272. // or equal to the size of our data then we don't have an data to
  273. // copy so return S_FALSE
  274. if ( m_ulOffset >= m_pd.cbData )
  275. return S_FALSE;
  276. // figure out how much we are going to copy
  277. DWORD dwCopy = m_pd.cbData - m_ulOffset;
  278. if ( dwCopy >= cb )
  279. dwCopy = cb;
  280. // if we have negative memory to copy, or 0, then we are done and we don't
  281. // actually want to do anything besides return S_FALSE
  282. if ( dwCopy <= 0 )
  283. return S_FALSE;
  284. // do the memcpy and setup our state and the return value
  285. memcpy( pv, reinterpret_cast<BYTE*>(m_pd.pData) + m_ulOffset, dwCopy );
  286. m_ulOffset += dwCopy;
  287. *pcbRead = dwCopy;
  288. return ( dwCopy == cb ? S_OK : S_FALSE );
  289. }
  290. /*-----------------------------------------------------------------------------
  291. * CWiaProtocol::Seek
  292. *
  293. * Called to seek our data. However, we don't support seeking so this just
  294. * returns E_FAIL
  295. *
  296. * dlibMove: how far to move the offset
  297. * dwOrigin: indicates where the move shoudl begin
  298. * plibNewPosition: The new position of the offset
  299. *--(samclem)-----------------------------------------------------------------*/
  300. STDMETHODIMP
  301. CWiaProtocol::Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition )
  302. {
  303. // Don't support
  304. return E_FAIL;
  305. }
  306. /*-----------------------------------------------------------------------------
  307. * CWiaProtocol::LockRequest
  308. *
  309. * Called to lock the data. we don't need to lock our data, so this just
  310. * returns S_OK
  311. *
  312. * dwOptions: reserved, will be 0.
  313. *--(samclem)-----------------------------------------------------------------*/
  314. STDMETHODIMP
  315. CWiaProtocol::LockRequest( DWORD dwOptions )
  316. {
  317. //Don't support locking
  318. return S_OK;
  319. }
  320. /*-----------------------------------------------------------------------------
  321. * CWiaProtocol::UnlockRequest
  322. *
  323. * Called to unlock our data. We don't need or support locking, so this
  324. * doesn't do anything besides return S_OK.
  325. *--(samclem)-----------------------------------------------------------------*/
  326. STDMETHODIMP
  327. CWiaProtocol::UnlockRequest()
  328. {
  329. //Don't support locking
  330. return S_OK;
  331. }
  332. /*-----------------------------------------------------------------------------
  333. * CWiaProtocol::CrackURL
  334. *
  335. * This handles breaking appart a URL which is passed in to us. This will
  336. * return S_OK if it is a valid URL and we can work with it. otherwise this
  337. * will return INET_E_INVALID_URL
  338. *
  339. * bstrUrl: the full url to be cracked
  340. * pbstrDeviceId: Out, recieves the device id portion of the URL
  341. * pbstrItem: Out, recieves the item portion of the URL
  342. *--(samclem)-----------------------------------------------------------------*/
  343. HRESULT CWiaProtocol::CrackURL( CComBSTR bstrUrl, BSTR* pbstrDeviceId, BSTR* pbstrItem )
  344. {
  345. WCHAR* pwchUrl = reinterpret_cast<WCHAR*>((BSTR)bstrUrl);
  346. WCHAR* pwch = NULL;
  347. WCHAR awch[INTERNET_MAX_URL_LENGTH] = { 0 };
  348. HRESULT hr = INET_E_INVALID_URL;
  349. Assert( pbstrDeviceId && pbstrItem );
  350. *pbstrDeviceId = NULL;
  351. *pbstrItem = NULL;
  352. if (SysStringLen(bstrUrl) >= INTERNET_MAX_URL_LENGTH)
  353. goto Cleanup;
  354. /*
  355. * We are going to use the SHWAPI functions to parse this URL. Our format
  356. * is very simple.
  357. *
  358. * proto:///<deviceId>?<item>
  359. */
  360. if ( StrCmpNIW( k_wszProtocolName, pwchUrl, k_cchProtocolName ) )
  361. goto Cleanup;
  362. pwchUrl += k_cchProtocolName;
  363. while ( *pwchUrl == k_wchColon || *pwchUrl == k_wchFrontSlash )
  364. pwchUrl++;
  365. if ( !(*pwchUrl ) )
  366. goto Cleanup;
  367. // get the device portion of the URL
  368. pwch = StrChrIW( pwchUrl, k_wchSeperator );
  369. if ( !pwch )
  370. goto Cleanup;
  371. StrCpyNW( awch, pwchUrl, ( pwch - pwchUrl + 1 ) );
  372. *pbstrDeviceId = SysAllocString( awch );
  373. if ( !*pbstrDeviceId )
  374. {
  375. hr = E_OUTOFMEMORY;
  376. goto Cleanup;
  377. }
  378. // adjust our pointer past the '?'
  379. pwchUrl = pwch + 1;
  380. if ( !*pwchUrl )
  381. goto Cleanup;
  382. if ( StrCmpNIW( k_wszThumb, pwchUrl, z_cchThumb ) )
  383. goto Cleanup;
  384. // get the command portion of the URL
  385. pwch = StrChrIW( pwchUrl, k_wchSeperator );
  386. if ( !pwch )
  387. goto Cleanup;
  388. // adjust our pointer past the '?'
  389. pwchUrl = pwch + 1;
  390. if ( !*pwchUrl )
  391. goto Cleanup;
  392. // attempt to get the item portion of the url
  393. pwch = StrRChrIW( pwchUrl, 0, k_wchPeriod );
  394. awch[0] = k_wchEOS;
  395. if ( pwch )
  396. StrCpyNW( awch, pwchUrl, ( pwch - pwchUrl + 1) );
  397. else
  398. StrCpyW( awch, pwchUrl );
  399. *pbstrItem = SysAllocString( awch );
  400. if ( !*pbstrItem )
  401. {
  402. hr = E_OUTOFMEMORY;
  403. goto Cleanup;
  404. }
  405. TraceTag((tagWiaProto, "URL: Device=%S, Item=%S",
  406. *pbstrDeviceId, *pbstrItem ));
  407. // everything was ok
  408. return S_OK;
  409. Cleanup:
  410. if ( FAILED( hr ) )
  411. {
  412. SysFreeString( *pbstrDeviceId );
  413. SysFreeString( *pbstrItem );
  414. }
  415. return INET_E_INVALID_URL;
  416. }
  417. /*-----------------------------------------------------------------------------
  418. * CWiaProtocol::CreateDevice
  419. *
  420. * This is a helper method which handles creating a wia device with the
  421. * specified id. this instances a IWiaDevMgr object and then attempts
  422. * to create the device.
  423. *
  424. * bstrId: the id of the device to create
  425. * ppDevice: Out, recieves the pointer to the newly created device
  426. *--(samclem)-----------------------------------------------------------------*/
  427. HRESULT CWiaProtocol::CreateDevice( BSTR bstrId, IWiaItem** ppDevice )
  428. {
  429. CComPtr<IWiaItem> pDevice;
  430. CComPtr<IWiaDevMgr> pDevMgr;
  431. HRESULT hr;
  432. Assert( ppDevice );
  433. *ppDevice = 0;
  434. // first we need to create our device manager
  435. hr = THR( pDevMgr.CoCreateInstance( CLSID_WiaDevMgr ) );
  436. if ( FAILED( hr ) )
  437. return hr;
  438. // now we need the device manager to create a device
  439. hr = THR( pDevMgr->CreateDevice( bstrId, &pDevice ) );
  440. if ( FAILED( hr ) )
  441. return hr;
  442. // copy our device pointer over
  443. return THR( pDevice.CopyTo( ppDevice ) );
  444. }
  445. /*-----------------------------------------------------------------------------
  446. * CWiaProtocol::CreateURL [static]
  447. *
  448. * This method creates a URL for the given item. This doesn't verifiy the
  449. * item. Other than making sure it has a root so that we can build the URL.
  450. * This may return an invalid URL. It is important to verify that the item
  451. * can actually have a thumbnail before calling this.
  452. *
  453. * Note: in order to create a thumbnail:
  454. * lItemType & ( WiaItemTypeFile | WiaItemTypeImage )
  455. *
  456. * pItem: The wia item that we want to generate the URL for.
  457. * pbstrUrl: Out, recieves the finished URL
  458. *--(samclem)-----------------------------------------------------------------*/
  459. HRESULT CWiaProtocol::CreateURL( IWiaItem* pItem, BSTR* pbstrUrl )
  460. {
  461. HRESULT hr;
  462. CComBSTR bstrUrl;
  463. CComPtr<IWiaItem> pRootItem;
  464. CComQIPtr<IWiaPropertyStorage> pWiaStg;
  465. CComQIPtr<IWiaPropertyStorage> pRootWiaStg;
  466. PROPSPEC spec = { PRSPEC_PROPID, WIA_DIP_DEV_ID };
  467. PROPVARIANT va;
  468. if ( !pbstrUrl || !pItem )
  469. return E_POINTER;
  470. PropVariantInit( &va );
  471. // get the interfaces that we need
  472. pWiaStg = pItem;
  473. if ( !pWiaStg )
  474. {
  475. hr = E_NOINTERFACE;
  476. goto Cleanup;
  477. }
  478. hr = THR( pItem->GetRootItem( &pRootItem ) );
  479. if ( FAILED( hr ) || !pRootItem )
  480. goto Cleanup;
  481. pRootWiaStg = pRootItem;
  482. if ( !pRootWiaStg )
  483. {
  484. hr = E_NOINTERFACE;
  485. goto Cleanup;
  486. }
  487. // We need the device ID of the root item, and if we can't
  488. // get it then we don't have anything else to do.
  489. hr = THR( pRootWiaStg->ReadMultiple( 1, &spec, &va ) );
  490. if ( FAILED( hr ) || va.vt != VT_BSTR )
  491. goto Cleanup;
  492. // start building our URL
  493. bstrUrl.Append( k_wszProtocolName );
  494. bstrUrl.Append( k_wszColonSlash );
  495. bstrUrl.AppendBSTR( va.bstrVal );
  496. bstrUrl.Append( k_wszSeperator );
  497. bstrUrl.Append( k_wszThumb );
  498. bstrUrl.Append( k_wszSeperator );
  499. // we need to get the full item name from the item, because
  500. // we need to tack that on to the end
  501. PropVariantClear( &va );
  502. spec.propid = WIA_IPA_FULL_ITEM_NAME;
  503. hr = THR( pWiaStg->ReadMultiple( 1, &spec, &va ) );
  504. if ( FAILED( hr ) || va.vt != VT_BSTR )
  505. goto Cleanup;
  506. bstrUrl.AppendBSTR( va.bstrVal );
  507. bstrUrl.Append( k_wszExtension );
  508. TraceTag((tagWiaProto, "Created URL: %S", (BSTR)bstrUrl ));
  509. *pbstrUrl = bstrUrl.Copy();
  510. if ( !*pbstrUrl )
  511. hr = E_OUTOFMEMORY;
  512. Cleanup:
  513. PropVariantClear( &va );
  514. return hr;
  515. }
  516. /*-----------------------------------------------------------------------------
  517. * CWiaProtocol::TransferThumbnail [static]
  518. *
  519. * This handles the actual transfer of the thumbnail. This is only called
  520. * however, if we don't already have a cached copy of the thumbnail. Otherwise
  521. * we can simply use that one.
  522. *
  523. * Note: we spawn a thread with this function, which is why its static
  524. *
  525. * pvParams: a pointer to a TTPARAMS structure, which contains a pointer
  526. * to the IInternetProtoclSink and the IStream where the
  527. * item is marshalled.
  528. *--(samclem)-----------------------------------------------------------------*/
  529. DWORD WINAPI
  530. CWiaProtocol::TransferThumbnail( LPVOID pvParams )
  531. {
  532. CComPtr<IWiaItem> pItem;
  533. CComPtr<IInternetProtocolSink> pProtSink;
  534. IStream* pStrm = NULL;
  535. DWORD cbData = 0;
  536. BYTE* pbData = NULL;
  537. TTPARAMS* pParams = reinterpret_cast<TTPARAMS*>(pvParams);
  538. PROTOCOLDATA* ppd = NULL;
  539. HRESULT hr;
  540. HRESULT hrCoInit;
  541. Assert( pParams );
  542. pProtSink = pParams->pInetSink;
  543. pStrm = pParams->pStrm;
  544. hrCoInit = THR( CoInitialize( NULL ) );
  545. // we no longer need our params, so we can free them now. we
  546. // will handle freeing the params here since its simpler
  547. pParams->pInetSink->Release();
  548. CoTaskMemFree( pParams );
  549. pParams = NULL;
  550. // get the IWiaItem from the stream
  551. hr = THR( CoGetInterfaceAndReleaseStream(
  552. pStrm,
  553. IID_IWiaItem,
  554. reinterpret_cast<void**>(&pItem) ) );
  555. if ( FAILED( hr ) )
  556. goto Cleanup;
  557. // allocate a protocol data structure so that we can give this back to
  558. // the other thread. We will allocate this here. it may be freed if
  559. // something fails
  560. ppd = reinterpret_cast<PROTOCOLDATA*>(LocalAlloc( LPTR, sizeof( PROTOCOLDATA ) ) );
  561. if ( !ppd )
  562. {
  563. hr = E_OUTOFMEMORY;
  564. goto Cleanup;
  565. }
  566. // use the utility method on CWiaItem to do the transfer
  567. hr = THR( CWiaItem::TransferThumbnailToCache( pItem, &pbData, &cbData ) );
  568. if ( FAILED( hr ) )
  569. goto Cleanup;
  570. ppd->pData = pbData;
  571. ppd->cbData = cbData;
  572. ppd->dwState = k_dwTransferComplete;
  573. // we are all done now, we can tell trident that we are 100% done
  574. // and then call switch
  575. hr = THR( pProtSink->Switch( ppd ) );
  576. if ( FAILED( hr ) )
  577. goto Cleanup;
  578. hr = THR( pProtSink->ReportData(BSCF_LASTDATANOTIFICATION, cbData, cbData ) );
  579. Cleanup:
  580. // post our result status back to the sink
  581. //TODO(Aug, 27) samclem: implement the error string param
  582. if ( pProtSink )
  583. THR( pProtSink->ReportResult( hr, hr, NULL ) );
  584. if ( ppd )
  585. LocalFree( ppd );
  586. if ( SUCCEEDED( hrCoInit ) )
  587. CoUninitialize();
  588. return hr;
  589. }