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
687 lines
22 KiB
/*-----------------------------------------------------------------------------
|
|
*
|
|
* File: wiaproto.cpp
|
|
* Author: Samuel Clement (samclem)
|
|
* Date: Fri Aug 27 15:16:44 1999
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*
|
|
* Description:
|
|
* This contains the implementation of the "wia" internet protocol. This
|
|
* is a pluggable protocol that handles downloading thumbnails from a wia
|
|
* device.
|
|
*
|
|
* History:
|
|
* 27 Aug 1999: Created.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
// declare some debugging tags
|
|
DeclareTag( tagWiaProto, "!WiaProto", "Wia Protocol debug information" );
|
|
|
|
const WCHAR* k_wszProtocolName = L"wia";
|
|
const WCHAR* k_wszColonSlash = L":///";
|
|
const WCHAR* k_wszSeperator = L"?";
|
|
const WCHAR* k_wszThumb = L"thumb";
|
|
const WCHAR* k_wszExtension = L".bmp";
|
|
|
|
const int k_cchProtocolName = 3;
|
|
const int z_cchThumb = 5;
|
|
|
|
const WCHAR k_wchSeperator = L'?';
|
|
const WCHAR k_wchColon = L':';
|
|
const WCHAR k_wchFrontSlash = L'/';
|
|
const WCHAR k_wchPeriod = L'.';
|
|
const WCHAR k_wchEOS = L'\0';
|
|
|
|
enum
|
|
{
|
|
k_dwTransferPending = 0,
|
|
k_dwTransferComplete = 1,
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol
|
|
*
|
|
* Create a new CWiaProtocol. This simply initializes all the members to
|
|
* a known state so that we can then test against them
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
CWiaProtocol::CWiaProtocol()
|
|
: m_pFileItem( NULL ), m_ulOffset( 0 )
|
|
{
|
|
TRACK_OBJECT( "CWiaProtocol" );
|
|
m_pd.dwState = k_dwTransferPending;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::FinalRelease
|
|
*
|
|
* Called when we are finally released to cleanup any resources that we
|
|
* want to cleanup.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP_(void)
|
|
CWiaProtocol::FinalRelease()
|
|
{
|
|
if ( m_pFileItem )
|
|
m_pFileItem->Release();
|
|
m_pFileItem = NULL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
*
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Start( LPCWSTR szUrl, IInternetProtocolSink* pOIProtSink,
|
|
IInternetBindInfo* pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved )
|
|
{
|
|
CWiaCacheManager* pCache= CWiaCacheManager::GetInstance();
|
|
CComPtr<IWiaItem> pDevice;
|
|
CComBSTR bstrDeviceId ;
|
|
CComBSTR bstrItem ;
|
|
TTPARAMS* pParams = NULL;
|
|
HANDLE hThread = NULL;
|
|
DWORD dwThreadId = 0;
|
|
LONG lItemType = 0;
|
|
BYTE* pbThumb = NULL;
|
|
DWORD cbThumb = 0;
|
|
HRESULT hr;
|
|
|
|
// the first thing that we want to do is to attempt to crack the URL,
|
|
// this can be an involved process so we have a helper method that
|
|
// handles doing this for us.
|
|
hr = THR( CrackURL( szUrl, &bstrDeviceId, &bstrItem ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// do we already have a cached version of this item, if so we can avoid
|
|
// having to do anything else
|
|
if ( pCache->GetThumbnail( bstrItem, &pbThumb, &cbThumb ) )
|
|
{
|
|
TraceTag((tagWiaProto, "Using cached thumbnail" ));
|
|
|
|
m_pd.pData = pbThumb;
|
|
m_pd.cbData = cbThumb;
|
|
m_pd.dwState = k_dwTransferComplete;
|
|
|
|
hr = THR( pOIProtSink->ReportData( BSCF_LASTDATANOTIFICATION, cbThumb, cbThumb ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pOIProtSink->ReportResult( hr, hr, NULL ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
if ( !pCache->GetDevice( bstrDeviceId, &pDevice ) )
|
|
{
|
|
hr = THR( CreateDevice( bstrDeviceId, &pDevice ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
pCache->AddDevice( bstrDeviceId, pDevice );
|
|
}
|
|
else
|
|
{
|
|
TraceTag((tagWiaProto, "Using cached device pointer" ));
|
|
}
|
|
|
|
hr = THR( pDevice->FindItemByName( 0, bstrItem, &m_pFileItem ) );
|
|
if ( FAILED( hr ) || S_FALSE == hr )
|
|
{
|
|
TraceTag((tagWiaProto, "unable to locate item: %S", bstrItem ));
|
|
hr = INET_E_RESOURCE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// the last thing we want to verify is that the item is an image
|
|
// and a file, otherwise we don't want anything to do with it
|
|
hr = THR( m_pFileItem->GetItemType( &lItemType ) );
|
|
if ( !( lItemType & WiaItemTypeFile ) &&
|
|
!( lItemType & WiaItemTypeImage ) )
|
|
{
|
|
TraceTag((tagWiaProto, "unsupported wia item type for download" ));
|
|
hr = INET_E_INVALID_REQUEST;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// at this point everything is happy in our land. we have a valid
|
|
// thing to download from. We now need to create the thread which
|
|
// will do the main work
|
|
pParams = reinterpret_cast<TTPARAMS*>(CoTaskMemAlloc( sizeof( TTPARAMS ) ) );
|
|
if ( !pParams )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = THR( CoMarshalInterThreadInterfaceInStream(
|
|
IID_IWiaItem,
|
|
m_pFileItem,
|
|
&pParams->pStrm ) );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
TraceTag((tagWiaProto, "error marshalling interface" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
pParams->pInetSink = pOIProtSink;
|
|
pParams->pInetSink->AddRef();
|
|
|
|
hThread = CreateThread( NULL,
|
|
0,
|
|
CWiaProtocol::TransferThumbnail,
|
|
pParams,
|
|
0,
|
|
&dwThreadId );
|
|
|
|
if ( NULL == hThread )
|
|
{
|
|
pParams->pInetSink->Release();
|
|
pParams->pStrm->Release();
|
|
CoTaskMemFree( pParams );
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
TraceTag((tagWiaProto, "Started transfer thread: id(%x)", dwThreadId ));
|
|
}
|
|
|
|
Cleanup:
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( m_pFileItem )
|
|
m_pFileItem->Release();
|
|
m_pFileItem = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Continue
|
|
*
|
|
* This is called to pass data back from the the other threads. It lets
|
|
* the controlling thread know we have data.
|
|
*
|
|
* Note: Copy the data from the pointer, DON'T use thier pointer, they will
|
|
* free it following the return of this call.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Continue( PROTOCOLDATA* pProtocolData )
|
|
{
|
|
if ( k_dwTransferComplete == m_pd.dwState )
|
|
return E_UNEXPECTED;
|
|
|
|
memcpy( &m_pd, pProtocolData, sizeof( PROTOCOLDATA ) );
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocl::Abort
|
|
*
|
|
* This is called to abort our transfer. this is NYI. However, it would
|
|
* need to kill our thread if it is still running and free our data. However,
|
|
* it is perfectly harmless if the thread keeps running.
|
|
*
|
|
* hrReason: the reason for the abort
|
|
* dwOptions: the options for this abourt
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Abort( HRESULT hrReason, DWORD dwOptions )
|
|
{
|
|
|
|
TraceTag((tagWiaProto, "NYI: Abort hrReason=%hr", hrReason ));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Terminate
|
|
*
|
|
* This is called when the transfer is finished. This is responsible for
|
|
* cleaning anything up that we might need to do. We currently don't have
|
|
* anything to clean up. So this simply returns S_OK.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Terminate( DWORD dwOptions )
|
|
{
|
|
// Nothing to do.
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Suspend
|
|
*
|
|
* This is called to suspend the transfer. This is currently not implemenet
|
|
* inside of trident, so our methods just return E_NOTIMPL
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Suspend()
|
|
{
|
|
TraceTag((tagWiaProto, "NYI: Suspend" ));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Resume
|
|
*
|
|
* This is called to resume a suspended transfer. This is not suppored
|
|
* inside of URLMON, so we just return E_NOTIMPL
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Resume()
|
|
{
|
|
TraceTag((tagWiaProto, "NYI: Resume" ));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Read
|
|
*
|
|
* This is called to read data from our protocol. this copies cb bytes to
|
|
* the buffer passed in. Or it will copy what ever we have.
|
|
*
|
|
* pv: the buffer that we want to copy the data to
|
|
* cb: the size fo buffer, max bytes to copy
|
|
* pcbRead: Out, the number of bytes that we actually copied to the buffer
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Read( void* pv, ULONG cb, ULONG* pcbRead)
|
|
{
|
|
// validate our arguments
|
|
if ( !pv || !pcbRead )
|
|
return E_POINTER;
|
|
|
|
*pcbRead = 0;
|
|
|
|
// is the transfer currently pending? if so then
|
|
// we don't actually want to do anything here.
|
|
if ( k_dwTransferPending == m_pd.dwState )
|
|
return E_PENDING;
|
|
|
|
// do we actually have data to copy? if the offset is greater
|
|
// or equal to the size of our data then we don't have an data to
|
|
// copy so return S_FALSE
|
|
if ( m_ulOffset >= m_pd.cbData )
|
|
return S_FALSE;
|
|
|
|
// figure out how much we are going to copy
|
|
DWORD dwCopy = m_pd.cbData - m_ulOffset;
|
|
if ( dwCopy >= cb )
|
|
dwCopy = cb;
|
|
|
|
// if we have negative memory to copy, or 0, then we are done and we don't
|
|
// actually want to do anything besides return S_FALSE
|
|
if ( dwCopy <= 0 )
|
|
return S_FALSE;
|
|
|
|
// do the memcpy and setup our state and the return value
|
|
memcpy( pv, reinterpret_cast<BYTE*>(m_pd.pData) + m_ulOffset, dwCopy );
|
|
m_ulOffset += dwCopy;
|
|
*pcbRead = dwCopy;
|
|
|
|
return ( dwCopy == cb ? S_OK : S_FALSE );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::Seek
|
|
*
|
|
* Called to seek our data. However, we don't support seeking so this just
|
|
* returns E_FAIL
|
|
*
|
|
* dlibMove: how far to move the offset
|
|
* dwOrigin: indicates where the move shoudl begin
|
|
* plibNewPosition: The new position of the offset
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition )
|
|
{
|
|
// Don't support
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::LockRequest
|
|
*
|
|
* Called to lock the data. we don't need to lock our data, so this just
|
|
* returns S_OK
|
|
*
|
|
* dwOptions: reserved, will be 0.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::LockRequest( DWORD dwOptions )
|
|
{
|
|
//Don't support locking
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::UnlockRequest
|
|
*
|
|
* Called to unlock our data. We don't need or support locking, so this
|
|
* doesn't do anything besides return S_OK.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWiaProtocol::UnlockRequest()
|
|
{
|
|
//Don't support locking
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::CrackURL
|
|
*
|
|
* This handles breaking appart a URL which is passed in to us. This will
|
|
* return S_OK if it is a valid URL and we can work with it. otherwise this
|
|
* will return INET_E_INVALID_URL
|
|
*
|
|
* bstrUrl: the full url to be cracked
|
|
* pbstrDeviceId: Out, recieves the device id portion of the URL
|
|
* pbstrItem: Out, recieves the item portion of the URL
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
HRESULT CWiaProtocol::CrackURL( CComBSTR bstrUrl, BSTR* pbstrDeviceId, BSTR* pbstrItem )
|
|
{
|
|
WCHAR* pwchUrl = reinterpret_cast<WCHAR*>((BSTR)bstrUrl);
|
|
WCHAR* pwch = NULL;
|
|
WCHAR awch[INTERNET_MAX_URL_LENGTH] = { 0 };
|
|
HRESULT hr = INET_E_INVALID_URL;
|
|
|
|
Assert( pbstrDeviceId && pbstrItem );
|
|
|
|
*pbstrDeviceId = NULL;
|
|
*pbstrItem = NULL;
|
|
|
|
if (SysStringLen(bstrUrl) >= INTERNET_MAX_URL_LENGTH)
|
|
goto Cleanup;
|
|
|
|
/*
|
|
* We are going to use the SHWAPI functions to parse this URL. Our format
|
|
* is very simple.
|
|
*
|
|
* proto:///<deviceId>?<item>
|
|
*/
|
|
if ( StrCmpNIW( k_wszProtocolName, pwchUrl, k_cchProtocolName ) )
|
|
goto Cleanup;
|
|
|
|
pwchUrl += k_cchProtocolName;
|
|
while ( *pwchUrl == k_wchColon || *pwchUrl == k_wchFrontSlash )
|
|
pwchUrl++;
|
|
|
|
if ( !(*pwchUrl ) )
|
|
goto Cleanup;
|
|
|
|
// get the device portion of the URL
|
|
pwch = StrChrIW( pwchUrl, k_wchSeperator );
|
|
if ( !pwch )
|
|
goto Cleanup;
|
|
|
|
StrCpyNW( awch, pwchUrl, ( pwch - pwchUrl + 1 ) );
|
|
*pbstrDeviceId = SysAllocString( awch );
|
|
if ( !*pbstrDeviceId )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// adjust our pointer past the '?'
|
|
pwchUrl = pwch + 1;
|
|
if ( !*pwchUrl )
|
|
goto Cleanup;
|
|
|
|
if ( StrCmpNIW( k_wszThumb, pwchUrl, z_cchThumb ) )
|
|
goto Cleanup;
|
|
|
|
// get the command portion of the URL
|
|
pwch = StrChrIW( pwchUrl, k_wchSeperator );
|
|
|
|
if ( !pwch )
|
|
goto Cleanup;
|
|
|
|
// adjust our pointer past the '?'
|
|
pwchUrl = pwch + 1;
|
|
if ( !*pwchUrl )
|
|
goto Cleanup;
|
|
|
|
// attempt to get the item portion of the url
|
|
pwch = StrRChrIW( pwchUrl, 0, k_wchPeriod );
|
|
awch[0] = k_wchEOS;
|
|
|
|
if ( pwch )
|
|
StrCpyNW( awch, pwchUrl, ( pwch - pwchUrl + 1) );
|
|
else
|
|
StrCpyW( awch, pwchUrl );
|
|
|
|
*pbstrItem = SysAllocString( awch );
|
|
if ( !*pbstrItem )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TraceTag((tagWiaProto, "URL: Device=%S, Item=%S",
|
|
*pbstrDeviceId, *pbstrItem ));
|
|
|
|
// everything was ok
|
|
return S_OK;
|
|
|
|
Cleanup:
|
|
if ( FAILED( hr ) )
|
|
{
|
|
SysFreeString( *pbstrDeviceId );
|
|
SysFreeString( *pbstrItem );
|
|
}
|
|
|
|
return INET_E_INVALID_URL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::CreateDevice
|
|
*
|
|
* This is a helper method which handles creating a wia device with the
|
|
* specified id. this instances a IWiaDevMgr object and then attempts
|
|
* to create the device.
|
|
*
|
|
* bstrId: the id of the device to create
|
|
* ppDevice: Out, recieves the pointer to the newly created device
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
HRESULT CWiaProtocol::CreateDevice( BSTR bstrId, IWiaItem** ppDevice )
|
|
{
|
|
CComPtr<IWiaItem> pDevice;
|
|
CComPtr<IWiaDevMgr> pDevMgr;
|
|
HRESULT hr;
|
|
|
|
Assert( ppDevice );
|
|
*ppDevice = 0;
|
|
|
|
// first we need to create our device manager
|
|
hr = THR( pDevMgr.CoCreateInstance( CLSID_WiaDevMgr ) );
|
|
if ( FAILED( hr ) )
|
|
return hr;
|
|
|
|
// now we need the device manager to create a device
|
|
hr = THR( pDevMgr->CreateDevice( bstrId, &pDevice ) );
|
|
if ( FAILED( hr ) )
|
|
return hr;
|
|
|
|
// copy our device pointer over
|
|
return THR( pDevice.CopyTo( ppDevice ) );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::CreateURL [static]
|
|
*
|
|
* This method creates a URL for the given item. This doesn't verifiy the
|
|
* item. Other than making sure it has a root so that we can build the URL.
|
|
* This may return an invalid URL. It is important to verify that the item
|
|
* can actually have a thumbnail before calling this.
|
|
*
|
|
* Note: in order to create a thumbnail:
|
|
* lItemType & ( WiaItemTypeFile | WiaItemTypeImage )
|
|
*
|
|
* pItem: The wia item that we want to generate the URL for.
|
|
* pbstrUrl: Out, recieves the finished URL
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
HRESULT CWiaProtocol::CreateURL( IWiaItem* pItem, BSTR* pbstrUrl )
|
|
{
|
|
HRESULT hr;
|
|
CComBSTR bstrUrl;
|
|
CComPtr<IWiaItem> pRootItem;
|
|
CComQIPtr<IWiaPropertyStorage> pWiaStg;
|
|
CComQIPtr<IWiaPropertyStorage> pRootWiaStg;
|
|
PROPSPEC spec = { PRSPEC_PROPID, WIA_DIP_DEV_ID };
|
|
PROPVARIANT va;
|
|
|
|
if ( !pbstrUrl || !pItem )
|
|
return E_POINTER;
|
|
|
|
PropVariantInit( &va );
|
|
|
|
|
|
// get the interfaces that we need
|
|
pWiaStg = pItem;
|
|
if ( !pWiaStg )
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = THR( pItem->GetRootItem( &pRootItem ) );
|
|
if ( FAILED( hr ) || !pRootItem )
|
|
goto Cleanup;
|
|
|
|
pRootWiaStg = pRootItem;
|
|
if ( !pRootWiaStg )
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// We need the device ID of the root item, and if we can't
|
|
// get it then we don't have anything else to do.
|
|
hr = THR( pRootWiaStg->ReadMultiple( 1, &spec, &va ) );
|
|
if ( FAILED( hr ) || va.vt != VT_BSTR )
|
|
goto Cleanup;
|
|
|
|
// start building our URL
|
|
bstrUrl.Append( k_wszProtocolName );
|
|
bstrUrl.Append( k_wszColonSlash );
|
|
bstrUrl.AppendBSTR( va.bstrVal );
|
|
bstrUrl.Append( k_wszSeperator );
|
|
bstrUrl.Append( k_wszThumb );
|
|
bstrUrl.Append( k_wszSeperator );
|
|
|
|
// we need to get the full item name from the item, because
|
|
// we need to tack that on to the end
|
|
PropVariantClear( &va );
|
|
spec.propid = WIA_IPA_FULL_ITEM_NAME;
|
|
hr = THR( pWiaStg->ReadMultiple( 1, &spec, &va ) );
|
|
if ( FAILED( hr ) || va.vt != VT_BSTR )
|
|
goto Cleanup;
|
|
|
|
bstrUrl.AppendBSTR( va.bstrVal );
|
|
bstrUrl.Append( k_wszExtension );
|
|
|
|
TraceTag((tagWiaProto, "Created URL: %S", (BSTR)bstrUrl ));
|
|
|
|
*pbstrUrl = bstrUrl.Copy();
|
|
if ( !*pbstrUrl )
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
Cleanup:
|
|
PropVariantClear( &va );
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWiaProtocol::TransferThumbnail [static]
|
|
*
|
|
* This handles the actual transfer of the thumbnail. This is only called
|
|
* however, if we don't already have a cached copy of the thumbnail. Otherwise
|
|
* we can simply use that one.
|
|
*
|
|
* Note: we spawn a thread with this function, which is why its static
|
|
*
|
|
* pvParams: a pointer to a TTPARAMS structure, which contains a pointer
|
|
* to the IInternetProtoclSink and the IStream where the
|
|
* item is marshalled.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
DWORD WINAPI
|
|
CWiaProtocol::TransferThumbnail( LPVOID pvParams )
|
|
{
|
|
CComPtr<IWiaItem> pItem;
|
|
CComPtr<IInternetProtocolSink> pProtSink;
|
|
IStream* pStrm = NULL;
|
|
DWORD cbData = 0;
|
|
BYTE* pbData = NULL;
|
|
TTPARAMS* pParams = reinterpret_cast<TTPARAMS*>(pvParams);
|
|
PROTOCOLDATA* ppd = NULL;
|
|
HRESULT hr;
|
|
HRESULT hrCoInit;
|
|
|
|
Assert( pParams );
|
|
|
|
pProtSink = pParams->pInetSink;
|
|
pStrm = pParams->pStrm;
|
|
|
|
hrCoInit = THR( CoInitialize( NULL ) );
|
|
|
|
// we no longer need our params, so we can free them now. we
|
|
// will handle freeing the params here since its simpler
|
|
pParams->pInetSink->Release();
|
|
CoTaskMemFree( pParams );
|
|
pParams = NULL;
|
|
|
|
// get the IWiaItem from the stream
|
|
hr = THR( CoGetInterfaceAndReleaseStream(
|
|
pStrm,
|
|
IID_IWiaItem,
|
|
reinterpret_cast<void**>(&pItem) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// allocate a protocol data structure so that we can give this back to
|
|
// the other thread. We will allocate this here. it may be freed if
|
|
// something fails
|
|
ppd = reinterpret_cast<PROTOCOLDATA*>(LocalAlloc( LPTR, sizeof( PROTOCOLDATA ) ) );
|
|
if ( !ppd )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// use the utility method on CWiaItem to do the transfer
|
|
hr = THR( CWiaItem::TransferThumbnailToCache( pItem, &pbData, &cbData ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
ppd->pData = pbData;
|
|
ppd->cbData = cbData;
|
|
ppd->dwState = k_dwTransferComplete;
|
|
|
|
// we are all done now, we can tell trident that we are 100% done
|
|
// and then call switch
|
|
hr = THR( pProtSink->Switch( ppd ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pProtSink->ReportData(BSCF_LASTDATANOTIFICATION, cbData, cbData ) );
|
|
|
|
Cleanup:
|
|
// post our result status back to the sink
|
|
//TODO(Aug, 27) samclem: implement the error string param
|
|
if ( pProtSink )
|
|
THR( pProtSink->ReportResult( hr, hr, NULL ) );
|
|
|
|
if ( ppd )
|
|
LocalFree( ppd );
|
|
|
|
if ( SUCCEEDED( hrCoInit ) )
|
|
CoUninitialize();
|
|
|
|
return hr;
|
|
}
|