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.
882 lines
28 KiB
882 lines
28 KiB
/*-----------------------------------------------------------------------------
|
|
*
|
|
* File: wia.cpp
|
|
* Author: Samuel Clement
|
|
* Date: Thu Aug 12 11:35:38 1999
|
|
* Description:
|
|
* Implementation of the CWia class
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*
|
|
* History:
|
|
* 12 Aug 1999: Created.
|
|
* 27 Aug 1999: Added, _DebugDialog implementation
|
|
* 10 Sep 1999: Use CWiaCacheManager when creating devices (samclem)
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
// register our window messages
|
|
const UINT WEM_TRANSFERCOMPLETE = RegisterWindowMessage( TEXT("wem_transfercomplete") );
|
|
|
|
// the window property to retrieve the CWia pointer
|
|
const TCHAR* CWIA_WNDPROP = TEXT("cwia_ptr");
|
|
const TCHAR* CWIA_EVENTWNDCLS = TEXT("cwia hidden window");
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::CWia
|
|
*
|
|
* This creates a new CWia object. this initializes the variables to a
|
|
* known state so they can do things.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
CWia::CWia()
|
|
: m_pWiaDevMgr( NULL ),
|
|
m_pDeviceCollectionCache( NULL ),
|
|
m_hwndEvent(NULL),
|
|
m_pCWiaEventCallback(NULL)
|
|
{
|
|
TRACK_OBJECT( "CWia" );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::FinalRelease
|
|
*
|
|
* This is called when we are finally released. this will clear all the
|
|
* pointers that we have and set them to null so that we know they were
|
|
* released.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP_(void)
|
|
CWia::FinalRelease()
|
|
{
|
|
if ( m_hwndEvent )
|
|
DestroyWindow( m_hwndEvent );
|
|
|
|
//
|
|
// Make sure we unregister for WIA Devices. Note that it is safe to call
|
|
// unregister multiple times.
|
|
//
|
|
if (m_pCWiaEventCallback)
|
|
{
|
|
m_pCWiaEventCallback->UnRegisterForConnectDisconnect();
|
|
m_pCWiaEventCallback->setOwner(NULL);
|
|
m_pCWiaEventCallback->Release();
|
|
}
|
|
m_pCWiaEventCallback = NULL;
|
|
|
|
|
|
if ( m_pWiaDevMgr )
|
|
m_pWiaDevMgr->Release();
|
|
m_pWiaDevMgr = NULL;
|
|
if ( m_pDeviceCollectionCache )
|
|
m_pDeviceCollectionCache->Release();
|
|
m_pDeviceCollectionCache = NULL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::FinalContruct
|
|
*
|
|
* This creates the IWiaDevMgr that we need to perform our work.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWia::FinalConstruct()
|
|
{
|
|
HRESULT hr;
|
|
WNDCLASSEX wc;
|
|
|
|
// first we want to create our hidden event window
|
|
if ( !GetClassInfoEx( _Module.GetModuleInstance(),
|
|
CWIA_EVENTWNDCLS, &wc ) )
|
|
{
|
|
// we need to register this window
|
|
ZeroMemory( &wc, sizeof( wc ) );
|
|
wc.cbSize = sizeof( wc );
|
|
wc.lpszClassName = CWIA_EVENTWNDCLS;
|
|
wc.hInstance = _Module.GetModuleInstance();
|
|
wc.lpfnWndProc = CWia::EventWndProc;
|
|
|
|
if ( !RegisterClassEx( &wc ) )
|
|
{
|
|
TraceTag(( tagError, "unable to register window class" ));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// now we can create our window
|
|
m_hwndEvent = CreateWindowEx( 0,
|
|
CWIA_EVENTWNDCLS,
|
|
NULL,
|
|
0,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
_Module.GetModuleInstance(),
|
|
this );
|
|
|
|
if ( !m_hwndEvent )
|
|
{
|
|
TraceTag(( tagError, "Error creating the window" ));
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = THR( CoCreateInstance( CLSID_WiaDevMgr,
|
|
NULL,
|
|
CLSCTX_SERVER,
|
|
IID_IWiaDevMgr,
|
|
reinterpret_cast<void**>(&m_pWiaDevMgr) ) );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
TraceTag(( tagError, "Failed to create WiaDevMgr instance" ));
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Setup the event callbacks that this object cares about. we
|
|
* register both connect/disconnect on this object. since the
|
|
* callback tells us the GUID of the event, we can add
|
|
* more logic there. This is more efficent that having a
|
|
* proxy object which handles the events.
|
|
*/
|
|
hr = CComObject<CWiaEventCallback>::CreateInstance(&m_pCWiaEventCallback);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pCWiaEventCallback->AddRef();
|
|
m_pCWiaEventCallback->setOwner(this);
|
|
hr = m_pCWiaEventCallback->RegisterForConnectDisconnect(m_pWiaDevMgr);
|
|
}
|
|
else
|
|
{
|
|
TraceTag(( tagError, "Failed to create WiaEventCallback instance" ));
|
|
return hr;
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
|
|
m_pWiaDevMgr->Release();
|
|
m_pWiaDevMgr = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::_DebugDialog
|
|
*
|
|
* This shows a debugging dialog if you are using the debug build, or simply
|
|
* returns S_OK in the retail build.
|
|
*
|
|
* fWait: true if we want to wait for the dialog to finish in order to
|
|
* return. Or false to return immediatly.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWia::_DebugDialog( BOOL fWait )
|
|
{
|
|
|
|
#if defined(_DEBUG)
|
|
DoTracePointsDialog( fWait );
|
|
#endif //defined(_DEBUG)
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::get_Devices
|
|
*
|
|
* This returns a collection of the devices currently connected. this can
|
|
* return an empty collection of there are no devices currently attached.
|
|
*
|
|
* This will cache the collection object that we create. This allows for
|
|
* increased performace since we don't want to recreate it each time, that
|
|
* requires called to an Out-Of-Proc server which is expensive. Therefore
|
|
* since this method is called a lot, we cache the results in:
|
|
*
|
|
* m_pDeviceCollectionCache
|
|
*
|
|
* ppCol: out, a point to recieve the collection interface.
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWia::get_Devices( ICollection** ppCol )
|
|
{
|
|
HRESULT hr;
|
|
CComObject<CCollection>* pCollection = NULL;
|
|
IEnumWIA_DEV_INFO* pEnum = NULL;
|
|
IWiaPropertyStorage* pWiaStg = NULL;
|
|
IDispatch** rgpDispatch = NULL;
|
|
CComObject<CWiaDeviceInfo>* pDevInfo = NULL;
|
|
unsigned long cDevices = 0;
|
|
unsigned long celtFetched = 0;
|
|
unsigned long iDevice = 0;
|
|
|
|
// validate our arguments
|
|
if ( NULL == ppCol )
|
|
return E_POINTER;
|
|
*ppCol = NULL;
|
|
|
|
// do we already have a collection cache? if so then we want
|
|
// to use that.
|
|
if ( m_pDeviceCollectionCache )
|
|
{
|
|
*ppCol = m_pDeviceCollectionCache;
|
|
(*ppCol)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
// first we need an instance of the collection
|
|
hr = THR( CComObject<CCollection>::CreateInstance( &pCollection ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// are we vaild?
|
|
Assert( m_pWiaDevMgr );
|
|
hr = THR( m_pWiaDevMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &pEnum ) );
|
|
if ( FAILED(hr) )
|
|
goto Cleanup;
|
|
|
|
// we can now enumerate over the device info, if we have them
|
|
// otherwise we don't want to do anything
|
|
hr = THR( pEnum->GetCount( &cDevices ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
if ( cDevices )
|
|
{
|
|
// we need storage for these items
|
|
rgpDispatch = static_cast<IDispatch**>(CoTaskMemAlloc( cDevices * sizeof( IDispatch* ) ));
|
|
if ( !rgpDispatch )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ZeroMemory( rgpDispatch, sizeof( IDispatch* ) * cDevices );
|
|
while ( SUCCEEDED( hr ) && hr != S_FALSE )
|
|
{
|
|
// release the old stream if it is hanging around
|
|
if ( pWiaStg )
|
|
pWiaStg->Release();
|
|
pWiaStg = NULL;
|
|
|
|
hr = THR( pEnum->Next( 1, &pWiaStg, &celtFetched ) );
|
|
if ( SUCCEEDED( hr ) && hr == S_OK )
|
|
{
|
|
// we got this item successfully, so we need to create
|
|
// a CWiaDeviceInfo and add it to our dispatch array
|
|
Assert( celtFetched == 1 );
|
|
|
|
hr = THR( CComObject<CWiaDeviceInfo>::CreateInstance( &pDevInfo ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pDevInfo->AttachTo( pWiaStg, static_cast<IWia*>(this) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pDevInfo->QueryInterface( IID_IDispatch,
|
|
reinterpret_cast<void**>(&rgpDispatch[iDevice++]) ) );
|
|
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if( !pCollection->SetDispatchArray(rgpDispatch, cDevices))
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// fill the out param with the proper value
|
|
hr = THR( pCollection->QueryInterface( IID_ICollection,
|
|
reinterpret_cast<void**>(&m_pDeviceCollectionCache) ) );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
*ppCol = m_pDeviceCollectionCache;
|
|
(*ppCol)->AddRef();
|
|
}
|
|
|
|
Cleanup:
|
|
if ( pEnum )
|
|
pEnum->Release();
|
|
if ( pWiaStg )
|
|
pWiaStg->Release();
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( m_pDeviceCollectionCache )
|
|
m_pDeviceCollectionCache->Release();
|
|
m_pDeviceCollectionCache = NULL;
|
|
if ( pCollection )
|
|
delete pCollection;
|
|
if ( rgpDispatch )
|
|
{
|
|
for ( unsigned long i = 0; i < cDevices; i++ )
|
|
if ( rgpDispatch[i] )
|
|
rgpDispatch[i]->Release();
|
|
CoTaskMemFree( rgpDispatch );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::Create [IWia]
|
|
*
|
|
* The handles creating a device. This will create a dispatch object which
|
|
* can represent the device.
|
|
*
|
|
* This can take several different paramaters to determine what device to
|
|
* create.
|
|
*
|
|
* VT_UNKNOWN, VT_DISPATCH --> An IWiaDeviceInfo dispatch object which
|
|
* holds information about the device.
|
|
* VT_BSTR --> The DeviceID of the device to create
|
|
* VT_I4 --> The index of the device in the Devices()
|
|
* collection.
|
|
* VT_xx --> Not currently supported.
|
|
*
|
|
* pvaDevice: A variant which contains the device to create
|
|
* ppDevice: Out, recieves the newly created device object
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWia::Create( VARIANT* pvaDevice, IWiaDispatchItem** ppDevice )
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
IWiaDeviceInfo* pDeviceInfo = NULL;
|
|
ICollection* pCollection = NULL;
|
|
IDispatch* pDispatch = NULL;
|
|
BSTR bstrDeviceId = NULL;
|
|
IWiaItem* pWiaItem = NULL;
|
|
CComObject<CWiaItem>* pItem = NULL;
|
|
CWiaCacheManager* pCache = CWiaCacheManager::GetInstance();
|
|
|
|
if ( !pvaDevice || !ppDevice )
|
|
return E_POINTER;
|
|
|
|
|
|
//BUG (Aug, 18) samclem:
|
|
//
|
|
// make sure that the variant is the proper type, or at least
|
|
// one that we want to deal with. this isn't perfect and probally
|
|
// be revistied at some point in life. this will miss handle things
|
|
// like:
|
|
//
|
|
// camera = wiaObject.create( "0" );
|
|
//
|
|
|
|
// If nothing was passed in, we end up showing the selection UI.
|
|
// Use an empty BSTR to indicate this. Note that script can also
|
|
// pass an empty string ("") to get the selection UI.
|
|
if ( pvaDevice->vt == VT_EMPTY || pvaDevice->vt == VT_NULL ||
|
|
( pvaDevice->vt == VT_ERROR && pvaDevice->scode == DISP_E_PARAMNOTFOUND ) )
|
|
{
|
|
pvaDevice->vt = VT_BSTR;
|
|
pvaDevice->bstrVal = NULL;
|
|
}
|
|
|
|
if ( pvaDevice->vt != VT_DISPATCH &&
|
|
pvaDevice->vt != VT_UNKNOWN &&
|
|
pvaDevice->vt != VT_BSTR )
|
|
{
|
|
hr = THR( VariantChangeType( pvaDevice, pvaDevice, 0, VT_I4 ) );
|
|
if ( FAILED( hr ) )
|
|
return hr;
|
|
}
|
|
|
|
if ( pvaDevice->vt == VT_DISPATCH )
|
|
{
|
|
// pvaDevice->pdispVal == NULL if we're supposed to show WIA's device
|
|
// selection, so only QI if pdispVal is valid.
|
|
if (pvaDevice->pdispVal != NULL)
|
|
{
|
|
hr = THR( pvaDevice->pdispVal->QueryInterface( IID_IWiaDeviceInfo,
|
|
reinterpret_cast<void**>(&pDeviceInfo) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if ( pvaDevice->vt == VT_UNKNOWN )
|
|
{
|
|
hr = THR( pvaDevice->punkVal->QueryInterface( IID_IWiaDeviceInfo,
|
|
reinterpret_cast<void**>(&pDeviceInfo) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
else if ( pvaDevice->vt == VT_BSTR )
|
|
{
|
|
if ( pvaDevice->bstrVal && *pvaDevice->bstrVal )
|
|
bstrDeviceId = SysAllocString( pvaDevice->bstrVal );
|
|
}
|
|
else if ( pvaDevice->vt == VT_I4 )
|
|
{
|
|
hr = THR( get_Devices( &pCollection ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// get the item with that index
|
|
hr = THR( pCollection->get_Item( pvaDevice->lVal, &pDispatch ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// did we get an item, if we didn't then we were out of
|
|
// range in our collection would return a null dispatch
|
|
//BUG (Aug, 18) samclem: Perhaps CCollection::get_Item() should
|
|
// return false in this case.
|
|
if ( !pDispatch )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pDispatch->QueryInterface( IID_IWiaDeviceInfo,
|
|
reinterpret_cast<void**>(&pDeviceInfo) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
goto Cleanup;
|
|
|
|
// if we have a valid IWiaDeviceInfo then we can query that for
|
|
// the bstr to create.
|
|
if ( pDeviceInfo )
|
|
{
|
|
hr = THR( pDeviceInfo->get_Id( &bstrDeviceId ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
|
|
// either we call CreateDevice from the WIA device manager, or we
|
|
// bring up WIA's device selection UI to return a IWiaItem interface.
|
|
if (bstrDeviceId != NULL)
|
|
{
|
|
if ( !pCache->GetDevice( bstrDeviceId, &pWiaItem ) )
|
|
{
|
|
// at this point we should have a valid device id to create
|
|
// our device from
|
|
hr = THR( m_pWiaDevMgr->CreateDevice( bstrDeviceId, &pWiaItem ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// bring up the selection UI
|
|
hr = THR( m_pWiaDevMgr->SelectDeviceDlg(NULL,
|
|
0,
|
|
0,
|
|
&bstrDeviceId,
|
|
&pWiaItem ) );
|
|
// have to check against S_OK since cancel produces S_FALSE
|
|
if ( hr != S_OK )
|
|
goto Cleanup;
|
|
}
|
|
|
|
// add our created device to our cache so that we don't have
|
|
// to create it again.
|
|
// NOTE: We effectively disable the device list cache
|
|
// by not adding the device here. The cache doesn't really buy us
|
|
// anything since the driver caches thumbnails, and you shouldn't cache
|
|
// devices, so we simply ignore it here.
|
|
//pCache->AddDevice( bstrDeviceId, pWiaItem );
|
|
|
|
hr = THR( CComObject<CWiaItem>::CreateInstance( &pItem ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pItem->AttachTo( this, pWiaItem ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pItem->QueryInterface( IID_IDispatch,
|
|
reinterpret_cast<void**>(ppDevice) ) );
|
|
|
|
Cleanup:
|
|
if ( pItem && FAILED( hr ) )
|
|
delete pItem;
|
|
if ( pDispatch )
|
|
pDispatch->Release();
|
|
if ( pWiaItem )
|
|
pWiaItem->Release();
|
|
if ( pDeviceInfo )
|
|
pDeviceInfo->Release();
|
|
if ( pCollection )
|
|
pCollection->Release();
|
|
if ( bstrDeviceId )
|
|
SysFreeString( bstrDeviceId );
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::ImageEventCallback [IWiaEventCallback]
|
|
*
|
|
* This is called by Wia when something interesting happens. this is used to
|
|
* fire these events off to scripting for them to do do something.
|
|
*
|
|
* pEventGUID: the GUID of the event which happend
|
|
* bstrEventDescription: A string description of the event?? [not in docs]
|
|
* bstrDeviceID: The device id of the device?? [not in docs]
|
|
* bstrDeviceDescription: The description of the device?? [nid]
|
|
* dwDeviceType: ?? [nid]
|
|
* pulEventType: ?? [nid]
|
|
* Reserved: Reserved (0)
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CWia::ImageEventCallback( const GUID* pEventGUID, BSTR bstrEventDescription,
|
|
BSTR bstrDeviceID, BSTR bstrDeviceDescription, DWORD dwDeviceType,
|
|
BSTR bstrFullItemName,
|
|
/*in,out*/ ULONG* pulEventType, ULONG Reserved )
|
|
{
|
|
#if _DEBUG
|
|
USES_CONVERSION;
|
|
#endif
|
|
|
|
if ( m_pDeviceCollectionCache )
|
|
{
|
|
m_pDeviceCollectionCache->Release();
|
|
m_pDeviceCollectionCache = NULL;
|
|
}
|
|
|
|
// we are listening to both connections, and disconnections so we need
|
|
// to decice what is happening
|
|
//TODO: we want to handle these using the window message not by directly
|
|
// sending them through here.
|
|
if ( *pEventGUID == WIA_EVENT_DEVICE_CONNECTED )
|
|
{
|
|
TraceTag((0, "firing event connected: %s", OLE2A( bstrDeviceID )));
|
|
Fire_OnDeviceConnected( bstrDeviceID );
|
|
}
|
|
else if ( *pEventGUID == WIA_EVENT_DEVICE_DISCONNECTED )
|
|
{
|
|
TraceTag((0, "firing event disconnected: %s", OLE2A( bstrDeviceID )));
|
|
CWiaCacheManager::GetInstance()->RemoveDevice( bstrDeviceID );
|
|
Fire_OnDeviceDisconnected( bstrDeviceID );
|
|
}
|
|
else
|
|
{
|
|
TraceTag((0, "ImageEventCallback -> unexpected event type" ) );
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CWia::EventWndProc
|
|
*
|
|
* This is the window proc that is used for the hidden window which
|
|
* handles posting the events. This recieves messages that should be posted
|
|
* back to the client. This ensures that the notifications get posted back
|
|
* from the expected thread.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
LRESULT CALLBACK CWia::EventWndProc( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
CWia* pWia = reinterpret_cast<CWia*>(GetProp( hwnd, CWIA_WNDPROP ));
|
|
|
|
switch ( iMsg )
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
LPCREATESTRUCT pcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
|
|
pWia = reinterpret_cast<CWia*>(pcs->lpCreateParams);
|
|
if ( !pWia )
|
|
return -1;
|
|
if ( !SetProp( hwnd, CWIA_WNDPROP, reinterpret_cast<HANDLE>(pWia) ) )
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
if ( pWia )
|
|
RemoveProp( hwnd, CWIA_WNDPROP );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// since our custom window messages are obtained using
|
|
// RegisterWindowMessage(), they are not constant and therfore
|
|
// can't be processed in a switch() statement.
|
|
if ( WEM_TRANSFERCOMPLETE == iMsg )
|
|
{
|
|
if ( pWia )
|
|
{
|
|
TraceTag((0, "EventWndProc - firing onTransferComplete"));
|
|
pWia->Fire_OnTransferComplete(
|
|
reinterpret_cast<IDispatch*>(wParam),
|
|
reinterpret_cast<BSTR>(lParam) );
|
|
}
|
|
if ( lParam )
|
|
{
|
|
SysFreeString(reinterpret_cast<BSTR>(lParam));
|
|
lParam = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc( hwnd, iMsg, wParam, lParam );
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
|
|
CSafeWia::CSafeWia() :
|
|
m_pWiaDevMgr( NULL ),
|
|
m_pWiaDevConEvent( NULL ),
|
|
m_pWiaDevDisEvent( NULL ),
|
|
m_pDeviceCollectionCache( NULL ),
|
|
m_SafeInstance(TRUE)
|
|
{
|
|
|
|
TRACK_OBJECT( "CSafeWia" );
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CSafeWia::FinalRelease()
|
|
{
|
|
if ( m_hwndEvent )
|
|
DestroyWindow( m_hwndEvent );
|
|
|
|
if ( m_pWiaDevMgr )
|
|
m_pWiaDevMgr->Release();
|
|
m_pWiaDevMgr = NULL;
|
|
if ( m_pWiaDevConEvent )
|
|
m_pWiaDevConEvent->Release();
|
|
m_pWiaDevConEvent = NULL;
|
|
if ( m_pWiaDevDisEvent )
|
|
m_pWiaDevDisEvent->Release();
|
|
m_pWiaDevDisEvent = NULL;
|
|
if ( m_pDeviceCollectionCache )
|
|
m_pDeviceCollectionCache->Release();
|
|
m_pDeviceCollectionCache = NULL;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CSafeWia::FinalContruct
|
|
*
|
|
* This creates the IWiaDevMgr that we need to perform our work.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CSafeWia::FinalConstruct()
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = THR( CoCreateInstance( CLSID_WiaDevMgr,
|
|
NULL,
|
|
CLSCTX_SERVER,
|
|
IID_IWiaDevMgr,
|
|
reinterpret_cast<void**>(&m_pWiaDevMgr) ) );
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
TraceTag(( tagError, "Failed to create WiaDevMgr instance" ));
|
|
return hr;
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( m_pWiaDevConEvent )
|
|
m_pWiaDevConEvent->Release();
|
|
m_pWiaDevConEvent = NULL;
|
|
if ( m_pWiaDevDisEvent )
|
|
m_pWiaDevDisEvent->Release();
|
|
m_pWiaDevDisEvent = NULL;
|
|
|
|
m_pWiaDevMgr->Release();
|
|
m_pWiaDevMgr = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CSafeWia::_DebugDialog
|
|
*
|
|
* This shows a debugging dialog if you are using the debug build, or simply
|
|
* returns S_OK in the retail build.
|
|
*
|
|
* fWait: true if we want to wait for the dialog to finish in order to
|
|
* return. Or false to return immediatly.
|
|
*--(samclem)-----------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CSafeWia::_DebugDialog( BOOL fWait )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* CSafeWia::get_Devices
|
|
*
|
|
* This returns a collection of the devices currently connected. this can
|
|
* return an empty collection of there are no devices currently attached.
|
|
*
|
|
* This will cache the collection object that we create. This allows for
|
|
* increased performace since we don't want to recreate it each time, that
|
|
* requires called to an Out-Of-Proc server which is expensive. Therefore
|
|
* since this method is called a lot, we cache the results in:
|
|
*
|
|
* m_pDeviceCollectionCache
|
|
*
|
|
* ppCol: out, a point to recieve the collection interface.
|
|
*---------------------------------------------------------------------------*/
|
|
STDMETHODIMP
|
|
CSafeWia::get_Devices( ICollection** ppCol )
|
|
{
|
|
HRESULT hr;
|
|
CComObject<CCollection>* pCollection = NULL;
|
|
IEnumWIA_DEV_INFO* pEnum = NULL;
|
|
IWiaPropertyStorage* pWiaStg = NULL;
|
|
IDispatch** rgpDispatch = NULL;
|
|
CComObject<CWiaDeviceInfo>* pDevInfo = NULL;
|
|
unsigned long cDevices = 0;
|
|
unsigned long celtFetched = 0;
|
|
unsigned long iDevice = 0;
|
|
|
|
// validate our arguments
|
|
if ( NULL == ppCol )
|
|
return E_POINTER;
|
|
*ppCol = NULL;
|
|
|
|
// do we already have a collection cache? if so then we want
|
|
// to use that.
|
|
if ( m_pDeviceCollectionCache )
|
|
{
|
|
*ppCol = m_pDeviceCollectionCache;
|
|
(*ppCol)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
// first we need an instance of the collection
|
|
hr = THR( CComObject<CCollection>::CreateInstance( &pCollection ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
// are we vaild?
|
|
Assert( m_pWiaDevMgr );
|
|
hr = THR( m_pWiaDevMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &pEnum ) );
|
|
if ( FAILED(hr) )
|
|
goto Cleanup;
|
|
|
|
// we can now enumerate over the device info, if we have them
|
|
// otherwise we don't want to do anything
|
|
hr = THR( pEnum->GetCount( &cDevices ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
if ( cDevices )
|
|
{
|
|
// we need storage for these items
|
|
rgpDispatch = static_cast<IDispatch**>(CoTaskMemAlloc( cDevices * sizeof( IDispatch* ) ));
|
|
if ( !rgpDispatch )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ZeroMemory( rgpDispatch, sizeof( IDispatch* ) * cDevices );
|
|
while ( SUCCEEDED( hr ) && hr != S_FALSE )
|
|
{
|
|
// release the old stream if it is hanging around
|
|
if ( pWiaStg )
|
|
pWiaStg->Release();
|
|
|
|
hr = THR( pEnum->Next( 1, &pWiaStg, &celtFetched ) );
|
|
if ( SUCCEEDED( hr ) && hr == S_OK )
|
|
{
|
|
// we got this item successfully, so we need to create
|
|
// a CWiaDeviceInfo and add it to our dispatch array
|
|
Assert( celtFetched == 1 );
|
|
|
|
hr = THR( CComObject<CWiaDeviceInfo>::CreateInstance( &pDevInfo ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pDevInfo->AttachTo( pWiaStg, static_cast<IWia*>(this) ) );
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
|
|
hr = THR( pDevInfo->QueryInterface( IID_IDispatch,
|
|
reinterpret_cast<void**>(&rgpDispatch[iDevice++]) ) );
|
|
|
|
if ( FAILED( hr ) )
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if( !pCollection->SetDispatchArray(rgpDispatch, cDevices))
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// fill the out param with the proper value
|
|
hr = THR( pCollection->QueryInterface( IID_ICollection,
|
|
reinterpret_cast<void**>(&m_pDeviceCollectionCache) ) );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
*ppCol = m_pDeviceCollectionCache;
|
|
(*ppCol)->AddRef();
|
|
}
|
|
|
|
Cleanup:
|
|
if ( pEnum )
|
|
pEnum->Release();
|
|
if ( pWiaStg )
|
|
pWiaStg->Release();
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if ( m_pDeviceCollectionCache )
|
|
m_pDeviceCollectionCache->Release();
|
|
m_pDeviceCollectionCache = NULL;
|
|
if ( pCollection )
|
|
delete pCollection;
|
|
if ( rgpDispatch )
|
|
{
|
|
for ( unsigned long i = 0; i < cDevices; i++ )
|
|
if ( rgpDispatch[i] )
|
|
rgpDispatch[i]->Release();
|
|
CoTaskMemFree( rgpDispatch );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CSafeWia::Create( VARIANT* pvaDevice, IWiaDispatchItem** ppDevice )
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
|
|
#ifdef MAXDEBUG
|
|
::OutputDebugString(TEXT("WIA script object: CSafeWia::Create rejected\n\r "));
|
|
#endif
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CSafeWia::ImageEventCallback( const GUID* pEventGUID, BSTR bstrEventDescription,
|
|
BSTR bstrDeviceID, BSTR bstrDeviceDescription, DWORD dwDeviceType,
|
|
BSTR bstrFullItemName,
|
|
/*in,out*/ ULONG* pulEventType, ULONG Reserved )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
LRESULT CALLBACK CSafeWia::EventWndProc( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
return DefWindowProc( hwnd, iMsg, wParam, lParam );
|
|
}
|
|
|
|
|