windows-nt/Source/XPSP1/NT/printscan/wia/drivers/video/usd/minidrv.cpp
2020-09-26 16:20:57 +08:00

2483 lines
68 KiB
C++

/*****************************************************************************
*
* (C) COPYRIGHT MICROSOFT CORPORATION, 1998 - 2000
*
* TITLE: minidrv.cpp
*
* VERSION: 1.0
*
* AUTHOR: RickTu
*
* DATE: 9/9/99
*
* DESCRIPTION: This module implements IWiaMiniDrv for this device.
*
*****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#include "wiamindr_i.c"
#include <sddl.h>
#include <shlobj.h>
///////////////////////////////
// Constants
//
const TCHAR* EVENT_PREFIX_GLOBAL = TEXT("Global\\");
const TCHAR* EVENT_SUFFIX_TAKE_PICTURE = TEXT("_TAKE_PICTURE");
const TCHAR* EVENT_SUFFIX_PICTURE_READY = TEXT("_PICTURE_READY");
const UINT TAKE_PICTURE_TIMEOUT = 1000 * 15; // 15 seconds
//const UINT DEFAULT_LOCK_TIMEOUT = 1000 * 2; // 2 seconds
// This is the Security Descriptor Language
// - Each ACE (access control entry) is represented in by parentheses.
// - A = Allow ACE (as opposed to a Deny ACE)
// - OICI = Allow Object Inheritance and Container Inheritence
// - GA = Generic All Access (Full Control)
// - SY = System account (SID)
// - BA = Builtin Administrators Group
// - CO = Creator/Owner
// - GR = Generic Read
// - GW = Generic Write
// - GX = Generic Execute.
// - IU = Interactive Users (User's logged on at the computer)
//
// More info, go to http://msdn.microsoft.com/library/psdk/winbase/accctrl_2n1v.htm
//
//
//
const TCHAR *OBJECT_DACLS= TEXT("D:(A;OICI;GA;;;SY)") // SYSTEM
TEXT("(A;OICI;GA;;;BA)") // Admin
TEXT("(A;OICI;GRGWGXDTSDCCLC;;;WD)") // Everyone
TEXT("(A;OICI;GRGWGXDTSDCCLC;;;PU)") // Power Users
TEXT("(A;OICI;GRGWGXDTSDCCLC;;;BU)"); // Users
/*****************************************************************************
DirectoryExists
Checks to see whether the given fully qualified directory exists.
*****************************************************************************/
BOOL DirectoryExists(LPCTSTR pszDirectoryName)
{
BOOL bExists = FALSE;
//
// Try to determine if this directory exists
//
if (pszDirectoryName)
{
DWORD dwFileAttributes = GetFileAttributes(pszDirectoryName);
if (dwFileAttributes == 0xFFFFFFFF ||
!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
bExists = FALSE;
}
else
{
bExists = TRUE;
}
}
else
{
bExists = FALSE;
}
return bExists;
}
/*****************************************************************************
RecursiveCreateDirectory
Take a fully qualified path and create the directory in pieces as needed.
*****************************************************************************/
BOOL RecursiveCreateDirectory(CSimpleString *pstrDirectoryName)
{
ASSERT(pstrDirectoryName != NULL);
//
// If this directory already exists, return true.
//
if (DirectoryExists(*pstrDirectoryName))
{
return TRUE;
}
//
// Otherwise try to create it.
//
CreateDirectory(*pstrDirectoryName, NULL );
//
// If it now exists, return true
//
if (DirectoryExists(*pstrDirectoryName))
{
return TRUE;
}
else
{
//
// Remove the last subdir and try again
//
int nFind = pstrDirectoryName->ReverseFind(TEXT('\\'));
if (nFind >= 0)
{
RecursiveCreateDirectory(&(pstrDirectoryName->Left(nFind)));
//
// Now try to create it.
//
CreateDirectory(*pstrDirectoryName, NULL);
}
}
//
//Does it exist now?
//
return DirectoryExists(*pstrDirectoryName);
}
///////////////////////////////
// SetDirectorySecurity
//
HRESULT SetDirectorySecurity(CSimpleString *pstrDirectoryName)
{
HRESULT hr = S_OK;
BOOL bSuccess = TRUE;
SECURITY_ATTRIBUTES SA;
SA.nLength = sizeof(SECURITY_ATTRIBUTES);
SA.bInheritHandle = TRUE;
if (ConvertStringSecurityDescriptorToSecurityDescriptor(
OBJECT_DACLS,
SDDL_REVISION_1,
&(SA.lpSecurityDescriptor),
NULL))
{
bSuccess = SetFileSecurity(*pstrDirectoryName,
DACL_SECURITY_INFORMATION,
SA.lpSecurityDescriptor);
if (!bSuccess)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
if (SA.lpSecurityDescriptor)
{
LocalFree(SA.lpSecurityDescriptor);
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvInitializeWia [IWiaMiniDrv]
WIA calls this method to ask us to do the following:
* Initialize our mini driver.
* Setup our optional private interface(s).
* Build our device item tree.
During initializiation we:
* Cache the STI device pointer for locking.
* Cache the device ID and root item full item name.
* Initialize and hook up the DirectShow stream.
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvInitializeWia( BYTE *pWiasContext,
LONG lFlags,
BSTR bstrDeviceID,
BSTR bstrRootFullItemName,
IUnknown *pStiDevice,
IUnknown *pIUnknownOuter,
IWiaDrvItem **ppIDrvItemRoot,
IUnknown **ppIUnknownInner,
LONG *plDevErrVal
)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvInitializeWia");
//
// Initialize return values
//
if (ppIDrvItemRoot)
{
*ppIDrvItemRoot = NULL;
}
if (ppIUnknownInner)
{
*ppIUnknownInner = NULL;
}
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// Enter the critical section.
//
EnterCriticalSection(&m_csItemTree);
m_dwConnectedApps++;
DBG_TRC(("CVideoStiUsd::drvInitializeWia - Initializing Video Driver, "
"Num Connected Apps = '%lu', device id = '%ws', Root Item Name = '%ws'",
m_dwConnectedApps,
bstrDeviceID,
bstrRootFullItemName));
if (m_dwConnectedApps == 1)
{
//
// Cache what we need to
//
if (pStiDevice)
{
pStiDevice->QueryInterface( IID_IStiDevice, (void **)&m_pStiDevice );
}
m_strDeviceId.Assign(CSimpleStringWide(bstrDeviceID));
m_strRootFullItemName.Assign(CSimpleStringWide(bstrRootFullItemName));
//
// Set the images directory. The first param is NULL, which indicates
// that a default directory should be set.
//
if (hr == S_OK)
{
hr = SetImagesDirectory(NULL,
pWiasContext,
&m_pRootItem,
plDevErrVal);
}
//
// Enable the take picture event so that an app can send this driver
// the take picture command, and this driver can signal the appliation
// that owns wiavideo to take the picture.
//
if (hr == S_OK)
{
EnableTakePicture(pWiasContext);
}
}
else
{
RefreshTree(m_pRootItem, plDevErrVal);
}
if (ppIDrvItemRoot)
{
*ppIDrvItemRoot = m_pRootItem;
}
//
// Leave the critical section
//
LeaveCriticalSection(&m_csItemTree);
CHECK_S_OK(hr);
return hr;
}
/**************************************************************************\
CVideoStiUsd::drvUnInitializeWia [IWiaMiniDrv]
Gets called when a client connection is going away.
WIA calls this method to ask us to do the following:
* Cleanup any resources that are releated to this client connection
(identified by pWiasContext)
*************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvUnInitializeWia(BYTE *pWiasContext)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvUnInitializeWia");
EnterCriticalSection(&m_csItemTree);
if (m_dwConnectedApps > 0)
{
m_dwConnectedApps--;
}
DBG_TRC(("CVideoStiUsd::drvUnInitializeWia, Num Connected Apps = '%lu'",
m_dwConnectedApps));
if ((m_dwConnectedApps == 0) && (m_pRootItem))
{
DisableTakePicture(pWiasContext, TRUE);
DBG_TRC(("CVideoStiUsd::drvUnInitializeWia, no more connected apps, deleting tree"));
hr = m_pRootItem->UnlinkItemTree(WiaItemTypeDisconnected);
CHECK_S_OK2(hr,("m_pRootItem->UnlinkItemTree()"));
// Clear the root item
m_pRootItem = NULL;
// Clear the pointer to the STI device we received
m_pStiDevice = NULL;
// reset the num pictures taken to 0.
m_lPicsTaken = 0;
}
LeaveCriticalSection(&m_csItemTree);
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvGetDeviceErrorStr [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvGetDeviceErrorStr(LONG lFlags,
LONG lDevErrVal,
LPOLESTR * ppszDevErrStr,
LONG * plDevErr)
{
HRESULT hr = E_NOTIMPL;
DBG_FN("CVideoStiUsd::drvGetDeviceErrorStr");
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvDeviceCommand [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvDeviceCommand(BYTE * pWiasContext,
LONG lFlags,
const GUID * pGUIDCommand,
IWiaDrvItem ** ppMiniDrvItem,
LONG * plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvDeviceCommand");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// We support "Take snapshot"
//
if (*pGUIDCommand == WIA_CMD_TAKE_PICTURE)
{
DBG_TRC(("CVideoStiUsd::drvDeviceCommand received command "
"WIA_CMD_TAKE_PICTURE"));
//
// Take a picture
//
hr = TakePicture(pWiasContext, ppMiniDrvItem);
}
else if (*pGUIDCommand == WIA_CMD_ENABLE_TAKE_PICTURE)
{
//
// This command doesn't do anything. However WiaVideo still expects
// it to succeed, so if you remove this, remove the call from WiaVideo too.
//
DBG_TRC(("CVideoStiUsd::drvDeviceCommand received command "
"WIA_CMD_ENABLE_TAKE_PICTURE"));
hr = S_OK;
}
else if (*pGUIDCommand == WIA_CMD_DISABLE_TAKE_PICTURE)
{
//
// This command doesn't do anything. However WiaVideo still expects
// it to succeed, so if you remove this, remove the call from WiaVideo too.
//
DBG_TRC(("CVideoStiUsd::drvDeviceCommand received command "
"WIA_CMD_DISABLE_TAKE_PICTURE"));
hr = S_OK;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::ValidateDataTransferContext
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::ValidateDataTransferContext(
PMINIDRV_TRANSFER_CONTEXT pDataTransferContext)
{
DBG_FN("CVideoStiUsd::ValidateDataTransferContext");
if (pDataTransferContext->lSize != sizeof(MINIDRV_TRANSFER_CONTEXT))
{
DBG_ERR(("invalid data transfer context -- wrong lSize"));
return E_INVALIDARG;;
}
//
// for tymed file or hglobal, only WiaImgFmt_BMP || WiaImgFmt_JPEG
// is allowed
//
if ((pDataTransferContext->tymed == TYMED_FILE) ||
(pDataTransferContext->tymed == TYMED_HGLOBAL)
)
{
if ((pDataTransferContext->guidFormatID != WiaImgFmt_BMP) &&
(pDataTransferContext->guidFormatID != WiaImgFmt_JPEG))
{
DBG_ERR(("invalid format -- asked for TYMED_FILE or TYMED_HGLOBAL "
"but guidFormatID != (WiaImgFmt_BMP | WiaImgFmt_JPEG)"));
return E_INVALIDARG;;
}
}
//
// for tymed CALLBACK, only WiaImgFmt_MEMORYBMP, WiaImgFmt_BMP and
// WiaImgFmt_JPEG are allowed
//
if (pDataTransferContext->tymed == TYMED_CALLBACK)
{
if ((pDataTransferContext->guidFormatID != WiaImgFmt_BMP) &&
(pDataTransferContext->guidFormatID != WiaImgFmt_MEMORYBMP) &&
(pDataTransferContext->guidFormatID != WiaImgFmt_JPEG))
{
DBG_ERR(("invalid format -- asked for TYMED_CALLBACK but "
"guidFormatID != (WiaImgFmt_BMP | WiaImgFmt_MEMORYBMP "
"| WiaImgFmt_JPEG)"));
return E_INVALIDARG;;
}
}
//
// callback is always double buffered, non-callback never is
//
if (pDataTransferContext->pTransferBuffer == NULL)
{
DBG_ERR(("invalid transfer context -- pTransferBuffer is NULL!"));
return E_INVALIDARG;
}
return S_OK;
}
/*****************************************************************************
CVideoStiUsd::SendBitmapHeader
Sends bitmap header during banded transfer
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::SendBitmapHeader(IWiaDrvItem * pDrvItem,
PMINIDRV_TRANSFER_CONTEXT pTranCtx)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::SendBitmapHeader");
//
// driver is sending TOPDOWN data, must swap biHeight
//
// this routine assumes pTranCtx->pHeader points to a
// BITMAPINFO header (TYMED_FILE doesn't use this path
// and DIB is the only format supported now)
//
PBITMAPINFO pbmi = (PBITMAPINFO)pTranCtx->pTransferBuffer;
if (pTranCtx->guidFormatID == WiaImgFmt_MEMORYBMP)
{
pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight;
}
hr = pTranCtx->pIWiaMiniDrvCallBack->MiniDrvCallback(
IT_MSG_DATA,
IT_STATUS_TRANSFER_TO_CLIENT,
0,
0,
pTranCtx->lHeaderSize,
pTranCtx,
0);
if (hr == S_OK)
{
//
// advance offset for destination copy
//
pTranCtx->cbOffset += pTranCtx->lHeaderSize;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvAquireItemData [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvAcquireItemData(BYTE * pWiasContext,
LONG lFlags,
PMINIDRV_TRANSFER_CONTEXT pDataContext,
LONG * plDevErrVal)
{
HRESULT hr = E_NOTIMPL;
DBG_FN("CVideoStiUsd::drvAcquireItemData");
*plDevErrVal = 0;
//
// Get a pointer to the associated driver item.
//
IWiaDrvItem* pDrvItem;
hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
if (FAILED(hr))
{
CHECK_S_OK2(hr, ("wiaGetDrvItem Failed"));
return hr;
}
//
// Validate the data transfer context.
//
hr = ValidateDataTransferContext( pDataContext );
if (FAILED(hr))
{
CHECK_S_OK2(hr, ("ValidateTransferContext failed"));
return hr;
}
#ifdef DEBUG
//
// Dump the request
//
DBG_TRC(("Asking for TYMED of 0x%x", pDataContext->tymed));
if (pDataContext->guidFormatID == WiaImgFmt_BMP)
{
DBG_TRC(("Asking for WiaImgFmt_BMP"));
}
else if (pDataContext->guidFormatID == WiaImgFmt_MEMORYBMP)
{
DBG_TRC(("Asking for WiaImgFmt_MEMORYBMP"));
}
else if (pDataContext->guidFormatID == WiaImgFmt_JPEG)
{
DBG_TRC(("Asking for WiaImgFmt_JPEG"));
}
#endif
//
// get item specific driver data
//
STILLCAM_IMAGE_CONTEXT *pContext;
pDrvItem->GetDeviceSpecContext((BYTE **)&pContext);
if (!pContext)
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("drvAcquireItemData, NULL item context"));
return hr;
}
//
// Use our internal routines to get format specific info...
//
if (pContext->pImage)
{
hr = pContext->pImage->SetItemSize( pWiasContext, pDataContext );
CHECK_S_OK2(hr, ("pContext->pImage->SetItemSize()"));
}
else
{
DBG_ERR(("Couldn't use our internal routines to compute image "
"information, this is bad!"));
//
// As a last resort, use WIA services to fetch format specific info.
//
hr = wiasGetImageInformation(pWiasContext,
0,
pDataContext);
CHECK_S_OK2(hr,("wiaGetImageInformation()"));
}
if (FAILED(hr))
{
CHECK_S_OK2(hr, ("wiasGetImageInformation failed"));
return hr;
}
//
// determine if this is a callback or buffered transfer
//
if (pDataContext->tymed == TYMED_CALLBACK)
{
DBG_TRC(("Caller wants callback"));
//
// For formats that have a data header, send it to the client
//
if (pDataContext->lHeaderSize > 0)
{
DBG_TRC(("Sending Bitmap Header..."));
hr = SendBitmapHeader( pDrvItem, pDataContext );
CHECK_S_OK2(hr,("SendBitmapHeader( pDrvItem, pDataContext )"));
}
if (hr == S_OK)
{
DBG_TRC(("Calling LoadImageCB..."));
hr = LoadImageCB( pContext, pDataContext, plDevErrVal );
CHECK_S_OK2(hr, ("LoadImageCB( pContext, pDataContext, "
"plDevErrVal"));
}
}
else
{
DBG_TRC(("Caller doesn't want callback"));
//
// inc past header
//
pDataContext->cbOffset += pDataContext->lHeaderSize;
DBG_TRC(("Calling LoadImage..."));
hr = LoadImage( pContext, pDataContext, plDevErrVal );
CHECK_S_OK2(hr, ("LoadImage( pContext, pDataContext, "
"plDevErrVal )"));
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvInitItemProperties [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvInitItemProperties(BYTE * pWiasContext,
LONG lFlags,
LONG * plDevErrVal)
{
DBG_FN("CVideoStiUsd::drvInitItemProperties");
HRESULT hr = S_OK;
LONG lItemType;
PSTILLCAM_IMAGE_CONTEXT pContext;
IWiaDrvItem * pDrvItem; // This is not a CComPtr because there
// is no addref for us to release
//
// This device doesn't touch hardware to initialize the
// device item properties.
//
*plDevErrVal = 0;
//
// Parameter validation.
//
if (!pWiasContext)
{
DBG_ERR(("drvInitItemProperties, invalid input pointers"));
return E_INVALIDARG;
}
//
// Get a pointer to the associated driver item.
//
if (hr == S_OK)
{
hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
CHECK_S_OK2(hr,("wiaGetDrvItem"));
}
if (hr == S_OK)
{
//
// Root item has the all the device properties
//
hr = pDrvItem->GetItemFlags(&lItemType);
CHECK_S_OK2(hr,("pDrvItem->GetItemFlags"));
}
if (hr == S_OK)
{
if (lItemType & WiaItemTypeRoot)
{
//
// Root item property init finishes here
//
hr = InitDeviceProperties( pWiasContext, plDevErrVal );
CHECK_S_OK2(hr,("InitDeviceProperties for root item"));
}
else if (lItemType & WiaItemTypeFile)
{
//
// If this is a file, init the properties
//
//
// Add the item property names.
//
if (hr == S_OK)
{
hr = wiasSetItemPropNames(pWiasContext,
NUM_CAM_ITEM_PROPS,
gItemPropIDs,
gItemPropNames);
CHECK_S_OK2(hr,("wiaSetItemPropNames for item"));
}
if (hr == S_OK)
{
//
// Use WIA services to set the default item properties.
//
hr = wiasWriteMultiple(pWiasContext,
NUM_CAM_ITEM_PROPS,
gPropSpecDefaults,
(PROPVARIANT*)gPropVarDefaults);
CHECK_S_OK2(hr,("wiaWriteMultiple for item props"));
}
if (hr == S_OK)
{
hr = pDrvItem->GetDeviceSpecContext( (BYTE **)&pContext );
CHECK_S_OK2(hr,("GetDeviceSpecContext"));
}
if (hr == S_OK)
{
hr = InitImageInformation(pWiasContext, pContext, plDevErrVal);
CHECK_S_OK2(hr,("InitImageInformation"));
}
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::ValidateItemProperties
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::ValidateItemProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal,
IWiaDrvItem *pDrvItem)
{
DBG_FN("CVideoStiUsd::ValidateFileProperties");
HRESULT hr = S_OK;
if ((pWiasContext == NULL) ||
(pPropSpec == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::ValidateItemProperties received "
"NULL params"));
return hr;
}
STILLCAM_IMAGE_CONTEXT *pContext = NULL;
hr = pDrvItem->GetDeviceSpecContext( (BYTE **)&pContext);
CHECK_S_OK2(hr,("CVideoStiUsd::ValidateItemProperties, "
"GetDeviceSpecContext failed"));
if (SUCCEEDED(hr) && pContext)
{
CImage * pImage = pContext->pImage;
if (pImage)
{
//
// calc item size
//
hr = pImage->SetItemSize( pWiasContext, NULL );
CHECK_S_OK2(hr,("SetItemSize( pWiasContext )"));
}
//
// Change MinBufferSize property. Need to get Tymed and
// ItemSize first, since MinBufferSize in dependant on these
// properties.
//
LONG lTymed;
LONG lItemSize;
LONG lMinBufSize = 0;
hr = wiasReadPropLong(pWiasContext,
WIA_IPA_TYMED,
&lTymed,
NULL,
TRUE);
CHECK_S_OK2(hr, ("wiasReadPropLong( WIA_IPA_TYPMED )"));
if (SUCCEEDED(hr))
{
hr = wiasReadPropLong(pWiasContext,
WIA_IPA_ITEM_SIZE,
&lItemSize,
NULL,
TRUE);
CHECK_S_OK2(hr,("wiasReadPropLong( WIA_IPA_ITEM_SIZE )"));
if (SUCCEEDED(hr))
{
//
// Update the MinBufferSize property.
//
switch (lTymed)
{
case TYMED_CALLBACK:
lMinBufSize = 65535;
break;
default:
lMinBufSize = lItemSize;
break;
}
if (lMinBufSize)
{
hr = wiasWritePropLong(pWiasContext,
WIA_IPA_MIN_BUFFER_SIZE,
lMinBufSize);
CHECK_S_OK2(hr, ("wiasWritePropLong( "
"WIA_IPA_MIN_BUFFER_SIZE )"));
}
DBG_TRC(("WIA_IPA_MIN_BUFFER_SIZE set to %d bytes",
lMinBufSize));
}
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::ValidateDeviceProperties
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::ValidateDeviceProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal,
IWiaDrvItem *pDrvItem)
{
DBG_FN("CVideoStiUsd::ValidateRootProperties");
HRESULT hr = S_OK;
//
// Parameter validation.
//
if ((pWiasContext == NULL) ||
(pPropSpec == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::ValidateDeviceProperties received "
"NULL params"));
return hr;
}
for (ULONG i = 0; i < nPropSpec; i++)
{
if ((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_LAST_PICTURE_TAKEN))
{
DBG_TRC(("CVideoStiUsd::ValidateDeviceProperties, setting the "
"WIA_DPV_LAST_PICTURE_TAKEN property."));
EnterCriticalSection(&m_csItemTree);
//
// process the last picture taken property change.
//
BSTR bstrLastPictureTaken = NULL;
//
// Read in the value for last picture taken.
//
hr = wiasReadPropStr(pWiasContext,
WIA_DPV_LAST_PICTURE_TAKEN,
&bstrLastPictureTaken,
NULL,
TRUE);
if (hr == S_OK)
{
m_strLastPictureTaken = bstrLastPictureTaken;
DBG_TRC(("CVideoStiUsd::ValidateDeviceProperties, last picture "
"taken = '%ls'", m_strLastPictureTaken.String()));
//
// This will add the new item to the tree and queue an
// ITEM_CREATED event
//
hr = SignalNewImage(bstrLastPictureTaken);
}
// reset the last picture taken value. This is needed because the
// service checks to see if the new value being set is the same as
// the current value, and if it is, it doesn't forward it on to us.
// This is bad in the event of the Scanner and Camera wizard, where
// it takes 1 picture, (so LAST_PICTURE_TAKEN has a value of "Picture 1"),
// then deletes it, then user backs up the wizard, and takes a picture
// again. This new picture will have a value of "Picture 1" but we won't
// add it to the tree because the value of the property hasn't changed
// as far as the wia service is concerned.
//
if (hr == S_OK)
{
//
// Write the Last Picture Taken
//
hr = wiasWritePropStr(pWiasContext,
WIA_DPV_LAST_PICTURE_TAKEN,
CSimpleBStr(TEXT("")));
}
//
// Free the BSTR
//
if (bstrLastPictureTaken)
{
::SysFreeString(bstrLastPictureTaken);
bstrLastPictureTaken = NULL;
}
LeaveCriticalSection(&m_csItemTree);
}
else if ((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_IMAGES_DIRECTORY))
{
//
// DPV_IMAGES_DIRECTORY -
//
hr = E_FAIL;
CHECK_S_OK2(hr, ("CVideoStiUsd::ValidateRootProperties, "
"attempting to validate the Images Directory "
"property, but this is a read-only "
"property"));
}
else if ((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_DSHOW_DEVICE_PATH))
{
//
// process the DShowDeviceID change.
//
hr = E_FAIL;
CHECK_S_OK2(hr, ("CVideoStiUsd::ValidateRootProperties, "
"attempting to validate the DShow Device "
"ID property, but this is a read-only "
"property"));
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvValidateItemProperties [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvValidateItemProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvValidateItemProperties");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// Parameter validation.
//
if ((pWiasContext == NULL) ||
(pPropSpec == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::drvValidateItemProperties received "
"NULL params"));
return hr;
}
//
// Get item in question
//
//
// not a CComPtr because there isn't an extra ref
// on this guy from the caller
//
IWiaDrvItem* pDrvItem = NULL;
hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
CHECK_S_OK2(hr,("wiasGetDrvItem( pWiasContext, &pDrvItem )"));
if (SUCCEEDED(hr))
{
LONG lItemType = 0;
//
// What kind of item is this?
//
hr = pDrvItem->GetItemFlags(&lItemType);
CHECK_S_OK2(hr,("pDrvItem->GetItemFlags( &lItemType )"));
if (SUCCEEDED(hr))
{
if (lItemType & WiaItemTypeFile)
{
hr = ValidateItemProperties(pWiasContext,
lFlags,
nPropSpec,
pPropSpec,
plDevErrVal,
pDrvItem);
}
else if (lItemType & WiaItemTypeRoot)
{
hr = ValidateDeviceProperties(pWiasContext,
lFlags,
nPropSpec,
pPropSpec,
plDevErrVal,
pDrvItem);
}
}
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvWriteItemProperties [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvWriteItemProperties(BYTE * pWiasContext,
LONG lFLags,
PMINIDRV_TRANSFER_CONTEXT pmdtc,
LONG * plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvWriteItemProperties");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::ReadItemProperties
We only support reading thumbnails on demand for items
*****************************************************************************/
HRESULT
CVideoStiUsd::ReadItemProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal,
IWiaDrvItem *pDrvItem)
{
HRESULT hr = S_OK;
STILLCAM_IMAGE_CONTEXT *pContext = NULL;
if ((pPropSpec == NULL) ||
(pDrvItem == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::ReadItemProperties received a "
"NULL param"));
return hr;
}
//
// It's an item, now loop through the requested properties
// and see if they're looking for the Thumbnail
//
for (ULONG i = 0; i < nPropSpec; i++)
{
if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_IPC_THUMBNAIL)) ||
((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
(wcscmp(pPropSpec[i].lpwstr, WIA_IPC_THUMBNAIL_STR) == 0)))
{
//
// They'd like the thumbnail
//
hr = pDrvItem->GetDeviceSpecContext((BYTE **)&pContext);
CHECK_S_OK2(hr,("pDrvItem->GetDeviceSpecContext()"));
if (SUCCEEDED(hr) && pContext)
{
if (pContext->pImage)
{
//
// Use our internal routines to load the thumbnail...
//
hr = pContext->pImage->LoadThumbnail(pWiasContext);
break;
}
else
{
DBG_ERR(("pContext->pImage was NULL!"));
}
}
else
{
DBG_ERR(("Couldn't get pContext"));
}
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::ReadDeviceProperties
We support all our custom properties
*****************************************************************************/
HRESULT
CVideoStiUsd::ReadDeviceProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal,
IWiaDrvItem *pDrvItem)
{
HRESULT hr = S_OK;
if ((pPropSpec == NULL) ||
(pDrvItem == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::ReadItemProperties received a "
"NULL param"));
return hr;
}
for (ULONG i = 0; i < nPropSpec; i++)
{
if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPC_PICTURES_TAKEN)) ||
((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
(!wcscmp(pPropSpec[i].lpwstr, WIA_DPC_PICTURES_TAKEN_STR))))
{
//
// Requesting the number of pictures taken.
//
DBG_TRC(("CVideoStiUsd::ReadDeviceProperties, reading propID "
"'%lu' (0x%08lx) WIA_DPC_PICTURES_TAKEN = '%lu'",
pPropSpec[i].propid,
pPropSpec[i].propid,
m_lPicsTaken));
wiasWritePropLong(pWiasContext,
WIA_DPC_PICTURES_TAKEN,
m_lPicsTaken);
}
else if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_DSHOW_DEVICE_PATH)) ||
((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
(!wcscmp(pPropSpec[i].lpwstr, WIA_DPV_DSHOW_DEVICE_PATH_STR))))
{
//
// Requesting the DShow Device ID.
//
DBG_TRC(("CVideoStiUsd::ReadDeviceProperties, reading propID "
"'%lu' (0x%08lx) WIA_DPV_DSHOW_DEVICE_PATH = '%ls'",
pPropSpec[i].propid,
pPropSpec[i].propid,
m_strDShowDeviceId.String()));
wiasWritePropStr(pWiasContext,
WIA_DPV_DSHOW_DEVICE_PATH,
CSimpleBStr(m_strDShowDeviceId).BString());
}
else if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_IMAGES_DIRECTORY)) ||
((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
(!wcscmp(pPropSpec[i].lpwstr, WIA_DPV_IMAGES_DIRECTORY_STR))))
{
//
// Requesting the Images Directory.
//
DBG_TRC(("CVideoStiUsd::ReadDeviceProperties, reading propID "
"'%lu' (0x%08lx) WIA_DPV_IMAGES_DIRECTORY = '%ls'",
pPropSpec[i].propid,
pPropSpec[i].propid,
m_strStillPath.String()));
wiasWritePropStr(pWiasContext,
WIA_DPV_IMAGES_DIRECTORY,
CSimpleBStr(m_strStillPath).BString());
}
else if (((pPropSpec[i].ulKind == PRSPEC_PROPID) &&
(pPropSpec[i].propid == WIA_DPV_LAST_PICTURE_TAKEN)) ||
((pPropSpec[i].ulKind == PRSPEC_LPWSTR) &&
(!wcscmp(pPropSpec[i].lpwstr, WIA_DPV_LAST_PICTURE_TAKEN_STR))))
{
//
// Requesting the last picture taken
//
DBG_TRC(("CVideoStiUsd::ReadDeviceProperties, reading propID "
"'%lu' (0x%08lx) WIA_DPV_LAST_PICTURE_TAKEN = '%ls'",
pPropSpec[i].propid,
pPropSpec[i].propid,
m_strLastPictureTaken.String()));
wiasWritePropStr(pWiasContext,
WIA_DPV_LAST_PICTURE_TAKEN,
CSimpleBStr(m_strLastPictureTaken).BString());
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvReadItemProperties [IWiaMiniDrv]
We only support reading thumbnails on demand.
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvReadItemProperties(BYTE *pWiasContext,
LONG lFlags,
ULONG nPropSpec,
const PROPSPEC *pPropSpec,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
LONG lItemType = 0;
IWiaDrvItem *pDrvItem = NULL;
DBG_FN("CVideoStiUsd::drvReadItemProperties");
//
// Check for bad args
//
if ((nPropSpec == 0) ||
(pPropSpec == NULL) ||
(pWiasContext == NULL))
{
hr = E_INVALIDARG;
CHECK_S_OK2(hr, ("CVideoStiUsd::drvReadItemProperties received "
"NULL params"));
return hr;
}
if (hr == S_OK)
{
//
// Make sure we're dealing with an item, not the root item.
//
//
// Get minidriver item
//
hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
if ((hr == S_OK) && (pDrvItem == NULL))
{
hr = E_FAIL;
}
CHECK_S_OK2(hr,("CVideoStiUsd::drvReadItemProperties, wiasGetDrvItem "
"failed"));
}
if (hr == S_OK)
{
hr = pDrvItem->GetItemFlags(&lItemType);
CHECK_S_OK2(hr,("pDrvItem->GetItemFlags()"));
}
if (hr == S_OK)
{
if ((lItemType & WiaItemTypeFile) && (!(lItemType & WiaItemTypeRoot)))
{
//
// If property being requested is a file and it is NOT the root,
// then read in the item property.
//
hr = ReadItemProperties(pWiasContext,
lFlags,
nPropSpec,
pPropSpec,
plDevErrVal,
pDrvItem);
}
else if ((lItemType & WiaItemTypeFolder) &&
(lItemType & WiaItemTypeRoot))
{
//
// If the property being requested is the root, then read in
// the device properties.
//
hr = ReadDeviceProperties(pWiasContext,
lFlags,
nPropSpec,
pPropSpec,
plDevErrVal,
pDrvItem);
}
}
if (plDevErrVal)
{
*plDevErrVal = 0;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvLockWiaDevice [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvLockWiaDevice(BYTE *pWiasContext,
LONG lFlags,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvLockWiaDevice");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// We are purposely not locking the driver. This driver is thread
// safe and it looks like with large volumes of images (>1000)
// you get better performance if the driver manages synchronization.
//
// return m_pStiDevice->LockDevice(DEFAULT_LOCK_TIMEOUT);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvUnLockWiaDevice [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvUnLockWiaDevice(BYTE *pWiasContext,
LONG lFlags,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvUnLockWiaDevice");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// We are purposely not locking the driver. This driver is thread
// safe and it looks like with large volumes of images (>1000)
// you get better performance if the driver manages synchronization.
//
// return m_pStiDevice->UnLockDevice();
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvAnalyzeItem [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvAnalyzeItem(BYTE *pWiasContext,
LONG lFlags,
LONG *plDevErrVal)
{
HRESULT hr = E_NOTIMPL;
DBG_FN("CVideoStiUsd::drvAnalyzeItem");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvDeleteItem [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvDeleteItem(BYTE *pWiasContext,
LONG lFlags,
LONG *plDevErrVal)
{
HRESULT hr = E_FAIL;
DBG_FN("CVideoStiUsd::drvDeleteItem");
//
// check for bad params
//
if (pWiasContext == NULL)
{
DBG_ERR(("pWiasContext is NULL!"));
return E_INVALIDARG;
}
if (plDevErrVal)
{
*plDevErrVal = 0;
}
EnterCriticalSection(&m_csItemTree);
//
// Get a pointer to the associated driver item.
//
IWiaDrvItem * pDrvItem = NULL;
hr = wiasGetDrvItem(pWiasContext, &pDrvItem);
CHECK_S_OK2(hr,("wiasGetDrvItem"));
if (SUCCEEDED(hr) && pDrvItem)
{
//
// get item specific driver data
//
STILLCAM_IMAGE_CONTEXT *pContext = NULL;
pDrvItem->GetDeviceSpecContext((BYTE **)&pContext);
CHECK_S_OK2(hr,("pDrvItem->GetDeviceSpecContext"));
if (SUCCEEDED(hr) && pContext && pContext->pImage)
{
//
// Delete the file in question
//
hr = pContext->pImage->DoDelete();
CHECK_S_OK2(hr,("pContext->pImage->DoDelete()"));
//
// Dec the number of pictures taken
//
InterlockedDecrement(&m_lPicsTaken);
//
// write out the new amount
//
wiasWritePropLong(pWiasContext,
WIA_DPC_PICTURES_TAKEN,
m_lPicsTaken);
if (SUCCEEDED(hr))
{
HRESULT hr2;
BSTR bstrItemName = NULL;
//
// Get bstr of full item name
//
hr2 = pDrvItem->GetFullItemName(&bstrItemName);
CHECK_S_OK2(hr2,("pDrvItem->GetFullItemName()"));
//
// Send event that item was deleted
//
hr2 = wiasQueueEvent(CSimpleBStr(m_strDeviceId),
&WIA_EVENT_ITEM_DELETED,
bstrItemName);
CHECK_S_OK2(hr2,
("wiasQueueEvent( WIA_EVENT_ITEM_DELETED )"));
//
// Cleanup
//
if (bstrItemName)
{
SysFreeString(bstrItemName);
bstrItemName = NULL;
}
}
}
else
{
DBG_ERR(("pContext or pContext->pImage are NULL!"));
hr = E_FAIL;
}
}
LeaveCriticalSection( &m_csItemTree );
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvFreeDrvItem [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvFreeDrvItemContext(LONG lFlags,
BYTE *pDevContext,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvFreeDrvItemContext");
PSTILLCAM_IMAGE_CONTEXT pContext = (PSTILLCAM_IMAGE_CONTEXT)pDevContext;
if (pContext != NULL)
{
//
// delete is safe even if param is NULL.
//
delete pContext->pImage;
pContext->pImage = NULL;
}
if (plDevErrVal)
{
*plDevErrVal = 0;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CMiniDev::drvGetCapabilities [IWiaMiniDrv]
Let WIA know what things this driver can do.
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvGetCapabilities(BYTE *pWiasContext,
LONG lFlags,
LONG *pCelt,
WIA_DEV_CAP_DRV **ppCapabilities,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvGetCapabilities");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// Return Commmand and/or Events depending on flags
//
switch (lFlags)
{
case WIA_DEVICE_COMMANDS:
//
// Only commands
//
*pCelt = NUM_CAP_ENTRIES - NUM_EVENTS;
*ppCapabilities = &gCapabilities[NUM_EVENTS];
break;
case WIA_DEVICE_EVENTS:
//
// Only events
//
*pCelt = NUM_EVENTS;
*ppCapabilities = gCapabilities;
break;
case (WIA_DEVICE_COMMANDS | WIA_DEVICE_EVENTS):
//
// Both events and commands
//
*pCelt = NUM_CAP_ENTRIES;
*ppCapabilities = gCapabilities;
break;
default:
//
// Flags is invalid
//
DBG_ERR(("drvGetCapabilities, flags was invalid"));
hr = E_INVALIDARG;
break;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvGetWiaFormatInfo [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvGetWiaFormatInfo(BYTE *pWiasContext,
LONG lFlags,
LONG *pCelt,
WIA_FORMAT_INFO **ppwfi,
LONG *plDevErrVal)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvGetWiaFormatInfo");
if (plDevErrVal)
{
*plDevErrVal = 0;
}
//
// If it hasn't been done already, set up the g_wfiTable table
//
if (!m_wfi)
{
DBG_ERR(("drvGetWiaFormatInfo, m_wfi is NULL!"));
return E_OUTOFMEMORY;
}
if (pCelt)
{
*pCelt = NUM_WIA_FORMAT_INFO;
}
if (ppwfi)
{
*ppwfi = m_wfi;
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::drvNotifyPnpEvent [IWiaMiniDrv]
<Notes>
*****************************************************************************/
STDMETHODIMP
CVideoStiUsd::drvNotifyPnpEvent(const GUID *pEventGUID,
BSTR bstrDeviceID,
ULONG ulReserved)
{
HRESULT hr = S_OK;
DBG_FN("CVideoStiUsd::drvNotifyPnpEvent");
//
// CONNECTED event is of no interest, because a new USD is always created
//
if (*pEventGUID == WIA_EVENT_DEVICE_DISCONNECTED)
{
DBG_TRC(("got a WIA_EVENT_DISCONNECTED"));
}
CHECK_S_OK(hr);
return hr;
}
/*****************************************************************************
CVideoStiUsd::VerifyCorrectImagePath
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::VerifyCorrectImagePath(BSTR bstrNewImageFullPath)
{
DBG_FN("CVideoStiUsd::VerifyCorrectImagePath");
HRESULT hr = S_OK;
INT iIndex = 0;
CSimpleString strImageFullPath;
if (bstrNewImageFullPath == NULL)
{
hr = E_POINTER;
CHECK_S_OK2(hr, ("CVideoStiUsd::VerifyCorrectImagePath received a NULL pointer"));
return hr;
}
if (hr == S_OK)
{
strImageFullPath = CSimpleStringConvert::NaturalString(
CSimpleStringWide(bstrNewImageFullPath));
//
// Get the filename out of the full path. Find the last backslash.
//
iIndex = strImageFullPath.ReverseFind('\\');
strImageFullPath[iIndex] = 0;
if (strImageFullPath != m_strStillPath)
{
hr = E_ACCESSDENIED;
CHECK_S_OK2(hr, ("CVideoStiUsd::VerifyCorrectImagePath, the file that is "
"being added to the tree '%ls' is not in the allowed directory, "
"denying request with an E_ACCESSDENIED",
CSimpleStringWide(strImageFullPath).String()));
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::SignalNewImage
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::SignalNewImage(BSTR bstrNewImageFullPath)
{
DBG_FN("CVideoStiUsd::SignalNewImage");
HRESULT hr = S_OK;
CComPtr<IWiaDrvItem> pDrvItem = NULL;
BSTR bstrFullItemName = NULL;
CSimpleString strImageFullPath;
if (hr == S_OK)
{
hr = VerifyCorrectImagePath(bstrNewImageFullPath);
}
if (hr == S_OK)
{
strImageFullPath = CSimpleStringConvert::NaturalString(
CSimpleStringWide(bstrNewImageFullPath));
DBG_TRC(("CVideoStiUsd::SignalNewImage, adding image '%ls' to "
"tree of device '%ls'",
CSimpleStringWide(strImageFullPath).String(),
m_strDeviceId.String()));
// Add the new item to the tree if doesn't already exist
//
// Get the filename out of the full path. Find the last backslash and
// move 1 beyond it.
//
INT iIndex = strImageFullPath.ReverseFind('\\') + 1;
if (!IsFileAlreadyInTree(m_pRootItem, &(strImageFullPath[iIndex])))
{
hr = AddTreeItem(&strImageFullPath, &pDrvItem);
CHECK_S_OK2(hr, ("CVideoStiUsd::SignalNewImage, failed to add "
"image '%ls' to tree of device '%ls'",
CSimpleStringWide(strImageFullPath).String(),
m_strDeviceId.String()));
if (hr == S_OK)
{
//
// Get the full item name for this item
//
m_pLastItemCreated = pDrvItem;
hr = pDrvItem->GetFullItemName(&bstrFullItemName);
CHECK_S_OK2(hr,("CVideoStiUsd::SignalNewImage, failed to get Item "
"name for newly added item"));
}
if (hr == S_OK)
{
//
// Queue an event that a new item was created.
//
hr = wiasQueueEvent(CSimpleBStr(m_strDeviceId),
&WIA_EVENT_ITEM_CREATED,
bstrFullItemName);
CHECK_S_OK2(hr,("CVideoStiUsd::SignalNewImage, failed to "
"queue a new WIA_EVENT_ITEM_CREATED event"));
}
if (bstrFullItemName)
{
SysFreeString(bstrFullItemName);
bstrFullItemName = NULL;
}
}
else
{
DBG_TRC(("CVideoStiUsd::SignalNewImage, item '%ls' is already in the "
"tree. Probably tree was recently refreshed",
bstrNewImageFullPath));
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::SetImagesDirectory
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::SetImagesDirectory(BSTR bstrNewImagesDirectory,
BYTE *pWiasContext,
IWiaDrvItem **ppIDrvItemRoot,
LONG *plDevErrVal)
{
DBG_FN("CVideoStiUsd::SetImagesDirectory");
HRESULT hr = S_OK;
CSimpleString strOriginalDirectory;
//
// If we received a NULL Images directory, then build up our own
// generated one, then build the item tree.
//
strOriginalDirectory = m_strStillPath;
if (bstrNewImagesDirectory == NULL)
{
//
// If this path is not in the registry, we default to constructing
// a path of this type:
//
// %TEMP%\WIA\%DeviceID%
TCHAR szTempPath[MAX_PATH + 1] = {0};
hr = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_DEFAULT, szTempPath);
//
// We allow for the case of S_FALSE which indicates that the folder doesn't
// exist. This is fine since we recursively create it below.
//
if ((hr == S_OK) || (hr == S_FALSE))
{
if (szTempPath[_tcslen(szTempPath) - 1] != '\\')
{
_tcscat(szTempPath, TEXT("\\"));
}
//
// Set path to "Documents and Settings\Application Data\Microsoft\Wia\{deviceid}"
//
m_strStillPath.Assign(szTempPath);
m_strStillPath += TEXT("Microsoft\\WIA\\");
m_strStillPath += m_strDeviceId;
}
}
else
{
// we received a valid BSTR, attempt to create the directory.
m_strStillPath = bstrNewImagesDirectory;
}
if (!RecursiveCreateDirectory(&m_strStillPath))
{
hr = E_FAIL;
CHECK_S_OK2(hr, ("RecursiveCreateDirectory( %ws ) failed w/GLE=%d",
m_strStillPath.String(),
::GetLastError() ));
}
//
// Set the security DACL on the directory so that users and power users
// will be able to write and delete from it too.
//
if (hr == S_OK)
{
//
// We only set this directory security if we are using our default directory
// path. This isn't an issue now since the user cannot update the directory,
// but in the future if we allow them to, this could expose a security whole.
//
if (bstrNewImagesDirectory == NULL)
{
hr = SetDirectorySecurity(&m_strStillPath);
}
}
if (hr == S_OK)
{
if (m_strStillPath.Length())
{
BOOL bSendUpdateEvent = FALSE;
//
// If the original directory is different from the new directory
// and we already have a tree, then we should destroy our
// existing tree, and recreate it for the new directory.
//
if ((strOriginalDirectory.CompareNoCase(m_strStillPath) != 0) &&
(m_pRootItem != NULL))
{
EnterCriticalSection( &m_csItemTree );
hr = m_pRootItem->UnlinkItemTree(WiaItemTypeDisconnected);
CHECK_S_OK2(hr,("m_pRootItem->UnlinkItemTree()"));
if (SUCCEEDED(hr))
{
bSendUpdateEvent = TRUE;
m_pRootItem = NULL;
}
LeaveCriticalSection( &m_csItemTree );
}
//
// Build the item tree.
//
hr = BuildItemTree(ppIDrvItemRoot, plDevErrVal);
//
// write out the new amount of pictures taken
//
wiasWritePropLong(pWiasContext,
WIA_DPC_PICTURES_TAKEN,
m_lPicsTaken);
if (bSendUpdateEvent)
{
wiasQueueEvent(CSimpleBStr(m_strDeviceId),
&WIA_EVENT_TREE_UPDATED,
NULL);
}
}
else
{
hr = E_FAIL;
CHECK_S_OK2(hr,
("CVideoStiUsd::SetImagesDirectory, new directory "
"path has a length of 0, Directory = '%ls'",
m_strStillPath.String()));
}
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::TakePicture
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::TakePicture(BYTE *pTakePictureOwner,
IWiaDrvItem **ppNewDrvItem)
{
HRESULT hr = S_OK;
//
// Notice that we are allowing multiple applications to call the
// take picture command, even if they weren't the owns that enabled
// it.
//
DBG_TRC(("CVideoStiUsd::drvDeviceCommand received command "
"WIA_CMD_TAKE_PICTURE"));
if ((m_hTakePictureEvent) && (m_hPictureReadyEvent))
{
DWORD dwResult = 0;
m_pLastItemCreated = NULL;
//
// Tell the WiaVideo object that pertains to this device ID to
// take a picture.
//
SetEvent(m_hTakePictureEvent);
// The WiaVideo object has a thread waiting on the
// m_hTakePictureEvent. When it is signalled, the WiaVideo object
// takes the picture, then sets the driver's custom
// "LastPictureTaken" property. This causes the driver to update
// its tree and queue an ITEM_CREATED event. Once this is complete,
// the WiaVideo object sets the PictureReady Event, at which point
// we return from this function call.
// dwResult = WaitForSingleObject(m_hPictureReadyEvent,
// TAKE_PICTURE_TIMEOUT);
if (dwResult == WAIT_OBJECT_0)
{
// *ppNewDrvItem = m_pLastItemCreated;
}
else
{
if (dwResult == WAIT_TIMEOUT)
{
hr = E_FAIL;
CHECK_S_OK2(hr, ("CVideoStiUsd::TakePicture timed out "
"after waiting for '%lu' seconds for the "
"WiaVideo object to take a picture",
TAKE_PICTURE_TIMEOUT));
}
else if (dwResult == WAIT_ABANDONED)
{
hr = E_FAIL;
CHECK_S_OK2(hr, ("CVideoStiUsd::TakePicture failed, received "
"a WAIT_ABANDONED from the Wait function"));
}
else
{
hr = E_FAIL;
CHECK_S_OK2(hr, ("CVideoStiUsd::TakePicture failed to take a "
"picture."));
}
}
}
else
{
DBG_TRC(("CVideoStiUsd::drvDeviceCommand, ignoring "
"WIA_CMD_TAKE_PICTURE request, events created "
"by WiaVideo are not open"));
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::EnableTakePicture
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::EnableTakePicture(BYTE *pTakePictureOwner)
{
DBG_FN("CVideoStiUsd::EnableTakePicture");
HRESULT hr = S_OK;
CSimpleString strTakePictureEvent;
CSimpleString strPictureReadyEvent;
CSimpleString strDeviceID;
SECURITY_ATTRIBUTES SA;
SA.nLength = sizeof(SECURITY_ATTRIBUTES);
SA.bInheritHandle = TRUE;
//
// Convert to security descriptor
//
ConvertStringSecurityDescriptorToSecurityDescriptor(OBJECT_DACLS,
SDDL_REVISION_1,
&(SA.lpSecurityDescriptor),
NULL);
strDeviceID = CSimpleStringConvert::NaturalString(m_strDeviceId);
m_pTakePictureOwner = pTakePictureOwner;
if (hr == S_OK)
{
INT iPosition = 0;
CSimpleString strModifiedDeviceID;
// Change the device ID from {6B...}\xxxx, to {6B...}_xxxx
iPosition = strDeviceID.ReverseFind('\\');
strModifiedDeviceID = strDeviceID.MakeUpper();
strModifiedDeviceID.SetAt(iPosition, '_');
//
// Generate the event names. These names contain the Device ID in
// them so that they are unique across devices.
//
strTakePictureEvent = EVENT_PREFIX_GLOBAL;
strTakePictureEvent += strModifiedDeviceID;
strTakePictureEvent += EVENT_SUFFIX_TAKE_PICTURE;
strPictureReadyEvent = EVENT_PREFIX_GLOBAL;
strPictureReadyEvent += strModifiedDeviceID;
strPictureReadyEvent += EVENT_SUFFIX_PICTURE_READY;
}
if (hr == S_OK)
{
m_hTakePictureEvent = CreateEvent(&SA,
FALSE,
FALSE,
strTakePictureEvent);
//
// This is not really an error since the events will not have been created until
// the WiaVideo object comes up.
//
if (m_hTakePictureEvent == NULL)
{
hr = E_FAIL;
DBG_TRC(("CVideoStiUsd::EnableTakePicture, failed to open the "
"WIA event '%ls', this is not fatal (LastError = '%lu' "
"(0x%08lx))",
strTakePictureEvent.String(),
::GetLastError(),
::GetLastError()));
}
}
if (hr == S_OK)
{
m_hPictureReadyEvent = CreateEvent(&SA,
FALSE,
FALSE,
strPictureReadyEvent);
//
// This is not really an error since the events will not have been created until
// the WiaVideo object comes up.
//
if (m_hPictureReadyEvent == NULL)
{
hr = E_FAIL;
DBG_TRC(("CVideoStiUsd::EnableTakePicture, failed to open the WIA "
"event '%ls', this is not fatal (LastError = '%lu' "
"(0x%08lx))",
strPictureReadyEvent.String(),
::GetLastError(),
::GetLastError()));
}
}
if (SA.lpSecurityDescriptor)
{
LocalFree(SA.lpSecurityDescriptor);
}
return hr;
}
/*****************************************************************************
CVideoStiUsd::DisableTakePicture
<Notes>
*****************************************************************************/
HRESULT
CVideoStiUsd::DisableTakePicture(BYTE *pTakePictureOwner,
BOOL bShuttingDown)
{
HRESULT hr = S_OK;
if (m_hTakePictureEvent)
{
::CloseHandle(m_hTakePictureEvent);
m_hTakePictureEvent = NULL;
}
if (m_hPictureReadyEvent)
{
::CloseHandle(m_hPictureReadyEvent);
m_hPictureReadyEvent = NULL;
}
m_pTakePictureOwner = NULL;
return hr;
}