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.
 
 
 
 
 
 

1679 lines
54 KiB

/*-----------------------------------------------------------------------------
*
* File: wiaitem.cpp
* Author: Samuel Clement (samclem)
* Date: Tue Aug 17 17:26:17 1999
*
* Copyright (c) 1999 Microsoft Corporation
*
* Description:
* Contains the implementation of the CWiaItem object. This object provides
* the automation interface to the IWiaItem interface.
*
* History:
* 17 Aug 1999: Created.
* 27 Aug 1999: Added the tagWiaDataTrans (samclem)
* 10 Sep 1999: Moved thumbnail transfer to a static method. Hooked
* thumbnails up to CWiaProtocol for transfer, no more
* temporary files. (samclem)
*----------------------------------------------------------------------------*/
#include "stdafx.h"
HRESULT VerticalFlip(BYTE *pBuf);
DeclareTag( tagWiaDataTrans, "!WiaTrans", "Display output during the transfer" );
//const WORD k_wBitmapType = static_cast<WORD>('BM');
const WORD k_wBitmapType = 0x4d42; // "BM"
/*-----------------------------------------------------------------------------
* CWiaItem::CWiaItem
*
* Create a new wrapper around an IWiaItem for a device. This doesn't do
* anything besides initialize the variables to a known state.
*--(samclem)-----------------------------------------------------------------*/
CWiaItem::CWiaItem()
: m_pWiaItem( NULL ), m_pWiaStorage( NULL ), m_dwThumbWidth( -1 ), m_dwThumbHeight( -1 ),
m_bstrThumbUrl( NULL ), m_dwItemWidth( -1), m_dwItemHeight( -1 )
{
TRACK_OBJECT( "CWiaItem" );
}
/*-----------------------------------------------------------------------------
* CWiaItem::FinalRelease
*
* Called while destroying the object, releases all the interfaces that this
* object is attached to.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP_(void)
CWiaItem::FinalRelease()
{
if ( m_pWiaItem )
m_pWiaItem->Release();
m_pWiaItem = NULL;
if ( m_pWiaStorage )
m_pWiaStorage->Release();
m_pWiaStorage = NULL;
if ( m_bstrThumbUrl )
SysFreeString( m_bstrThumbUrl );
m_bstrThumbUrl = NULL;
}
/*-----------------------------------------------------------------------------
* CWiaItem::CacheProperties
*
* This is called to handle caching the important (frequently used)
* properties so we don't have to talk to the camera when we want these.
*
* pWiaStg: the property storage to read the properties from
*--(samclem)-----------------------------------------------------------------*/
HRESULT CWiaItem::CacheProperties( IWiaPropertyStorage* pWiaStg )
{
HRESULT hr;
enum
{
PropThumbWidth = 0,
PropThumbHeight = 1,
PropItemWidth = 2,
PropItemHeight = 3,
PropCount = 4,
};
PROPSPEC aspec[PropCount] = {
{ PRSPEC_PROPID, WIA_IPC_THUMB_WIDTH },
{ PRSPEC_PROPID, WIA_IPC_THUMB_HEIGHT },
{ PRSPEC_PROPID, WIA_IPA_PIXELS_PER_LINE },
{ PRSPEC_PROPID, WIA_IPA_NUMBER_OF_LINES },
};
PROPVARIANT avaProps[PropCount];
hr = THR( pWiaStg->ReadMultiple( PropCount, aspec, avaProps ) );
if ( FAILED( hr ) )
return hr;
// store the values away if they were valid
if ( avaProps[PropThumbWidth].vt != VT_EMPTY )
m_dwThumbWidth = avaProps[PropThumbWidth].lVal;
if ( avaProps[PropThumbHeight].vt != VT_EMPTY )
m_dwThumbHeight = avaProps[PropThumbHeight].lVal;
if ( avaProps[PropItemWidth].vt != VT_EMPTY )
m_dwItemWidth = avaProps[PropItemWidth].lVal;
if ( avaProps[PropItemHeight].vt != VT_EMPTY )
m_dwItemHeight = avaProps[PropItemHeight].lVal;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::AttachTo
*
* Called to attach this object to an IWiaItem that represents the device
*
* pWia: The CWia object that is the root of all evils, used to
* handle callbacks and collection cache.
* pWiaItem: the device item to attach this wrapper to.
*--(samclem)-----------------------------------------------------------------*/
HRESULT
CWiaItem::AttachTo( CWia* pWia, IWiaItem* pWiaItem )
{
Assert( NULL != pWiaItem );
Assert( NULL == m_pWiaItem );
HRESULT hr;
IWiaPropertyStorage* pWiaStg = NULL;
hr = THR( pWiaItem->QueryInterface( IID_IWiaPropertyStorage,
reinterpret_cast<void**>(&pWiaStg) ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( CacheProperties( pWiaStg ) );
if ( FAILED( hr ) )
goto Cleanup;
// set our pointers
m_pWiaItem = pWiaItem;
m_pWiaItem->AddRef();
m_pWiaStorage = pWiaStg;
m_pWiaStorage->AddRef();
// don't addref this, otherwise we have a circular referance
// problem. We will keep a weak referance.
m_pWia = pWia;
Cleanup:
if ( pWiaStg )
pWiaStg->Release();
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::GetItemsFromUI [IWiaDispatchItem]
*
* This handles showing the Data Acquisition U.I. Note that this is only valid
* off a root item.
*
*
* dwFlags: flags specifying UI operations.
* dwIntent: the intent value specifying attributes such as Color etc.
* ppCollection: the return collection of Wia Items
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::GetItemsFromUI( WiaFlag Flags, WiaIntent Intent, ICollection** ppCollection )
{
HRESULT hr = S_OK;
LONG lCount = 0;
IWiaItem **ppIWiaItem = NULL;
CComObject<CCollection>* pCol = NULL;
IDispatch** rgpDispatch = NULL;
LONG lItemType = 0;
if ( NULL == ppCollection )
return E_POINTER;
// first we want the item type of this item
hr = THR( m_pWiaItem->GetItemType( &lItemType ) );
if ( FAILED( hr ) )
goto Cleanup;
if ( !(lItemType & WiaItemTypeRoot) )
{
hr = E_INVALIDARG;
goto Cleanup;
}
DWORD dwFlags = (DWORD)Flags;
DWORD dwIntent = (DWORD)Intent;
// Show the get image dialog.
hr = m_pWiaItem->DeviceDlg((HWND)NULL,
dwFlags,
dwIntent,
&lCount,
&ppIWiaItem);
if (SUCCEEDED(hr))
{
// Check if user cancelled
if ( S_FALSE == hr )
{
goto Cleanup;
}
// Put returned items into a collection
// allocate our arrays, zeroing them if we are successful.
// Note: we check for failure after each one
if ( lCount > 0 )
{
hr = E_OUTOFMEMORY;
rgpDispatch = reinterpret_cast<IDispatch**>
(CoTaskMemAlloc( sizeof( IDispatch* ) * lCount ) );
if ( rgpDispatch )
ZeroMemory( rgpDispatch, sizeof( IDispatch* ) * lCount );
else
goto Cleanup;
// we have all our items, so we simply need to iterate
// over them and create the CWiaItem to attach to them
for ( LONG i = 0; i < lCount; i++ )
{
if ( !(ppIWiaItem[i]) )
continue;
CComObject<CWiaItem>* pItem;
hr = THR( CComObject<CWiaItem>::CreateInstance( &pItem ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pItem->AttachTo( m_pWia, ppIWiaItem[i] ) );
if ( FAILED( hr ) )
{
delete pItem;
goto Cleanup;
}
hr = THR( pItem->QueryInterface( &rgpDispatch[i] ) );
Assert( SUCCEEDED( hr ) ); // this shouldn't fail.
}
}
hr = THR( CComObject<CCollection>::CreateInstance( &pCol ) );
if ( FAILED( hr ) )
goto Cleanup;
if ( rgpDispatch )
{
if( !pCol->SetDispatchArray( rgpDispatch, lCount ) )
{
hr = E_FAIL;
goto Cleanup;
}
}
hr = THR( pCol->QueryInterface( ppCollection ) );
}
Cleanup:
if (ppIWiaItem)
{
for ( LONG i = 0; i < lCount; i++ )
{
if ( !(ppIWiaItem[i]) )
continue;
ppIWiaItem[i]->Release();
ppIWiaItem[i] = NULL;
}
LocalFree( ppIWiaItem );
}
if (FAILED(hr) && rgpDispatch)
{
for (LONG index = 0; index < lCount; index ++)
{
if (rgpDispatch[index])
rgpDispatch[index]->Release();
rgpDispatch[index] = NULL;
}
CoTaskMemFree( rgpDispatch );
}
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::Transfer [IWiaDispatchItem]
*
* This handles transfering this item to a file. This does several things:
*
* 1. Verifies that the item can actually be tranferred to a file
* 2. begins the async trans, by spawning a thread
* 3. following the completion of the async transfer the client is
* sent a onTransferComplete( item, filename ) event.
*
* Note: we need to consider how to handle this methods, this object is currently
* unsafe for scripting because this could potentially overwrite system
* files. Proposed fixes:
*
* 1. Don't over write existing files
* 2. If the file exists, then check its attributes if the system
* attribute is present then abort
* 3. If the file name starts with %WinDir% then abort
*
* bstrFilename: the name of the file to save this item to
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::Transfer( BSTR bstrFilename, VARIANT_BOOL bAsyncTransfer )
{
TraceTag((0, "attempting to transfer image to: %S", bstrFilename ));
DWORD dwThreadId = NULL;
HANDLE hThread = NULL;
LONG lItemType = 0;
HRESULT hr;
IStream* pStrm = NULL;
CWiaDataTransfer::ASYNCTRANSFERPARAMS* pParams;
if (bstrFilename == NULL)
return E_INVALIDARG; // No file name specified
if (SysStringLen(bstrFilename) >= MAX_PATH)
return E_INVALIDARG; // don't allow pathologicaly long file names
hr = THR( m_pWiaItem->GetItemType( &lItemType ) );
if ( FAILED( hr ) )
goto Cleanup;
if ( !( lItemType & WiaItemTypeFile ) && !( lItemType & WiaItemTypeTransfer ) )
return E_INVALIDARG; // can't download this guy
// we need to marshall the m_pWiaItem interface to another thread so that
// we can accessit inside of that object.
hr = THR( CoMarshalInterThreadInterfaceInStream( IID_IWiaItem,
m_pWiaItem,
&pStrm ) );
if ( FAILED( hr ) )
goto Cleanup;
pParams = reinterpret_cast<CWiaDataTransfer::ASYNCTRANSFERPARAMS*>
(CoTaskMemAlloc( sizeof( CWiaDataTransfer::ASYNCTRANSFERPARAMS ) ) );
if (!pParams) {
hr = E_OUTOFMEMORY;
goto Cleanup;
}
// setup the params
pParams->pStream = pStrm;
pParams->pStream->AddRef();
pParams->pItem = this;
pParams->pItem->AddRef();
pParams->bstrFilename = SysAllocString( bstrFilename );
if ( bAsyncTransfer == VARIANT_TRUE )
{
hThread = CreateThread( NULL,
0,
CWiaDataTransfer::DoAsyncTransfer,
pParams,
0,
&dwThreadId );
// did we create the thread?
if ( hThread == NULL )
{
TraceTag((0, "error creating the async transfer thread" ));
return E_FAIL;
}
TraceTag((0, "create async download thread: id(%ld)", dwThreadId ));
}
else
hr = CWiaDataTransfer::DoAsyncTransfer(pParams);
Cleanup:
if ( pStrm )
pStrm->Release();
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::TakePicture [IWiaDispatchItem]
*
* This method sends the take picture command to the driver. It will return
* a new dispatch item representing the new picture.
*
*--(byronc)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::TakePicture( IWiaDispatchItem** ppDispItem )
{
TraceTag((0, "attempting to take new picture" ));
HRESULT hr = S_OK;
IWiaItem *pNewIWiaItem = NULL;
CComObject<CWiaItem>*pItem = NULL;
if ( !ppDispItem )
return E_POINTER;
// Initialize the returned item to NULL
*ppDispItem = NULL;
// Send device command "TakePicture"
hr = m_pWiaItem->DeviceCommand(0,
&WIA_CMD_TAKE_PICTURE,
&pNewIWiaItem);
if (SUCCEEDED(hr)) {
// Check for new item created
if (pNewIWiaItem) {
// Set the returned item
hr = THR( CComObject<CWiaItem>::CreateInstance( &pItem ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pItem->AttachTo( m_pWia, pNewIWiaItem ) );
if ( FAILED( hr ) )
{
delete pItem;
goto Cleanup;
}
hr = THR( pItem->QueryInterface( ppDispItem ) );
Assert( SUCCEEDED( hr ) ); // this shouldn't fail.
}
} else {
// Call failed, so we'll set hr to false and return a NULL item.
hr = S_FALSE;
}
Cleanup:
if (FAILED(hr)) {
if (pItem) {
delete pItem;
pItem = NULL;
}
if (*ppDispItem) {
*ppDispItem = NULL;
}
}
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::SendTransferCompelete
*
* Called to send a transfer complete notification.
*
* pchFilename: the filename that we transfered to
*--(samclem)-----------------------------------------------------------------*/
void
CWiaItem::SendTransferComplete(BSTR bstrFilename )
{
//TODO(Aug, 24) samclem: implement this
TraceTag((0, "SendTransferComplete -- %S done.", bstrFilename ));
CComBSTR bstrPathname = bstrFilename;
m_pWia->SendEventMessage( WEM_TRANSFERCOMPLETE,
reinterpret_cast<WPARAM>(static_cast<IDispatch*>(this)),
reinterpret_cast<LPARAM>(bstrPathname.Detach()) );
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Children [IWiaDispatchItem]
*
* This returns a collection of the children this item has. this will return
* and empty collection if the doesn't or can't have any children.
*
* ppCollection: Out, recieves the ICollection pointer for our collection
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_Children( ICollection** ppCollection )
{
CComObject<CCollection>* pCol = NULL;
HRESULT hr;
IDispatch** rgpDispatch = NULL;
IEnumWiaItem* pEnum = NULL;
IWiaItem** rgpChildren = NULL;
// code below assumes cChildren is initialized to 0!!!!
ULONG cChildren = 0;
ULONG celtFetched = 0;
LONG ulItemType = 0;
//TODO(Aug, 18) samclem: for performance reasons we will want to
// cache the collection of our children. In order to do that however,
// we need to be able sink to the WIA_EVENT_ITEM_ADDED and the
// WIA_EVENT_ITEM_DELTED events. Currently this object doesn't
// do any syncing so we will create the collection each time it
// is requested. This however can be very slow.
if ( NULL == ppCollection )
return E_POINTER;
// first we want the item type of this item
hr = THR( m_pWiaItem->GetItemType( &ulItemType ) );
if ( FAILED( hr ) )
goto Cleanup;
// You can enumerate the children of a wia item if an only if
// it contains the WiaItemTypeFolder flag. We however, want to
// return an empty enumeration anyhow, so we will make this
// test upfront and get the enumeration and the child count
// only if we can support them
if ( ulItemType & WiaItemTypeFolder )
{
// enum the children
hr = THR( m_pWiaItem->EnumChildItems( &pEnum ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pEnum->GetCount( &cChildren ) );
if ( FAILED( hr ) )
goto Cleanup;
}
// allocate our arrays, zeroing them if we are successful.
// Note: we check for failure after each one
if ( cChildren > 0 )
{
hr = E_OUTOFMEMORY;
rgpChildren = new IWiaItem*[cChildren];
if ( rgpChildren )
ZeroMemory( rgpChildren, sizeof( IWiaItem* ) * cChildren );
else
goto Cleanup;
rgpDispatch = reinterpret_cast<IDispatch**>
(CoTaskMemAlloc( sizeof( IDispatch* ) * cChildren ) );
if ( rgpDispatch )
ZeroMemory( rgpDispatch, sizeof( IDispatch* ) * cChildren );
else
goto Cleanup;
//BUG (Aug, 18) samclem: You can't retrieve all the items at
// once, WIA doesn't want to do it, so we have another loop here
// but we still use the array, hoping that we can do this in
// the future.
// get the items from the enum
for ( ULONG iChild = 0; iChild < cChildren; iChild++ )
{
hr = THR( pEnum->Next( 1, &rgpChildren[iChild], &celtFetched ) );
if ( FAILED( hr ) || celtFetched != 1 )
goto Cleanup;
}
// we now have all our items, so we simply need iterate
// over them and create the CWiaItem to attach to them
for ( ULONG i = 0; i < cChildren; i++ )
{
if ( !rgpChildren[i] )
continue;
CComObject<CWiaItem>* pItem;
hr = THR( CComObject<CWiaItem>::CreateInstance( &pItem ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pItem->AttachTo( m_pWia, rgpChildren[i] ) );
if ( FAILED( hr ) )
{
delete pItem;
goto Cleanup;
}
hr = THR( pItem->QueryInterface( &rgpDispatch[i] ) );
Assert( SUCCEEDED( hr ) ); // this shouldn't fail.
}
}
hr = THR( CComObject<CCollection>::CreateInstance( &pCol ) );
if ( FAILED( hr ) )
goto Cleanup;
if ( rgpDispatch )
{
if( !pCol->SetDispatchArray( rgpDispatch, cChildren ) )
{
hr = E_FAIL;
goto Cleanup;
}
}
hr = THR( pCol->QueryInterface( ppCollection ) );
Cleanup:
if ( pEnum )
pEnum->Release();
if ( pCol && FAILED( hr ) )
delete pCol;
if ( rgpChildren )
{
for ( ULONG i = 0; i < cChildren; i++ )
if ( rgpChildren[i] ) rgpChildren[i]->Release();
delete[] rgpChildren;
}
if ( rgpDispatch && FAILED( hr ) )
{
for ( ULONG i = 0; i < cChildren; i++ )
if ( rgpDispatch[i] ) rgpDispatch[i]->Release();
CoTaskMemFree( rgpDispatch );
}
return hr;
}
// macro which helps inside of get_ItemType
#define CAT_SEMI( buf, str ) \
{ \
if( *buf ) \
_tcscat( buf, TEXT( ";" ) ); \
_tcscat( buf, str ); \
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_ItemType [IWiaDispatchItem]
*
* Retrieves the item type as a BSTR. This will have the format as follows:
*
* "device;folder", "image;file", "audio;file"
*
* The format is single strings seperated by ';'. there will be no semi-colon
* at the end of the string.
*
* pbstrType: recieves the type of the item as a bstr.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_ItemType( BSTR* pbstrType )
{
TCHAR tch[MAX_PATH] = { 0, 0 };
HRESULT hr;
LONG lItemType;
USES_CONVERSION;
if ( !pbstrType )
return E_POINTER;
*pbstrType = NULL;
// we will construct an array of tchar's that contain the
// property types. (note: an alternate approach would use CComBSTR)
hr = THR( m_pWiaItem->GetItemType( &lItemType ) );
if ( FAILED( hr ) )
return hr;
// process our flags, and create the item type.
if ( lItemType & WiaItemTypeAnalyze )
CAT_SEMI( tch, TEXT("analyze") );
if ( lItemType & WiaItemTypeAudio )
CAT_SEMI( tch, TEXT("audio") );
if ( lItemType & WiaItemTypeDeleted )
CAT_SEMI( tch, TEXT("deleted") );
if ( lItemType & WiaItemTypeDevice )
CAT_SEMI( tch, TEXT("device") );
if ( lItemType & WiaItemTypeDisconnected )
CAT_SEMI( tch, TEXT("disconnected") );
if ( lItemType & WiaItemTypeFile )
CAT_SEMI( tch, TEXT("file") );
if ( lItemType & WiaItemTypeFolder )
CAT_SEMI( tch, TEXT("folder") );
if ( lItemType & WiaItemTypeFree )
CAT_SEMI( tch, TEXT("free") );
if ( lItemType & WiaItemTypeImage )
CAT_SEMI( tch, TEXT("image") );
if ( lItemType & WiaItemTypeRoot )
CAT_SEMI( tch, TEXT("root") );
if ( lItemType & WiaItemTypeTransfer)
CAT_SEMI( tch, TEXT("transfer") );
//
// Original version:
// WCHAR awch[MAX_PATH];
// if ( MultiByteToWideChar( CP_ACP, 0, ach, -1, awch, MAX_PATH ) )
//
// Replaced with ATL conversion T2W.
//
*pbstrType = SysAllocString( T2W(tch) );
if ( !*pbstrType )
return E_OUTOFMEMORY;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::GetPropById [IWiaDispatchItem]
*
* This returns the unchanged variant value of the property with the given
* id.
*
* This will return a an empty variant if the property doesn't exist or
* it can't be easily converted to a variant.
*
* propid: the id of the property that we want
* pvaOut: Out, gets the value of the property.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::GetPropById( WiaItemPropertyId Id, VARIANT* pvaOut )
{
HRESULT hr;
PROPVARIANT vaProp;
DWORD dwPropID = (DWORD)Id;
hr = THR( GetWiaProperty( m_pWiaStorage, dwPropID, &vaProp ) );
if ( FAILED( hr ) )
return hr;
// attempt to convert
hr = THR( PropVariantToVariant( &vaProp, pvaOut ) );
if ( FAILED( hr ) )
{
TraceTag((0, "forcing device property %ld to VT_EMPTY", dwPropID ));
VariantInit( pvaOut );
pvaOut->vt = VT_EMPTY;
}
// clear and return
PropVariantClear( &vaProp );
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_ConnectStatus [IWiaDispatchItem]
*
* returns the connect status of the item. This is only applicatable to devices
* and otherwise it will return NULL.
*
* Values: "connected", "disconnected" or NULL if not applicable
*
* pbstrStatus: Out, recieves the current connect status of the item
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_ConnectStatus( BSTR* pbstrStatus )
{
PROPVARIANT vaProp;
HRESULT hr;
STRING_TABLE( stConnectStatus )
STRING_ENTRY( WIA_DEVICE_CONNECTED, "connected" )
STRING_ENTRY( WIA_DEVICE_NOT_CONNECTED, "disconnected" )
STRING_ENTRY( WIA_DEVICE_CONNECTED + 2, "not supported" )
END_STRING_TABLE()
if ( !pbstrStatus )
return E_POINTER;
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_DPA_CONNECT_STATUS, &vaProp ) );
if (hr != S_OK) {
if ( FAILED( hr ) ) {
return hr;
}
else {
//
// Property not found, so return "not supported"
//
vaProp.vt = VT_I4;
vaProp.lVal = WIA_DEVICE_CONNECTED + 2;
}
}
*pbstrStatus = SysAllocString( GetStringForVal( stConnectStatus, vaProp.lVal ) );
PropVariantClear( &vaProp );
if ( !*pbstrStatus )
return E_OUTOFMEMORY;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Time [IWiaDispatchItem]
*
* Retrieves the current time from this item.
*
* pbstrTime: Out, recieves the time as a BSTR.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_Time( BSTR* pbstrTime )
{
if ( !pbstrTime )
return E_POINTER;
PROPVARIANT vaProp;
HRESULT hr = S_OK;
LONG lItemType = 0;
WCHAR wszStr[MAX_PATH];
UNALIGNED SYSTEMTIME *pSysTime;
PropVariantInit(&vaProp);
//
// If this is the root item, get WIA_DPA_DEVICE_TIME, else get
// WIA_IPA_ITEM_ITEM.
//
hr = THR( m_pWiaItem->GetItemType( &lItemType ) );
if ( FAILED( hr ) )
return hr;
if (lItemType & WiaItemTypeRoot) {
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_DPA_DEVICE_TIME, &vaProp ) );
} else {
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_IPA_ITEM_TIME, &vaProp ) );
}
if ( FAILED( hr ) )
return hr;
//
// Convert the value in vaProp to a string. First check that the variant
// contains enough words to make up a SYSTEMTIME structure.
//
if (vaProp.caui.cElems >= (sizeof(SYSTEMTIME) / sizeof(WORD))) {
pSysTime = (SYSTEMTIME*) vaProp.caui.pElems;
swprintf(wszStr, L"%.4d/%.2d/%.2d:%.2d:%.2d:%.2d", pSysTime->wYear,
pSysTime->wMonth,
pSysTime->wDay,
pSysTime->wHour,
pSysTime->wMinute,
pSysTime->wSecond);
*pbstrTime = SysAllocString( wszStr );
} else {
hr = S_FALSE;
}
if ( hr != S_OK ) {
*pbstrTime = SysAllocString( L"not supported" );
}
if ( !*pbstrTime )
hr = E_OUTOFMEMORY;
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_FirmwareVersion [IWiaDispatchItem]
*
* Retrieves the firmware version from the device. Only applicatble on devices,
* if its not applicable NULL is returned.
*
* pbstrVersion: Out, recieves the version from the device
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_FirmwareVersion( BSTR* pbstrVersion )
{
if ( !pbstrVersion )
return E_POINTER;
PROPVARIANT vaProp;
HRESULT hr;
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_DPA_FIRMWARE_VERSION, &vaProp ) );
if ( FAILED( hr ) )
return hr;
// if it is already a bstr then leave it alone
if ( vaProp.vt == VT_BSTR )
*pbstrVersion = SysAllocString( vaProp.bstrVal );
else if ( vaProp.vt == VT_I4 )
{
WCHAR rgwch[255];
wsprintf(rgwch, L"%d", vaProp.lVal);
*pbstrVersion = SysAllocString( rgwch );
}
else
{
*pbstrVersion = SysAllocString( L"unknown" );
}
PropVariantClear( &vaProp );
if ( !*pbstrVersion )
return E_OUTOFMEMORY;
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Name [IWiaDispatchItem]
*
* Retrieves the name of the item. Applicable to all items.
*
* pbstrName: Out, recieves the name of the item
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_Name( BSTR* pbstrName )
{
if ( !pbstrName )
return E_POINTER;
return THR( GetWiaPropertyBSTR( m_pWiaStorage, WIA_IPA_ITEM_NAME, pbstrName ) );
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_FullName [IWiaDispatchItem]
*
* Retrieves the full name of the item, Applicable to all items
* Format: "Root\blah\blah"
*
* pbstrFullName: Out, recieves the full name of the item
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_FullName( BSTR* pbstrFullName )
{
if ( !pbstrFullName )
return E_POINTER;
return THR( GetWiaPropertyBSTR( m_pWiaStorage, WIA_IPA_FULL_ITEM_NAME, pbstrFullName ) );
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Width [IWiaDispatchItem]
*
* Retrieves the width of the item, most items will support getting thier
* width.
*
* plWidth: Out, recieves the items with in pixels
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP CWiaItem::get_Width( long* plWidth )
{
if ( !plWidth )
return E_POINTER;
*plWidth = (long)m_dwItemWidth;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Height [IWiaDispatchItem]
*
* Retrieves the height of the item, most items will support getting thier
* width. Will return 0, if there is no with to get
*
* plHeight: Out, recieves the itmes height in pixels
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP CWiaItem::get_Height( long* plHeight )
{
if ( !plHeight )
return E_POINTER;
*plHeight = (long)m_dwItemHeight;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_ThumbWidth [IWiaDispatchItem]
*
* Retrieves the thumbnail width for the thumbnail associated with this item,
* if this item doesn't support thumbnails this will be 0.
*
* plWidth: Out, recieves the width of the thumbnail image
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_ThumbWidth( long* plWidth )
{
if ( !plWidth )
return E_POINTER;
*plWidth = (long)m_dwThumbWidth;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_ThumbHeight [IWiaDispatchItem]
*
* Retrieves the thumbnail height for the thumbnail image. If this item doesn't
* support thumbnails then this will return 0.
*
* plHeight: Out, recieces the height of the thumbnail image
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_ThumbHeight( long* plHeight )
{
if ( !plHeight )
return E_POINTER;
*plHeight = (long)m_dwThumbHeight;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_Thumbnail [IWIaDispatchItem]
*
* This recieves a URL to the thumbnail. This returns a magic URL that
* will transfer the bits directly to trident. This will return
* E_INVALIDARG if the given item doesn't support thumbnails. Or NULL if
* we are unable to build a URL for the item.
*
* pbstrPath: Out, recieces teh full path tot the thumbnail
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_Thumbnail( BSTR* pbstrPath )
{
LONG lItemType = 0;
HRESULT hr;
if ( !pbstrPath )
return E_POINTER;
*pbstrPath = NULL;
hr = THR( m_pWiaItem->GetItemType( &lItemType ) );
if ( FAILED( hr ) )
return hr;
if ( !( lItemType & ( WiaItemTypeFile | WiaItemTypeImage ) ) )
{
TraceTag((tagError, "Requested thumbnail on an invaild item type" ));
return E_INVALIDARG;
}
// Do we already have the URL? if not then we can ask our custom
// protocol to create the URL for us.
if ( !m_bstrThumbUrl )
{
hr = THR( CWiaProtocol::CreateURL( m_pWiaItem, &m_bstrThumbUrl ) );
if ( FAILED( hr ) )
return hr;
}
*pbstrPath = SysAllocString( m_bstrThumbUrl );
if ( !*pbstrPath )
return E_OUTOFMEMORY;
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_PictureWidth [IWiaDispatchItem]
*
* Retrieces the width of the picture produced by this camera. this will return
* -1, if its not supported or on error.
*
* plWidth: Out, recieves the width of the picture
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_PictureWidth( long* plWidth )
{
PROPVARIANT vaProp;
HRESULT hr;
if ( !plWidth )
return E_POINTER;
*plWidth = -1;
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_DPC_PICT_WIDTH, &vaProp ) );
if ( SUCCEEDED( hr ) )
{
if ( vaProp.vt == VT_I4 )
*plWidth = vaProp.lVal;
}
PropVariantClear( &vaProp );
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::get_PictureHeight [IWiaDispatchItem]
*
* Retrieves the height of the pictures produced by this camera, or -1
* if the item doesn't support this property.
*
* plHeight: the height of the pictures produced.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaItem::get_PictureHeight( long* plHeight )
{
PROPVARIANT vaProp;
HRESULT hr;
if ( !plHeight )
return E_POINTER;
*plHeight = -1;
hr = THR( GetWiaProperty( m_pWiaStorage, WIA_DPC_PICT_HEIGHT, &vaProp ) );
if ( SUCCEEDED( hr ) )
{
if ( vaProp.vt == VT_I4 )
*plHeight = vaProp.lVal;
}
PropVariantClear( &vaProp );
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaItem::TransferThumbnailToCache
*
* This transfers a thumbnail for this bitmap to our internal cache.
* this will return S_OK if its successful or an error code if something
* goes wrong. It will also fill in the out params with the new thumbnail
*
* pItem: the item to get the thumbnail from
* ppbThumb: Out, recieves a pointer to the in-memory cached bitmap
* pcbThumb: Out, recieves the size of the in-memory bitmap
*--(samclem)-----------------------------------------------------------------*/
HRESULT
CWiaItem::TransferThumbnailToCache( IWiaItem* pItem, BYTE** ppbThumb, DWORD* pcbThumb )
{
enum
{
PropWidth = 0,
PropHeight = 1,
PropThumbnail = 2,
PropFullName = 3,
PropCount = 4,
};
HRESULT hr;
CComPtr<IWiaItem> pItemK = pItem;
DWORD cb = NULL;
BYTE* pbBitmap = NULL;
BYTE* pbData = NULL;
CComQIPtr<IWiaPropertyStorage> pWiaStg;
CWiaCacheManager* pCache = CWiaCacheManager::GetInstance();
PROPVARIANT avaProps[PropCount];
PROPSPEC aspec[PropCount] =
{
{ PRSPEC_PROPID, WIA_IPC_THUMB_WIDTH },
{ PRSPEC_PROPID, WIA_IPC_THUMB_HEIGHT },
{ PRSPEC_PROPID, WIA_IPC_THUMBNAIL },
{ PRSPEC_PROPID, WIA_IPA_FULL_ITEM_NAME },
};
Assert( pItem && ppbThumb && pcbThumb );
*ppbThumb = 0;
*pcbThumb = 0;
// initalize our prop variants
for ( int i = 0; i < PropCount; i++ )
PropVariantInit( &avaProps[i] );
// we need to access the WIA property storage. So if we can't
// access that then we better just bail, because everything else
// will be useless
pWiaStg = pItem;
if ( !pWiaStg )
{
TraceTag((tagError, "item didn't support IWiaPropertyStorage" ));
hr = E_NOINTERFACE;
goto Cleanup;
}
hr = THR( pWiaStg->ReadMultiple( PropCount, aspec, avaProps ) );
if ( FAILED( hr ) )
goto Cleanup;
// validate the types, we want to make sure we actually have a
// thumbnail to save, if we don't bail with a failure code
if ( avaProps[PropThumbnail].vt == VT_EMPTY ||
!( avaProps[PropThumbnail].vt & ( VT_VECTOR | VT_UI1 ) ) )
{
TraceTag((tagError, "item didn't return a useful thumbnail property" ));
hr = E_FAIL;
goto Cleanup;
}
// we now need to build our bitmap from the data, in order to do that
// we need to allocate a chunk of memory. Since we are putting
// this data in the cache, we want to allocate it using the
// cache
cb = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) +
( sizeof( UCHAR ) * ( avaProps[PropThumbnail].caul.cElems ) );
if ( !pCache->AllocThumbnail( cb, &pbBitmap ) )
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
BITMAPINFOHEADER bmi;
BITMAPFILEHEADER bmf;
//
// We need to set the BMP header info. To avoid data misalignment problems
// on 64bit, we'll modify the data structures on the stacj, then just copy them
// to the buffer.
//
// step 0, zero our memory
ZeroMemory(pbBitmap, sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER ) );
ZeroMemory(&bmf, sizeof(bmf));
ZeroMemory(&bmi, sizeof(bmi));
// step 1, setup the bitmap file header
bmf.bfType = k_wBitmapType;
bmf.bfSize = cb;
bmf.bfOffBits = sizeof(BITMAPINFOHEADER);
// step 2, setup the bitmap info header
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biWidth = avaProps[PropWidth].lVal;
bmi.biHeight = avaProps[PropHeight].lVal;
bmi.biPlanes = 1;
bmi.biBitCount = 24;
bmi.biCompression = BI_RGB;
// step 3, copy the new header info. to the buffer
memcpy(pbBitmap, &bmf, sizeof(BITMAPFILEHEADER));
memcpy(pbBitmap + sizeof(BITMAPFILEHEADER), &bmi, sizeof(BITMAPINFOHEADER));
pbData = (pbBitmap + (sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER)));
// copy the data that WIA gave us into the bitmap buffer. once we
// done this, our thumbnail is ready for the cache
memcpy( pbData, avaProps[PropThumbnail].caul.pElems,
sizeof( UCHAR ) * ( avaProps[PropThumbnail].caul.cElems ) );
pCache->AddThumbnail(
avaProps[PropFullName].bstrVal,
pbBitmap,
cb );
// setup the out params
*pcbThumb = cb;
*ppbThumb = pbBitmap;
Cleanup:
FreePropVariantArray( PropCount, avaProps );
if ( FAILED( hr ) )
{
if ( pbBitmap )
pCache->FreeThumbnail( pbBitmap );
}
return hr;
}
//------------------------------- CWiaDataTransfer ----------------------------
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::DoAsyncTransfer
*
* This is called to begin an Async transfer of the data. This will be
* called via a call to create thread.
*
* Note: You can't use any of the interfaces inside of pItem, you must
* query for them through the marshaled interface pointer.
*
* pvParams: AsyncTransferParams structure which has the data we need
*--(samclem)-----------------------------------------------------------------*/
DWORD WINAPI
CWiaDataTransfer::DoAsyncTransfer( LPVOID pvParams )
{
TraceTag((0, "** DoAsyncTransfer --> Begin Thread" ));
HRESULT hr;
HRESULT hrCoInit;
IWiaDataCallback* pCallback = NULL;
IWiaDataTransfer* pWiaTrans = NULL;
IWiaItem* pItem = NULL;
IWiaPropertyStorage* pWiaStg = NULL;
CComObject<CWiaDataTransfer>* pDataTrans = NULL;
WIA_DATA_TRANSFER_INFO wdti;
STGMEDIUM stgMedium;
enum
{
PropTymed = 0,
PropFormat = 1,
PropCount = 2,
};
PROPSPEC spec[PropCount] =
{
{ PRSPEC_PROPID, WIA_IPA_TYMED },
{ PRSPEC_PROPID, WIA_IPA_FORMAT },
};
PROPVARIANT rgvaProps[PropCount];
ASYNCTRANSFERPARAMS* pParams = reinterpret_cast<ASYNCTRANSFERPARAMS*>(pvParams);
Assert( pParams );
// wait 50ms so that things can settle down
Sleep( 50 );
for ( int i = 0; i < PropCount; i++ )
PropVariantInit( &rgvaProps[i] );
hrCoInit = THR( CoInitialize( NULL ) );
if ( FAILED( hrCoInit ) )
goto Cleanup;
// force a yield and let everyone else process what they
// would like to do.
Sleep( 0 );
// first we need to unmarshal our interface
hr = THR( CoGetInterfaceAndReleaseStream( pParams->pStream,
IID_IWiaItem,
reinterpret_cast<void**>(&pItem) ) );
if ( FAILED( hr ) )
goto Cleanup;
// we need the wia property storage
hr = THR( pItem->QueryInterface( IID_IWiaPropertyStorage,
reinterpret_cast<void**>(&pWiaStg) ) );
if ( FAILED( hr ) )
goto Cleanup;
// first query this object for the IWiaDataTransfer interface
hr = THR( pItem->QueryInterface( IID_IWiaDataTransfer,
reinterpret_cast<void**>(&pWiaTrans) ) );
if ( FAILED( hr ) )
goto Cleanup;
ZeroMemory( &wdti, sizeof( wdti ) );
rgvaProps[PropTymed].vt = VT_I4;
rgvaProps[PropFormat].vt = VT_CLSID;
if ( 0 == wcscmp( pParams->bstrFilename, CLIPBOARD_STR_W ) )
{
rgvaProps[PropTymed].lVal = TYMED_CALLBACK;
rgvaProps[PropFormat].puuid = (GUID*)&WiaImgFmt_MEMORYBMP;
}
else
{
rgvaProps[PropTymed].lVal = TYMED_FILE;
rgvaProps[PropFormat].puuid = (GUID*)&WiaImgFmt_BMP;
}
// write these properties out to the storage
hr = THR( pWiaStg->WriteMultiple( PropCount, spec, rgvaProps, WIA_IPC_FIRST ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( CComObject<CWiaDataTransfer>::CreateInstance( &pDataTrans ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pDataTrans->Initialize( pParams->pItem, pParams->bstrFilename ) );
if ( FAILED( hr ) )
goto Cleanup;
hr = THR( pDataTrans->QueryInterface( IID_IWiaDataCallback,
reinterpret_cast<void**>(&pCallback) ) );
if ( FAILED( hr ) )
goto Cleanup;
// we have everything we need, so setup the info and
// get ready to transfer
wdti.ulSize = sizeof( wdti );
wdti.ulBufferSize = ( 1024 * 64 ); // 64k transfer
if ( 0 == _wcsicmp( pParams->bstrFilename, CLIPBOARD_STR_W ) )
// Do the banded transfer.
hr = THR( pWiaTrans->idtGetBandedData( &wdti, pCallback ) );
else
{
ZeroMemory(&stgMedium, sizeof(STGMEDIUM));
stgMedium.tymed = TYMED_FILE;
stgMedium.lpszFileName = pParams->bstrFilename;
// Do the file transfer.
hr = THR( pWiaTrans->idtGetData( &stgMedium, pCallback ) );
}
Cleanup:
if ( pItem )
pItem->Release();
if ( pWiaStg )
pWiaStg->Release();
if ( pCallback )
pCallback->Release();
if ( pWiaTrans )
pWiaTrans->Release();
//
// Since the GUID we supplied as CLSID for PropFormat is a global const
// we must not free it. So we don't call FreePropVariantArry here,
// since there is nothing to free
//
ZeroMemory(rgvaProps, sizeof(rgvaProps));
// free the params that we were passed.
if ( pParams )
{
SysFreeString( pParams->bstrFilename );
pParams->pItem->Release();
CoTaskMemFree( pParams );
}
if ( SUCCEEDED( hrCoInit ) )
CoUninitialize();
TraceTag((0, "** DoAsyncTransfer --> End Thread" ));
return hr;
}
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::TransferComplete
*
* This is called when the transfer completed successfully, this will save
* the data out to the proper place.
*--(samclem)-----------------------------------------------------------------*/
HRESULT
CWiaDataTransfer::TransferComplete()
{
TraceTag((tagWiaDataTrans, "CWiaDataTransfer::TransferComplete *********" ));
HANDLE hFile = INVALID_HANDLE_VALUE;
BOOL bRes = TRUE;
DWORD cbw = 0;
HGLOBAL pBuf = NULL;
BYTE* pbBuf = NULL;
if ( m_pbBuffer )
{
// Check whether we save the data to clipboard or file
if ( 0 == _wcsicmp( m_bstrOutputFile, CLIPBOARD_STR_W ) )
{
pBuf = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, m_sizeBuffer);
if (!pBuf)
return E_OUTOFMEMORY;
if ( bRes = OpenClipboard(NULL) )
{
if ( bRes = EmptyClipboard() )
{
pbBuf = (BYTE*) GlobalLock(pBuf);
if ( pbBuf )
{
memcpy(pbBuf, m_pbBuffer, m_sizeBuffer);
// Callback dibs come back as TOPDOWN, so flip
VerticalFlip(pbBuf);
GlobalUnlock(pBuf);
if ( SetClipboardData(CF_DIB, pBuf) == NULL )
{
TraceTag((0, "TransferComplete - SetClipboardData failed" ));
// redundant statement added to get rid of "error C4390: ';' : empty controlled statement found;"
bRes = FALSE;
}
}
else
TraceTag((0, "TransferComplete - GlobalLock failed" ));
}
else
{
TraceTag((0, "TransferComplete - EmptyClipboard failed" ));
// redundant statement added to get rid of "error C4390: ';' : empty controlled statement found;"
bRes = FALSE;
}
bRes = CloseClipboard();
if ( !bRes )
{
TraceTag((0, "TransferComplete - CloseClipboard failed" ));
// redundant statement added to get rid of "error C4390: ';' : empty controlled statement found;"
bRes = FALSE;
}
}
else
TraceTag((0, "TransferComplete - OpenClipboard failed" ));
GlobalFree(pBuf);
}
m_pItem->SendTransferComplete(m_bstrOutputFile);
CoTaskMemFree( m_pbBuffer );
m_pbBuffer = NULL;
}
else
{
//
// File transfer complete, so signal the event
//
m_pItem->SendTransferComplete(m_bstrOutputFile);
}
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::CWiaDataTransfer
*
* This creates a new CWiaDataTransfer object. This initializes the member
* variables of this object to a know state.
*--(samclem)-----------------------------------------------------------------*/
CWiaDataTransfer::CWiaDataTransfer()
: m_pbBuffer( NULL ), m_sizeBuffer( 0 ), m_pItem( NULL )
{
TRACK_OBJECT("CWiaDataTransfer")
}
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::FinalRelease
*
* This is called when the object is finally released. This is responsible
* for cleaning up any memory allocated by this object.
*
* NOTE: this currently has a hack to get around the fact that the
* IT_MSG_TERMINATION is not always sent by WIA.
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP_(void)
CWiaDataTransfer::FinalRelease()
{
// do we have a buffer?
if ( m_pbBuffer )
{
TraceTag((tagError, "CWiaDataTransfer - buffer should have been freed!!!!" ));
TraceTag((tagError, " **** HACK HACK ***** Calling TansferComplete" ));
TraceTag((tagError, " **** This could write a bogus file which might be unsable" ));
TransferComplete();
}
if ( m_pItem )
m_pItem->Release();
m_pItem = NULL;
}
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::Initialize
*
* This handles the internal initialization of the CWiaDataTransfer. This
* should be called immediatly after it is created but before you attempt
* to do anything with it.
*
* pItem: the CWiaItem that we want to transfer from (AddRef'd)
* bstrFilename: the file where we want to save the data
*--(samclem)-----------------------------------------------------------------*/
HRESULT
CWiaDataTransfer::Initialize( CWiaItem* pItem, BSTR bstrFilename )
{
// copy the filename into our output buffer
m_bstrOutputFile = bstrFilename;
// set our owner item, we want to ensure the item exists
// as long as we do, so we will AddRef here and release
// in final release.
m_pItem = pItem;
m_pItem->AddRef();
return S_OK;
}
/*-----------------------------------------------------------------------------
* CWiaDataTransfer::BandedDataCallback [IWiaDataTransfer]
*
* This is the callback from WIA which tells us what is going on. This
* copies the memory into our own buffer so that we can eventually save it
* out. In any error conditions this returns S_FALSE to abort the transfer.
*
* lMessage: what is happening one of IT_MSG_xxx values
* lStatus: Sub status of whats happening
* lPercentComplete: Percent of the operation that has completed
* lOffset: the offset inside of pbBuffer where this operation is
* lLength: The length of the valid data inside of the buffer
* lReserved: Reserved.
* lResLength: Reserved.
* pbBuffer: The buffer we can read from in order to process the
* data. Exact use depends on lMessage
*--(samclem)-----------------------------------------------------------------*/
STDMETHODIMP
CWiaDataTransfer::BandedDataCallback( LONG lMessage, LONG lStatus, LONG lPercentComplete,
LONG lOffset, LONG lLength, LONG lReserved, LONG lResLength, BYTE *pbBuffer )
{
switch ( lMessage )
{
case IT_MSG_DATA:
TraceTag((tagWiaDataTrans, "IT_MSG_DATA: %ld%% complete", lPercentComplete ));
if ( m_pbBuffer )
{
// copy the data into our buffer
memcpy( m_pbBuffer + lOffset, pbBuffer, lLength );
}
break;
case IT_MSG_DATA_HEADER:
{
TraceTag((tagWiaDataTrans, "IT_MSG_DATA_HEADER" ));
UNALIGNED WIA_DATA_CALLBACK_HEADER* pHeader =
reinterpret_cast<WIA_DATA_CALLBACK_HEADER*>(pbBuffer);
TraceTag((tagWiaDataTrans, "-------> %ld bytes", pHeader->lBufferSize ));
// allocate our buffer
m_sizeBuffer = pHeader->lBufferSize;
m_pbBuffer = static_cast<BYTE*>(CoTaskMemAlloc( pHeader->lBufferSize ));
if ( !m_pbBuffer )
return S_FALSE; // abort
}
break;
case IT_MSG_NEW_PAGE:
TraceTag((tagWiaDataTrans, "IT_MSG_NEW_PAGE" ));
break;
case IT_MSG_STATUS:
TraceTag((tagWiaDataTrans, "IT_MSG_STATUS: %ld%% complete", lPercentComplete ));
break;
case IT_MSG_TERMINATION:
TraceTag((tagWiaDataTrans, "IT_MSG_TERMINATION: %ld%% complete", lPercentComplete ));
if ( FAILED( THR( TransferComplete() ) ) )
return S_FALSE;
break;
}
return S_OK;
}
/*-----------------------------------------------------------------------------
* VerticalFlip
*
* Vertically flips a DIB buffer. It assumes a non-NULL pointer argument.
*
* pBuf: pointer to the DIB image
*--(byronc)-----------------------------------------------------------------*/
HRESULT VerticalFlip(
BYTE *pBuf)
{
HRESULT hr = S_OK;
LONG lHeight;
LONG lWidth;
BITMAPINFOHEADER *pbmih;
PBYTE pTop = NULL;
PBYTE pBottom = NULL;
pbmih = (BITMAPINFOHEADER*) pBuf;
//
// If not a TOPDOWN dib then no need to flip
//
if (pbmih->biHeight > 0) {
return S_OK;
}
//
// Set Top pointer, width and height. Make sure the bitmap height
// is positive.
//
pTop = pBuf + pbmih->biSize + ((pbmih->biClrUsed) * sizeof(RGBQUAD));
lWidth = ((pbmih->biWidth * pbmih->biBitCount + 31) / 32) * 4;
pbmih->biHeight = abs(pbmih->biHeight);
lHeight = pbmih->biHeight;
//
// Allocat a temp scan line buffer
//
PBYTE pTempBuffer = (PBYTE)LocalAlloc(LPTR, lWidth);
if (pTempBuffer) {
LONG index;
pBottom = pTop + (lHeight-1) * lWidth;
for (index = 0;index < (lHeight/2);index++) {
//
// Swap Top and Bottom lines
//
memcpy(pTempBuffer, pTop, lWidth);
memcpy(pTop, pBottom, lWidth);
memcpy(pBottom,pTempBuffer, lWidth);
pTop += lWidth;
pBottom -= lWidth;
}
LocalFree(pTempBuffer);
} else {
hr = E_OUTOFMEMORY;
}
return hr;
}