/*++ Copyright (C) 1999- Microsoft Corporation Module Name: minidrv.cpp Abstract: This module implements main part of CWiaMiniDriver class Author: William Hsieh (williamh) created Revision History: --*/ #include "pch.h" #include #include #include #include #include "utils.h" // // Strings that will be loaded from resource // WCHAR UnknownString[MAX_PATH] = L"\0"; WCHAR FolderString[MAX_PATH] = L"\0"; WCHAR ScriptString[MAX_PATH] = L"\0"; WCHAR ExecString[MAX_PATH] = L"\0"; WCHAR TextString[MAX_PATH] = L"\0"; WCHAR HtmlString[MAX_PATH] = L"\0"; WCHAR DpofString[MAX_PATH] = L"\0"; WCHAR AudioString[MAX_PATH] = L"\0"; WCHAR VideoString[MAX_PATH] = L"\0"; WCHAR UnknownImgString[MAX_PATH] = L"\0"; WCHAR ImageString[MAX_PATH] = L"\0"; WCHAR AlbumString[MAX_PATH] = L"\0"; WCHAR BurstString[MAX_PATH] = L"\0"; WCHAR PanoramaString[MAX_PATH] = L"\0"; // // Structures for setting up WIA capabilities // WCHAR DeviceConnectedString[MAX_PATH] = L"\0"; WCHAR DeviceDisconnectedString[MAX_PATH] = L"\0"; WCHAR ItemCreatedString[MAX_PATH] = L"\0"; WCHAR ItemDeletedString[MAX_PATH] = L"\0"; WCHAR TakePictureString[MAX_PATH] = L"\0"; WCHAR SynchronizeString[MAX_PATH] = L"\0"; WCHAR TreeUpdatedString[MAX_PATH] = L"\0"; WCHAR VendorEventIconString[MAX_PATH] = WIA_ICON_DEVICE_CONNECTED; const BYTE NUMEVENTCAPS = 5; const BYTE NUMCMDCAPS = 2; WIA_DEV_CAP_DRV g_EventCaps[NUMEVENTCAPS] = { {(GUID *)&WIA_EVENT_DEVICE_CONNECTED, WIA_NOTIFICATION_EVENT | WIA_ACTION_EVENT, DeviceConnectedString, DeviceConnectedString, WIA_ICON_DEVICE_CONNECTED }, {(GUID *)&WIA_EVENT_DEVICE_DISCONNECTED, WIA_NOTIFICATION_EVENT, DeviceDisconnectedString, DeviceDisconnectedString, WIA_ICON_DEVICE_DISCONNECTED }, {(GUID *)&WIA_EVENT_ITEM_CREATED, WIA_NOTIFICATION_EVENT, ItemCreatedString, ItemCreatedString, WIA_ICON_ITEM_CREATED }, {(GUID *)&WIA_EVENT_ITEM_DELETED, WIA_NOTIFICATION_EVENT, ItemDeletedString, ItemDeletedString, WIA_ICON_ITEM_DELETED }, {(GUID *)&WIA_EVENT_TREE_UPDATED, WIA_NOTIFICATION_EVENT, TreeUpdatedString, TreeUpdatedString, WIA_ICON_BUILD_DEVICE_TREE } }; WIA_DEV_CAP_DRV g_CmdCaps[NUMCMDCAPS] = { {(GUID*)&WIA_CMD_SYNCHRONIZE, 0, SynchronizeString, SynchronizeString, WIA_ICON_SYNCHRONIZE }, {(GUID*)&WIA_CMD_TAKE_PICTURE, 0, TakePictureString, TakePictureString, WIA_ICON_TAKE_PICTURE } }; // // Constructor // CWiaMiniDriver::CWiaMiniDriver(LPUNKNOWN punkOuter) : m_Capabilities(NULL), m_nEventCaps(0), m_nCmdCaps(0), m_fInitCaptureChecked(FALSE), m_OpenApps(0), m_pDrvItemRoot(NULL), m_pPTPCamera(NULL), m_NumImages(0), m_pStiDevice(NULL), m_bstrDeviceId(NULL), m_bstrRootItemFullName(NULL), m_pDcb(NULL), m_dwObjectBeingSent(0), m_TakePictureDoneEvent(NULL), m_hPtpMutex(NULL), m_bTwoDigitsMillisecondsOutput(FALSE), m_Refs(1) { ::InterlockedIncrement(&CClassFactory::s_Objects); if (punkOuter) m_punkOuter = punkOuter; else m_punkOuter = (IUnknown *)(INonDelegatingUnknown *)this; } // // Destructor // CWiaMiniDriver::~CWiaMiniDriver() { HRESULT hr = S_OK; Shutdown(); // // CWiaMap m_VendorPropMap - delete all PROP_INFO objects // before calling RemoveAll() // for (int i = 0; i < m_VendorPropMap.GetSize(); i++) { delete m_VendorPropMap.GetValueAt(i); m_VendorPropMap.GetValueAt(i) = NULL; } m_VendorPropMap.RemoveAll(); // // CWiaMap m_VendorEventMap - delete all CVendorEventInfo // objects before calling RemoveAll() // for (i = 0; i < m_VendorEventMap.GetSize(); i++) { delete m_VendorEventMap.GetValueAt(i); m_VendorEventMap.GetValueAt(i) = NULL; } m_VendorEventMap.RemoveAll(); if (m_Capabilities) { delete[] m_Capabilities; } if (m_pStiDevice) m_pStiDevice->Release(); if (m_pDcb) m_pDcb->Release(); UnInitializeGDIPlus(); ::InterlockedDecrement(&CClassFactory::s_Objects); } // // INonDelegatingUnknown interface // STDMETHODIMP_(ULONG) CWiaMiniDriver::NonDelegatingAddRef() { ::InterlockedIncrement((LONG *)&m_Refs); return m_Refs; } STDMETHODIMP_(ULONG) CWiaMiniDriver::NonDelegatingRelease() { ::InterlockedDecrement((LONG*)&m_Refs); if (!m_Refs) { delete this; return 0; } return m_Refs; } STDMETHODIMP CWiaMiniDriver::NonDelegatingQueryInterface( REFIID riid, void **ppv ) { if (!ppv) return E_INVALIDARG; *ppv = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppv = static_cast(this); else if (IsEqualIID(riid, IID_IStiUSD)) *ppv = static_cast(this); else if (IsEqualIID(riid, IID_IWiaMiniDrv)) *ppv = static_cast(this); else { return E_NOINTERFACE; } // // Do not call NonDelegatingAddRef() .... // (reinterpret_cast(*ppv))->AddRef(); return S_OK; } // // IUnknown interface // STDMETHODIMP_(ULONG) CWiaMiniDriver::AddRef() { return m_punkOuter->AddRef(); } STDMETHODIMP_(ULONG) CWiaMiniDriver::Release() { return m_punkOuter->Release(); } STDMETHODIMP CWiaMiniDriver::QueryInterface( REFIID riid, void **ppv ) { return m_punkOuter->QueryInterface(riid, ppv); } // // IStiUSD interface // STDMETHODIMP CWiaMiniDriver::Initialize( PSTIDEVICECONTROL pDcb, DWORD dwStiVersion, HKEY hParametersKey ) { USES_CONVERSION; HRESULT hr; wiauDbgInit(g_hInst); DBG_FN("CWiaMiniDriver::Initialize"); if (!pDcb) return STIERR_INVALID_PARAM; // // Check STI specification version number // m_pDcb = pDcb; m_pDcb->AddRef(); hr = InitVendorExtentions(hParametersKey); if (FAILED(hr)) { wiauDbgError("Initialize", "vendor extensions not loaded"); // // Ignore errors from loading vendor extensions // hr = S_OK; } return hr; } STDMETHODIMP CWiaMiniDriver::GetCapabilities(PSTI_USD_CAPS pUsdCaps) { DBG_FN("CWiaMiniDriver::GetCapabilities"); if (!pUsdCaps) return STIERR_INVALID_PARAM; ZeroMemory(pUsdCaps, sizeof(*pUsdCaps)); pUsdCaps->dwVersion = STI_VERSION; pUsdCaps->dwGenericCaps = STI_GENCAP_AUTO_PORTSELECT; return S_OK; } STDMETHODIMP CWiaMiniDriver::GetStatus(PSTI_DEVICE_STATUS pDevStatus) { DBG_FN("CWiaMiniDriver::GetStatus"); if (pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE) pDevStatus->dwOnlineState |= STI_ONLINESTATE_OPERATIONAL; return S_OK; } STDMETHODIMP CWiaMiniDriver::DeviceReset(VOID) { DBG_FN("CWiaMiniDriver::DeviceReset"); // // Camera may not be open if this method is called before // drvInitializeWia. For now just return S_OK. // return HRESULT_FROM_WIN32(m_pPTPCamera->ResetDevice()); return S_OK; } STDMETHODIMP CWiaMiniDriver::Diagnostic(LPDIAG pBuffer) { DBG_FN("CWiaMiniDriver::Diagnostic"); HRESULT hr = STI_OK; // Initialize response buffer pBuffer->sErrorInfo.dwGenericError = STI_NOTCONNECTED; pBuffer->sErrorInfo.dwVendorError = 0; STI_DEVICE_STATUS DevStatus; // // Call status method to verify device is available // ::ZeroMemory(&DevStatus,sizeof(DevStatus)); DevStatus.StatusMask = STI_DEVSTATUS_ONLINE_STATE; // WIAFIX-8/9/2000-davepar Should this function actually talk to the camera? hr = GetStatus(&DevStatus); if (SUCCEEDED(hr)) { if (DevStatus.dwOnlineState & STI_ONLINESTATE_OPERATIONAL) { pBuffer->sErrorInfo.dwGenericError = STI_OK; } } return(hr); } STDMETHODIMP CWiaMiniDriver::SetNotificationHandle(HANDLE hEvent) { DBG_FN("CWiaMiniDriver::SetNotificationHandle"); // Use wiasQueueEvent instead return(S_OK); } STDMETHODIMP CWiaMiniDriver::GetNotificationData(LPSTINOTIFY pBuffer) { DBG_FN("CWiaMiniDriver::GetNotificationData"); // Use wiasQueueEvent instead return STIERR_NOEVENTS; } STDMETHODIMP CWiaMiniDriver::Escape( STI_RAW_CONTROL_CODE EscapeFunction, LPVOID pInData, DWORD cbInDataSize, LPVOID pOutData, DWORD cbOutDataSize, LPDWORD pcbActualDataSize ) { DBG_FN("CWiaMiniDriver::Escape"); HRESULT hr = S_OK; // // Locals // PTP_VENDOR_DATA_IN *pVendorDataIn = NULL; PTP_VENDOR_DATA_OUT *pVendorDataOut = NULL; UINT NumCommandParams = 0; INT NextPhase = 0; BYTE *pReadData = NULL; BYTE *pWriteData = NULL; UINT ReadDataSize = 0; UINT WriteDataSize = 0; DWORD dwObjectToAdd = 0; DWORD dwObjectToRemove = 0; CPtpMutex cpm(m_hPtpMutex); if (EscapeFunction & ESCAPE_PTP_VENDOR_COMMAND) { REQUIRE_ARGS(!pInData || !pOutData || !pcbActualDataSize, hr, "Escape"); if (cbInDataSize < SIZEOF_REQUIRED_VENDOR_DATA_IN) { wiauDbgError("Escape", "InDataSize is too small"); hr = E_FAIL; goto Cleanup; } if (cbOutDataSize < SIZEOF_REQUIRED_VENDOR_DATA_OUT) { wiauDbgError("Escape", "OutDataSize is too small"); hr = E_FAIL; goto Cleanup; } // // Set up some more convenient pointers // pVendorDataIn = (PTP_VENDOR_DATA_IN *) pInData; pVendorDataOut = (PTP_VENDOR_DATA_OUT *) pOutData; if (!(pVendorDataIn->OpCode & PTP_DATACODE_VENDORMASK)) { wiauDbgWarning("VendorCommand", "executing non-vendor command"); } NumCommandParams = pVendorDataIn->NumParams; NextPhase = pVendorDataIn->NextPhase; // // Verify that NumCommandParams and NextPhase are correct // if (NumCommandParams > COMMAND_NUMPARAMS_MAX || (NextPhase != PTP_NEXTPHASE_READ_DATA && NextPhase != PTP_NEXTPHASE_WRITE_DATA && NextPhase != PTP_NEXTPHASE_NO_DATA)) { hr = E_INVALIDARG; goto Cleanup; } // // Data to write and read buffer are right after command and response, // respectively // if (cbInDataSize > SIZEOF_REQUIRED_VENDOR_DATA_IN) { pWriteData = pVendorDataIn->VendorWriteData; WriteDataSize = cbInDataSize - SIZEOF_REQUIRED_VENDOR_DATA_IN; } if (cbOutDataSize > SIZEOF_REQUIRED_VENDOR_DATA_OUT) { pReadData = pVendorDataOut->VendorReadData; ReadDataSize = cbOutDataSize - SIZEOF_REQUIRED_VENDOR_DATA_OUT; } hr = m_pPTPCamera->VendorCommand((PTP_COMMAND *) pInData, (PTP_RESPONSE *) pOutData, &ReadDataSize, pReadData, WriteDataSize, pWriteData, NumCommandParams, NextPhase); REQUIRE_SUCCESS(hr, "Escape", "VendorCommand failed"); *pcbActualDataSize = SIZEOF_REQUIRED_VENDOR_DATA_OUT + ReadDataSize; // // For SendObjectInfo, hand on to handle until SendObject command // if (pVendorDataIn->OpCode == PTP_OPCODE_SENDOBJECTINFO) { m_dwObjectBeingSent = pVendorDataOut->Params[2]; // // For SendObject, add object // } else if (pVendorDataIn->OpCode == PTP_OPCODE_SENDOBJECT) { dwObjectToAdd = m_dwObjectBeingSent; m_dwObjectBeingSent = 0; // // Otherwise, see if add or remove flag is set // } else { if ((EscapeFunction & 0xf) >= PTP_MAX_PARAMS) { wiauDbgError("Escape", "Parameter number too large"); hr = E_FAIL; goto Cleanup; } if (EscapeFunction & ESCAPE_PTP_ADD_OBJ_CMD) { dwObjectToAdd = pVendorDataIn->Params[EscapeFunction & 0xf]; } if (EscapeFunction & ESCAPE_PTP_REM_OBJ_CMD) { dwObjectToRemove = pVendorDataIn->Params[EscapeFunction & 0xf]; } if (EscapeFunction & ESCAPE_PTP_ADD_OBJ_RESP) { dwObjectToAdd = pVendorDataOut->Params[EscapeFunction & 0xf]; } if (EscapeFunction & ESCAPE_PTP_REM_OBJ_RESP) { dwObjectToRemove = pVendorDataOut->Params[EscapeFunction & 0xf]; } } if (dwObjectToAdd) { hr = AddObject(dwObjectToAdd, TRUE); REQUIRE_SUCCESS(hr, "Escape", "AddObject failed"); } if (dwObjectToRemove) { hr = RemoveObject(dwObjectToRemove); REQUIRE_SUCCESS(hr, "Escape", "DeleteObject failed"); } } else if(EscapeFunction == ESCAPE_PTP_CLEAR_STALLS) { hr = m_pPTPCamera->RecoverFromError(); } else hr = STIERR_UNSUPPORTED; Cleanup: return hr; } STDMETHODIMP CWiaMiniDriver::GetLastError(LPDWORD pdwLastDeviceError) { DBG_FN("CWiaMiniDriver::GetLastError"); HRESULT hr = STI_OK; if (IsBadWritePtr(pdwLastDeviceError, 4)) { hr = STIERR_INVALID_PARAM; } else { *pdwLastDeviceError = 0; } return(hr); } STDMETHODIMP CWiaMiniDriver::GetLastErrorInfo(STI_ERROR_INFO *pLastErrorInfo) { DBG_FN("CWiaMiniDriver::GetLastErrorInfo"); HRESULT hr = STI_OK; if (IsBadWritePtr(pLastErrorInfo, 4)) { hr = STIERR_INVALID_PARAM; } else { pLastErrorInfo->dwGenericError = 0; pLastErrorInfo->szExtendedErrorText[0] = L'\0'; } return(hr); } STDMETHODIMP CWiaMiniDriver::LockDevice(VOID) { DBG_FN("CWiaMiniDriver::LockDevice"); return(S_OK); } STDMETHODIMP CWiaMiniDriver::UnLockDevice(VOID) { DBG_FN("CWiaMiniDriver::UnLockDevice"); return(S_OK); } STDMETHODIMP CWiaMiniDriver::RawReadData( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { DBG_FN("CWiaMiniDriver::RawReadData"); return(STIERR_UNSUPPORTED); } STDMETHODIMP CWiaMiniDriver::RawWriteData( LPVOID lpBuffer, DWORD dwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { DBG_FN("CWiaMiniDriver::RawWriteData"); return(STIERR_UNSUPPORTED); } STDMETHODIMP CWiaMiniDriver::RawReadCommand( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { DBG_FN("CWiaMiniDriver::RawReadCommand"); return(STIERR_UNSUPPORTED); } STDMETHODIMP CWiaMiniDriver::RawWriteCommand( LPVOID lpBuffer, DWORD nNumberOfBytes, LPOVERLAPPED lpOverlapped ) { DBG_FN("CWiaMiniDriver::RawWriteCommand"); return(STIERR_UNSUPPORTED); } ///////////////////////////////////////////////////// // // IWiaMiniDrvItem methods // ///////////////////////////////////////////////////// // // This method is the first call to initialize the mini driver // This is where a mini driver establish its IWiaDrvItem tree // // Input: // pWiasContext -- context used to call Wias service // lFlags -- misc flags. Not used for now // bstrDeviceId -- the device id // bstrRootItemFullName -- the full name of root driver item // pStiDevice -- IStiDevice interface pointer // punkOuter -- not used. // ppDrvItemRoot -- to return our root IWiaDrvItem // ppunkInner -- mini driver special interface which allows // the applications to directly access. // plDevErrVal -- to return device error code. // HRESULT CWiaMiniDriver::drvInitializeWia( BYTE *pWiasContext, LONG lFlags, BSTR bstrDeviceID, BSTR bstrRootItemFullName, IUnknown *pStiDevice, IUnknown *punkOuter, IWiaDrvItem **ppDrvItemRoot, IUnknown **ppunkInner, LONG *plDevErrVal ) { #define REQUIRE(x, y) if(!(x)) { wiauDbgError("drvInitializeWia", y); hr = HRESULT_FROM_WIN32(::GetLastError()); goto Cleanup; } #define REQUIRE_SUCCESS_(x, y) if(FAILED(x)) { wiauDbgError("drvInitializeWia", y); goto Cleanup; } DBG_FN("CWiaMiniDriver::drvInitializeWia"); HRESULT hr = S_OK; *plDevErrVal = DEVERR_OK; if (!ppDrvItemRoot || !ppunkInner || !plDevErrVal) { wiauDbgError("drvInitializeWia", "invalid arg"); return E_INVALIDARG; } *ppDrvItemRoot = NULL; *ppunkInner = NULL; m_OpenApps++; // // If this is the first app, create everything // if (m_OpenApps == 1) { // // Load the strings from the resource // hr = LoadStrings(); REQUIRE_SUCCESS_(hr, "LoadStrings failed"); // // Set up a mutex to guarantee exclusive access to the device and the minidriver's structures // if(!m_hPtpMutex) { m_hPtpMutex = CreateMutex(NULL, FALSE, NULL); REQUIRE(m_hPtpMutex, "CreateMutex failed"); } { CPtpMutex cpm(m_hPtpMutex); *ppDrvItemRoot = NULL; // // Create event for waiting for TakePicture command to complete // if (!m_TakePictureDoneEvent) { m_TakePictureDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL); REQUIRE(m_TakePictureDoneEvent, "CreateEvent failed"); } // // Allocate strings needed for later // if (!m_bstrDeviceId) { m_bstrDeviceId = SysAllocString(bstrDeviceID); REQUIRE(m_bstrDeviceId, "failed to allocate Device ID string"); } if (!m_bstrRootItemFullName) { m_bstrRootItemFullName = SysAllocString(bstrRootItemFullName); REQUIRE(m_bstrRootItemFullName, "failed to allocate root item name"); } // // Create a camera object. Right now we only handle USB, but in the future this could look at the // port name to figure out what type of camera to create. // if (!m_pPTPCamera) { m_pPTPCamera = new CUsbCamera; REQUIRE(m_pPTPCamera, "failed to new CUsbCamera"); } // // Open the camera // if (!m_pPTPCamera->IsCameraOpen()) { // // Retrieve the port name from the ISTIDeviceControl // WCHAR wcsPortName[MAX_PATH]; hr = m_pDcb->GetMyDevicePortName(wcsPortName, sizeof(wcsPortName)); REQUIRE_SUCCESS_(hr, "GetMyDevicePortName failed"); hr = m_pPTPCamera->Open(wcsPortName, &EventCallback, &DataCallback, (LPVOID) this); REQUIRE_SUCCESS_(hr, "Camera open failed"); } // // Open a session on the camera. Doesn't matter which session ID we use, so just use 1. // if (!m_pPTPCamera->IsCameraSessionOpen()) { hr = m_pPTPCamera->OpenSession(WIA_SESSION_ID); REQUIRE_SUCCESS_(hr, "OpenSession failed"); } // // Get the DeviceInfo for the camera // hr = m_pPTPCamera->GetDeviceInfo(&m_DeviceInfo); REQUIRE_SUCCESS_(hr, "GetDeviceInfo failed"); // // Remove properties that aren't supported by WIA. RGB gain isn't supported // because PTP defines it as a string and WIA can't handle string ranges. // m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_RGBGAIN); m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_FUNCTIONMODE); // // Special hack for the Kodak DC4800 // // Some property codes (which the camera says it supports) cause the camera to // stall the endpoint when the GetDevicePropDesc command is sent // The hack can be removed only if support of DC4800 is removed // if (m_pPTPCamera->GetHackModel() == HACK_MODEL_DC4800) { wiauDbgTrace("drvInitializeWia", "removing DC4800 unsupported props"); const WORD KODAK_PROPCODE_D001 = 0xD001; m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_RGBGAIN); m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_FNUMBER); m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_FOCUSDISTANCE); m_DeviceInfo.m_SupportedProps.Remove(PTP_PROPERTYCODE_EXPOSURETIME); m_DeviceInfo.m_SupportedProps.Remove(KODAK_PROPCODE_D001); } // // Get all the StorageInfo structures // if (m_StorageIds.GetSize() == 0) { hr = m_pPTPCamera->GetStorageIDs(&m_StorageIds); REQUIRE_SUCCESS_(hr, "GetStorageIDs failed"); CPtpStorageInfo tempSI; for (int count = 0; count < m_StorageIds.GetSize(); count++) { REQUIRE(m_StorageInfos.Add(tempSI), "memory allocation failed"); // // Get info about logical storages only. If we ask for info about non-logical // storage (ejected media), it may stall the camera. // if (m_StorageIds[count] & PTP_STORAGEID_LOGICAL) { hr = m_pPTPCamera->GetStorageInfo(m_StorageIds[count], &m_StorageInfos[count]); REQUIRE_SUCCESS_(hr, "GetStorageInfo failed"); } // // Add an empty entry to the DCIM handle array // ULONG dummy = 0; REQUIRE(m_DcimHandle.Add(dummy), "add dcim handle failed"); } } // // Get all of the property description structures supported by the device // if (m_PropDescs.GetSize() == 0) { CPtpPropDesc tempPD; int NumProps = m_DeviceInfo.m_SupportedProps.GetSize(); REQUIRE(m_PropDescs.GrowTo(NumProps), "reallocation of supported properties array failed"); PROP_INFO *pPropInfo = NULL; WORD PropCode = 0; for (int count = 0; count < NumProps; count++) { PropCode = m_DeviceInfo.m_SupportedProps[count]; // // Remove properties that aren't supported by this driver or by // vendor entries in the INF // pPropInfo = PropCodeToPropInfo(PropCode); if (!pPropInfo->PropId && PropCode != PTP_PROPERTYCODE_IMAGESIZE) { wiauDbgTrace("drvInitializeWia", "removing unsupported prop, 0x%04x", PropCode); m_DeviceInfo.m_SupportedProps.RemoveAt(count); NumProps--; count--; } else { // // Get the property description info from the device // REQUIRE(m_PropDescs.Add(tempPD), "add prop desc failed"); hr = m_pPTPCamera->GetDevicePropDesc(PropCode, &m_PropDescs[count]); REQUIRE_SUCCESS_(hr, "GetDevicePropDesc failed"); } } } // // Cache the STI interface // if (!m_pStiDevice) { m_pStiDevice = (IStiDevice *)pStiDevice; m_pStiDevice->AddRef(); } // // Build the tree, if we haven't already // if (!m_pDrvItemRoot) { hr = CreateDrvItemTree(&m_pDrvItemRoot); REQUIRE_SUCCESS_(hr, "CreateDrvItemTree failed"); } } } *ppDrvItemRoot = m_pDrvItemRoot; Cleanup: if(FAILED(hr)) { // force re-init to happen next time someone tries to create // device m_OpenApps = 0; } // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This methods gets called when a client connection is going away. // // Input: // pWiasContext -- Pointer to the WIA Root item context of the client's item tree. // HRESULT CWiaMiniDriver::drvUnInitializeWia(BYTE *pWiasContext) { DBG_FN("CWiaMiniDriver::drvUnInitializeWia"); HRESULT hr = S_OK; if (!pWiasContext) { wiauDbgError("drvUnInitializeWia", "invalid arg"); return E_INVALIDARG; } m_OpenApps--; if (m_OpenApps == 0) { Shutdown(); } if(m_OpenApps < 0) { // allow unmatched drvUninializeWia calls and don't ever make // m_OpenApps negative m_OpenApps = 0; } return hr; } // // This method executes a command on the device // // Input: // pWiasContext -- context used to call wias services // lFlags -- Misc flags, not used // pCommandGuid -- the command guid // ppDrvItem -- new IWiaDrvItem if the command creates new item // plDevErrVal -- to return device error code // HRESULT CWiaMiniDriver::drvDeviceCommand( BYTE *pWiasContext, LONG lFlags, const GUID *pCommandGuid, IWiaDrvItem **ppDrvItem, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvDeviceCommand"); HRESULT hr = S_OK; if (!pWiasContext || !pCommandGuid || !ppDrvItem || !plDevErrVal) { wiauDbgError("drvDeviceCommand", "invalid arg"); return E_INVALIDARG; } *ppDrvItem = NULL; *plDevErrVal = DEVERR_OK; if (*pCommandGuid == WIA_CMD_TAKE_PICTURE && m_DeviceInfo.m_SupportedOps.Find(PTP_OPCODE_INITIATECAPTURE) >= 0) { LONG ItemType = 0; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgError("drvDeviceCommand", "wiasGetItemType failed"); goto cleanup; } // // TakePicture only works on the root // if (WiaItemTypeRoot & ItemType) { hr = WriteDeviceProperties(pWiasContext); if (FAILED(hr)) { wiauDbgError("drvDeviceCommand", "WriteDeviceProperties failed"); goto cleanup; } hr = TakePicture(pWiasContext, ppDrvItem); if (FAILED(hr)) { wiauDbgError("drvDeviceCommand", "TakePicture failed"); goto cleanup; } } } else if (*pCommandGuid == WIA_CMD_SYNCHRONIZE) { // // Don't need to do anything, because the PTP driver is always in sync with the device // } else { hr = E_NOTIMPL; } cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method deletes an object from the camera. The WIA service will ensure that // the item has no children and has access rights to be deleted, and the service will // take care of deleting the driver item and calling drvFreeItemContext. // // Input: // pWiasContext -- wias context that identifies the item // lFlags -- misc flags // plDevErrVal -- to return the device error // STDMETHODIMP CWiaMiniDriver::drvDeleteItem( BYTE *pWiasContext, LONG lFlags, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvDeleteItem"); HRESULT hr = S_OK; if (!pWiasContext || !plDevErrVal) { wiauDbgError("drvDeleteItem", "invalid arg"); return E_INVALIDARG; } // // Verify that PTP_OPCODE_DELETEOBJECT command is supported by the camera // if (m_DeviceInfo.m_SupportedOps.Find(PTP_OPCODE_DELETEOBJECT) < 0) { wiauDbgError("drvDeleteItem", "PTP_OPCODE_DELETEOBJECT command is not supported by the camera"); return E_NOTIMPL; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); IWiaDrvItem *pDrvItem; DRVITEM_CONTEXT *pItemCtx; hr = WiasContextToItemContext(pWiasContext, &pItemCtx, &pDrvItem); if (FAILED(hr)) { wiauDbgError("drvDeleteItem", "WiasContextToItemContext failed"); goto cleanup; } // // Delete the object on the camera // hr = m_pPTPCamera->DeleteObject(pItemCtx->pObjectInfo->m_ObjectHandle, 0); if (FAILED(hr)) { wiauDbgError("drvDeleteItem", "DeleteObject failed"); goto cleanup; } // // Keep count of the number of images // if (pItemCtx->pObjectInfo->m_FormatCode & PTP_FORMATMASK_IMAGE) { m_NumImages--; } // // Update Storage Info (we are especially interested in Free Space info) // hr = UpdateStorageInfo(pItemCtx->pObjectInfo->m_StorageId); if (FAILED(hr)) { wiauDbgError("drvDeleteItem", "UpdateStorageInfo failed"); // we can proceed, even if storage info can't be updated } // // Remove the item from the m_HandleItem map // m_HandleItem.Remove(pItemCtx->pObjectInfo->m_ObjectHandle); // // Get the item's full name // BSTR bstrFullName; hr = pDrvItem->GetFullItemName(&bstrFullName); if (FAILED(hr)) { wiauDbgError("drvDeleteItem", "GetFullItemName failed"); goto cleanup; } // // Queue an "item deleted" event // hr = wiasQueueEvent(m_bstrDeviceId, &WIA_EVENT_ITEM_DELETED, bstrFullName); if (FAILED(hr)) { wiauDbgErrorHr(hr, "drvDeleteItem", "wiasQueueEvent failed"); // Continue to free the string and return hr } SysFreeString(bstrFullName); cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method updates Storage Info for the specified storage // Input: // StorageId - ID of the sorage to be updated // HRESULT CWiaMiniDriver::UpdateStorageInfo(ULONG StorageId) { HRESULT hr = S_FALSE; BOOL bDone = FALSE; for (int count = 0; (count < m_StorageIds.GetSize()) && (!bDone); count++) { if (m_StorageIds[count] == StorageId) { bDone = TRUE; hr = m_pPTPCamera->GetStorageInfo(m_StorageIds[count], &m_StorageInfos[count]); } } return hr; } // // This method returns the device capabilities // // Input: // pWiasContext -- wias service context // lFlags -- indicate what capabilities to return // pCelt -- to return number of entries are returned // ppCapbilities -- to receive the capabilities // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvGetCapabilities( BYTE *pWiasContext, LONG lFlags, LONG *pCelt, WIA_DEV_CAP_DRV **ppCapabilities, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvGetCapabilities"); HRESULT hr = S_OK; if (!pCelt || !ppCapabilities || !plDevErrVal) { wiauDbgError("drvGetCapabilities", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; // // Load the strings from the resource // hr = LoadStrings(); if (FAILED(hr)) { wiauDbgError("drvGetCapabilities", "LoadStrings failed"); return E_FAIL; } // // check if we have already built the list of capabilities. If not, build it // It will have the following structure: // // XXXXXXXXXXXXXXXXXXXXXXXXX YYYYYYYYYYYYYYYYYYYYYYYYYYY ZZZZZZZZZZZZZZZZZZZZZZZ // (predefined events) (vendor events) (predefined commands) // if (m_Capabilities == NULL) { UINT nVendorEvents = m_VendorEventMap.GetSize(); if (nVendorEvents > MAX_VENDOR_EVENTS) { wiauDbgWarning("drvGetCapabilities", "vendor events limit exceeded, ignoring events over limit"); nVendorEvents = MAX_VENDOR_EVENTS; } m_nEventCaps = NUMEVENTCAPS + nVendorEvents; m_nCmdCaps = NUMCMDCAPS; // we don't need to put vendor commands in the list. they are called through escape function m_Capabilities = new WIA_DEV_CAP_DRV[m_nEventCaps + m_nCmdCaps]; // WIA uses this array instead of copying, don't delete it if (m_Capabilities == NULL) { return E_OUTOFMEMORY; } // // create events first // memcpy(m_Capabilities, g_EventCaps, sizeof(g_EventCaps)); // default events for (UINT i = 0; i < nVendorEvents; i++) // vendor events { CVendorEventInfo *pEventInfo = m_VendorEventMap.GetValueAt(i); m_Capabilities[NUMEVENTCAPS + i].guid = pEventInfo->pGuid; m_Capabilities[NUMEVENTCAPS + i].ulFlags = WIA_NOTIFICATION_EVENT | WIA_ACTION_EVENT; m_Capabilities[NUMEVENTCAPS + i].wszIcon = VendorEventIconString; m_Capabilities[NUMEVENTCAPS + i].wszName = pEventInfo->EventName; m_Capabilities[NUMEVENTCAPS + i].wszDescription = pEventInfo->EventName; } // // add commands // memcpy(m_Capabilities + m_nEventCaps, g_CmdCaps, sizeof(g_CmdCaps)); } // // eventing code calls this entry point without first going // through drvInitializeWia // if(lFlags == WIA_DEVICE_EVENTS) { *pCelt = m_nEventCaps; *ppCapabilities = m_Capabilities; return S_OK; } // // query if camera supports InitiateCapture command (if we hadn't already) // if (!m_fInitCaptureChecked) { m_fInitCaptureChecked = TRUE; CPtpMutex cpm(m_hPtpMutex); if (m_DeviceInfo.m_SupportedOps.Find(PTP_OPCODE_INITIATECAPTURE) < 0) { m_nCmdCaps--; } } // // Report commands or (events and commands) // switch (lFlags) { case WIA_DEVICE_COMMANDS: *pCelt = m_nCmdCaps; // // Command capability list is right behind the event list // *ppCapabilities = m_Capabilities + m_nEventCaps; break; case (WIA_DEVICE_EVENTS | WIA_DEVICE_COMMANDS): *pCelt = m_nEventCaps + m_nCmdCaps; *ppCapabilities = m_Capabilities; break; default: break; } // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method initializes an item's properties. If the item is the // root item, this function initializes the device properties. // // Input: // pWiasContext -- wias service context // lFlags -- misc flags // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvInitItemProperties( BYTE *pWiasContext, LONG lFlags, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvInitItemProperties"); HRESULT hr = S_OK; if (!pWiasContext || !plDevErrVal) { wiauDbgError("drvInitItemProperties", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); LONG ItemType; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgErrorHr(hr, "drvInitItemProperties", "wiasGetItemType failed"); goto cleanup; } if (ItemType & WiaItemTypeRoot) { hr = InitDeviceProperties(pWiasContext); if (FAILED(hr)) { wiauDbgError("drvInitItemProperties", "InitDeviceProperties failed"); goto cleanup; } } else { hr = InitItemProperties(pWiasContext); if (FAILED(hr)) { wiauDbgError("drvInitItemProperties", "InitItemProperties failed"); goto cleanup; } } cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method locks the device for exclusive use for the caller // // Input: // pWiasContext -- wias context // lFlags -- misc flags // Output: // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvLockWiaDevice( BYTE *pWiasContext, LONG lFlags, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvLockWiaDevice"); *plDevErrVal = DEVERR_OK; return S_OK; } // // This method unlocks the device // // Input: // pWiasContext -- wias context // lFlags -- misc flags // Output: // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvUnLockWiaDevice( BYTE *pWiasContext, LONG lFlags, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvUnLockWiaDevice"); *plDevErrVal = DEVERR_OK; return S_OK; } // // This method analyizes the given driver item. It is not implemented for cameras. // // Input: // pWiasContext -- wias context // lFlags -- misc flags // Output: // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvAnalyzeItem( BYTE *pWiasContext, LONG lFlags, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvAnalyzeItem"); if (!pWiasContext || !plDevErrVal) { wiauDbgError("drvAnalyzeItem", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; return E_NOTIMPL; } // // This method returns the item's available format information. Every WIA // minidriver must support WiaImgFmt_BMP and WiaImgFmt_MEMORYBMP. This could // be a problem, because this driver can only decode JPEG and TIFF currently. // For other formats, we will not advertise BMP formats. // // Input: // pWiasContext -- wias service context // lFlags -- misc flags // pcelt -- to return how many format info the item has // ppwfi -- to hold a pointer to the format info // Output: // plDevErrVal -- to return device error code // STDMETHODIMP CWiaMiniDriver::drvGetWiaFormatInfo( BYTE *pWiasContext, LONG lFlags, LONG *pcelt, WIA_FORMAT_INFO **ppwfi, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvGetWiaFormatInfo"); HRESULT hr = S_OK; if (!pWiasContext || !pcelt || !ppwfi || !plDevErrVal) { wiauDbgError("drvGetWiaFormatInfo", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); *pcelt = 0; *ppwfi = NULL; DRVITEM_CONTEXT *pItemCtx = NULL; hr = WiasContextToItemContext(pWiasContext, &pItemCtx); if (FAILED(hr)) { wiauDbgError("drvGetWiaFormatInfo", "WiasContextToItemContext failed"); goto cleanup; } if (!pItemCtx) { wiauDbgError("drvGetWiaFormatInfo", "item context is null"); hr = E_FAIL; goto cleanup; } if (!pItemCtx->pFormatInfos) { // // The format info list is not intialized. Do it now. // LONG ItemType; DWORD ui32; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgErrorHr(hr, "drvGetWiaFormatInfo", "wiasGetItemType failed"); goto cleanup; } if (ItemType & WiaItemTypeFile) { // // Create the supported format for the item, based on the format stored in the // ObjectInfo structure. // if (!pItemCtx->pObjectInfo) { wiauDbgError("drvGetWiaFormatInfo", "pObjectInfo not initialized"); hr = E_FAIL; goto cleanup; } // // If the format is JPEG or TIFF based, add the BMP types to the format array, // since this driver can convert those to BMP // WORD FormatCode = pItemCtx->pObjectInfo->m_FormatCode; BOOL bAddBmp = (FormatCode == PTP_FORMATCODE_IMAGE_EXIF) || (FormatCode == PTP_FORMATCODE_IMAGE_TIFFEP) || (FormatCode == PTP_FORMATCODE_IMAGE_TIFF) || (FormatCode == PTP_FORMATCODE_IMAGE_JFIF) || (FormatCode == PTP_FORMATCODE_IMAGE_FLASHPIX) || (FormatCode == PTP_FORMATCODE_IMAGE_BMP) || (FormatCode == PTP_FORMATCODE_IMAGE_CIFF) || (FormatCode == PTP_FORMATCODE_IMAGE_GIF) || (FormatCode == PTP_FORMATCODE_IMAGE_JFIF) || (FormatCode == PTP_FORMATCODE_IMAGE_PCD) || (FormatCode == PTP_FORMATCODE_IMAGE_PICT) || (FormatCode == PTP_FORMATCODE_IMAGE_PNG) || (FormatCode == PTP_FORMATCODE_IMAGE_TIFFIT) || (FormatCode == PTP_FORMATCODE_IMAGE_JP2) || (FormatCode == PTP_FORMATCODE_IMAGE_JPX); ULONG NumWfi = bAddBmp ? 2 : 1; // // Allocate two entries for each format, one for file transfer and one for callback // WIA_FORMAT_INFO *pwfi = new WIA_FORMAT_INFO[2 * NumWfi]; if (!pwfi) { wiauDbgError("drvGetWiaFormatInfo", "memory allocation failed"); hr = E_OUTOFMEMORY; goto cleanup; } FORMAT_INFO *pFormatInfo = FormatCodeToFormatInfo(FormatCode); pwfi[0].lTymed = TYMED_FILE; pwfi[1].lTymed = TYMED_CALLBACK; if(pFormatInfo->FormatGuid) { pwfi[0].guidFormatID = *pFormatInfo->FormatGuid; pwfi[1].guidFormatID = *pFormatInfo->FormatGuid; } else { pwfi[0].guidFormatID = WiaImgFmt_UNDEFINED; pwfi[1].guidFormatID = WiaImgFmt_UNDEFINED; } // // Add the BMP entries when appropriate // if (bAddBmp) { pwfi[2].guidFormatID = WiaImgFmt_BMP; pwfi[2].lTymed = TYMED_FILE; pwfi[3].guidFormatID = WiaImgFmt_MEMORYBMP; pwfi[3].lTymed = TYMED_CALLBACK; } pItemCtx->NumFormatInfos = 2 * NumWfi; pItemCtx->pFormatInfos = pwfi; } else if ((ItemType & WiaItemTypeFolder) || (ItemType & WiaItemTypeRoot)) { // // Folders and the root don't really need format info, but some apps may fail // without it. Create a fake list just in case. // pItemCtx->pFormatInfos = new WIA_FORMAT_INFO[2]; if (!pItemCtx->pFormatInfos) { wiauDbgError("drvGetWiaFormatInfo", "memory allocation failed"); hr = E_OUTOFMEMORY; goto cleanup; } pItemCtx->NumFormatInfos = 2; pItemCtx->pFormatInfos[0].lTymed = TYMED_FILE; pItemCtx->pFormatInfos[0].guidFormatID = FMT_NOTHING; pItemCtx->pFormatInfos[1].lTymed = TYMED_CALLBACK; pItemCtx->pFormatInfos[1].guidFormatID = FMT_NOTHING; } } *pcelt = pItemCtx->NumFormatInfos; *ppwfi = pItemCtx->pFormatInfos; cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method processes pnp events // // Input: // pEventGuid -- the event identifier // bstrDeviceId -- the designated device // ulReserved -- reserved // STDMETHODIMP CWiaMiniDriver::drvNotifyPnpEvent( const GUID *pEventGuid, BSTR bstrDeviceId, ULONG ulReserved ) { return S_OK; } // // This method reads the item properties // // Input: // pWiasContext -- wias context // lFlags -- misc flags // NumPropSpecs -- number of properties to be read // pPropSpecs -- an array of PROPSPEC that specifies // what properties should be read // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvReadItemProperties( BYTE *pWiasContext, LONG lFlags, ULONG NumPropSpecs, const PROPSPEC *pPropSpecs, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvReadItemProperties"); HRESULT hr = S_OK; if (!pWiasContext || !pPropSpecs || !plDevErrVal) { wiauDbgError("drvReadItemProperties", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); LONG ItemType = 0; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgError("drvReadItemProperties", "wiasGetItemType failed"); goto cleanup; } if (WiaItemTypeRoot & ItemType) hr = ReadDeviceProperties(pWiasContext, NumPropSpecs, pPropSpecs); else hr = ReadItemProperties(pWiasContext, NumPropSpecs, pPropSpecs); cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method writes the item properties // // Input: // pWiasContext -- wias context // lFlags -- misc flags // pmdtc -- mini driver transfer context // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvWriteItemProperties( BYTE *pWiasContext, LONG lFlags, PMINIDRV_TRANSFER_CONTEXT pmdtc, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvWriteItemProperties"); HRESULT hr = S_OK; if (!pWiasContext || !pmdtc || !plDevErrVal) { wiauDbgError("drvWriteItemProperties", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); LONG ItemType = 0; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgError("drvWriteItemProperties", "wiasGetItemType failed"); goto cleanup; } // // Only properties to write are on the root // if (WiaItemTypeRoot & ItemType) { hr = WriteDeviceProperties(pWiasContext); if (FAILED(hr)) { wiauDbgError("drvWriteItemProperties", "WriteDeviceProperties failed"); goto cleanup; } } cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method validates the item properties // // Input: // pWiasContext -- wias context // lFlags -- misc flags // NumPropSpecs -- number of properties to be read // pPropSpecs -- an array of PROPSPEC that specifies // what properties should be read // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvValidateItemProperties( BYTE *pWiasContext, LONG lFlags, ULONG NumPropSpecs, const PROPSPEC *pPropSpecs, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvValidateItemProperties"); HRESULT hr = S_OK; if (!pWiasContext || !pPropSpecs || !plDevErrVal) { wiauDbgError("drvValidateItemProperties", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); LONG ItemType = 0; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgError("drvValidateItemProperties", "wiasGetItemType failed"); goto cleanup; } if (WiaItemTypeRoot & ItemType) { hr = ValidateDeviceProperties(pWiasContext, NumPropSpecs, pPropSpecs); if (FAILED(hr)) { wiauDbgError("drvValidateItemProperties", "ValidateDeviceProperties failed"); goto cleanup; } } else { hr = ValidateItemProperties(pWiasContext, NumPropSpecs, pPropSpecs, ItemType); if (FAILED(hr)) { wiauDbgError("drvValidateItemProperties", "ValidateItemProperties failed"); goto cleanup; } } cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method acquires the item's data // // Input: // pWiasContext -- wias context // lFlags -- misc flags // pmdtc -- mini driver transfer context // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvAcquireItemData( BYTE *pWiasContext, LONG lFlags, PMINIDRV_TRANSFER_CONTEXT pmdtc, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvAcquireItemData"); HRESULT hr = S_OK; if (!pWiasContext || !pmdtc || !plDevErrVal) { wiauDbgError("drvAcquireItemData", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; CPtpMutex cpm(m_hPtpMutex); LONG ItemType = 0; hr = wiasGetItemType(pWiasContext, &ItemType); if (FAILED(hr)) { wiauDbgError("drvAcquireItemData", "wiasGetItemType failed"); goto cleanup; } DRVITEM_CONTEXT *pItemCtx; hr = WiasContextToItemContext(pWiasContext, &pItemCtx); if (FAILED(hr)) { wiauDbgError("AcquireData", "WiasContextToItemContext failed"); goto cleanup; } wiauDbgTrace("drvAcquireItemData", "transferring image with tymed, 0x%08x", pmdtc->tymed); // // Translate to BMP, if needed. Otherwise just transfer the data. // if ((IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_BMP) || IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_MEMORYBMP)) && (pItemCtx->pObjectInfo->m_FormatCode != PTP_FORMATCODE_IMAGE_BMP)) { hr = AcquireDataAndTranslate(pWiasContext, pItemCtx, pmdtc); if (FAILED(hr)) { wiauDbgError("drvAcquireItemData", "AcquireDataAndTranslate failed"); goto cleanup; } } else { hr = AcquireData(pItemCtx, pmdtc); if (FAILED(hr)) { wiauDbgError("drvAcquireItemData", "AcquireData failed"); goto cleanup; } } cleanup: // // Update WIA on any changes in camera's state, like 'camera was reset' // NotifyWiaOnStateChanges(); return hr; } // // This method returns a description about the given device error code // // Input: // lFlags -- misc flags // lDevErrVal -- the designated error code // ppDevErrStr -- to receive a string pointer to the description // plDevErrVal -- device error code(used to report error if this method // need to retreive the string from the device // STDMETHODIMP CWiaMiniDriver::drvGetDeviceErrorStr( LONG lFlags, LONG lDevErrVal, LPOLESTR *ppDevErrStr, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvGetDeviceErrorStr"); HRESULT hr = S_OK; if (!ppDevErrStr || !plDevErrVal) { wiauDbgError("drvGetDeviceErrorStr", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; // // WIAFIX-10/2/2000-davepar No device-specific errors at this time // return E_NOTIMPL; } // // This method frees the given driver item context // // Input: // lFlags -- misc flags // pItemCtx -- the item context to be freed // plDevErrVal -- to return device error // STDMETHODIMP CWiaMiniDriver::drvFreeDrvItemContext( LONG lFlags, BYTE *pContext, LONG *plDevErrVal ) { DBG_FN("CWiaMiniDriver::drvFreeDrvItemContext"); HRESULT hr = S_OK; if (!pContext || !plDevErrVal) { wiauDbgError("drvFreeDrvItemContext", "invalid arg"); return E_INVALIDARG; } *plDevErrVal = DEVERR_OK; DRVITEM_CONTEXT *pItemCtx = (DRVITEM_CONTEXT *)pContext; if (pItemCtx) { if (pItemCtx->pThumb) { delete []pItemCtx->pThumb; pItemCtx->pThumb = NULL; } if (pItemCtx->pFormatInfos) { delete [] pItemCtx->pFormatInfos; pItemCtx->pFormatInfos = NULL; } if (pItemCtx->pObjectInfo) { delete pItemCtx->pObjectInfo; } } return hr; } // // This function will shutdown the driver // HRESULT CWiaMiniDriver::Shutdown() { DBG_FN("CWiaMiniDriver::Shutdown"); HRESULT hr = S_OK; // // Close the camera // wiauDbgTrace("Shutdown", "closing connection with camera"); if (m_pPTPCamera) { hr = m_pPTPCamera->Close(); if (FAILED(hr)) { wiauDbgError("Shutdown", "Close failed"); } } // // Free data structures // if (m_pDrvItemRoot) { m_pDrvItemRoot->UnlinkItemTree(WiaItemTypeDisconnected); m_pDrvItemRoot->Release(); m_pDrvItemRoot = NULL; } if (m_pPTPCamera) { delete m_pPTPCamera; m_pPTPCamera = NULL; } m_StorageIds.RemoveAll(); m_StorageInfos.RemoveAll(); m_PropDescs.RemoveAll(); // // CWiaMap m_HandleItem - we don't need to delete IWiaDrvItem // objects here, they are destroyed when items tree is unlinked // m_HandleItem.RemoveAll(); m_NumImages = 0; if (m_bstrDeviceId) { SysFreeString(m_bstrDeviceId); m_bstrDeviceId = NULL; } if (m_bstrRootItemFullName) { SysFreeString(m_bstrRootItemFullName); m_bstrRootItemFullName = NULL; } if (m_TakePictureDoneEvent) { CloseHandle(m_TakePictureDoneEvent); m_TakePictureDoneEvent = NULL; } if (m_hPtpMutex) { CloseHandle(m_hPtpMutex); m_hPtpMutex = NULL; } m_DcimHandle.RemoveAll(); // // CWiaMap m_AncAssocParent - we don't need to delete IWiaDrvItem // objects here, they are destroyed when items tree is unlinked // m_AncAssocParent.RemoveAll(); return hr; } // // This function asks the camera to take a picture. It also inserts // the new picture into the drive item tree. // // Input: // pWiasContext -- wias context // lFlags -- misc flags // plDevErrVal -- to return device error code // HRESULT CWiaMiniDriver::TakePicture( BYTE *pWiasContext, IWiaDrvItem **ppNewItem ) { DBG_FN("CWiaMiniDriver::TakePicture"); HRESULT hr = S_OK; if (!pWiasContext || !ppNewItem) { wiauDbgError("TakePicture", "invalid arg"); return E_INVALIDARG; } IWiaDrvItem *pDrvItem, *pParentItem; DRVITEM_CONTEXT *pItemCtx = NULL; *ppNewItem = NULL; WORD FormatCode = 0; // // Kodak DC4800 must have the format code parameter set to zero // This hack can be removed only if support of Kodak DC4800 is removed // if (m_pPTPCamera->GetHackModel() == HACK_MODEL_DC4800) { FormatCode = 0; } else { // // Determine which format to capture // GUID FormatGuid; hr = wiasReadPropGuid(pWiasContext, WIA_IPA_FORMAT, &FormatGuid, NULL, TRUE); if (FAILED(hr)) { wiauDbgError("TakePicture", "wiasReadPropLong failed"); return hr; } FormatCode = FormatGuidToFormatCode(&FormatGuid); } { CPtpMutex cpm(m_hPtpMutex); // // Reset the event that is waited upon below // if (!ResetEvent(m_TakePictureDoneEvent)) { hr = HRESULT_FROM_WIN32(::GetLastError()); wiauDbgErrorHr(hr, "TakePicture", "ResetEvent failed"); return hr; } // // Clear the list of captured objects // m_CapturedObjects.RemoveAll(); // // Start the image capture // hr = m_pPTPCamera->InitiateCapture(PTP_STORAGEID_DEFAULT, FormatCode); if (FAILED(hr)) { wiauDbgError("TakePicture", "InitiateCapture failed"); return hr; } } // // Estimate how long the capture may take. Assume 30 seconds for a simple single shot. // DWORD dwCaptureTimeout = 30000; // // Check if there is CaptureDelay, and add it to timeout // int nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_CAPTUREDELAY); if (nIndex != -1) { DWORD dwCaptureDelay = m_PropDescs[nIndex].m_lCurrent; dwCaptureTimeout += dwCaptureDelay; } // // Check if the camera is in Burst or Timelapse mode // nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_STILLCAPTUREMODE); if (nIndex != -1) { DWORD dwFuncMode = m_PropDescs[nIndex].m_lCurrent; if (dwFuncMode == PTP_CAPTUREMODE_BURST) { // // Calculate how much time burst operation may take ((BurstNumber - 1) * BurstInterval) // DWORD dwBurstNumber = 1; DWORD dwBurstInterval = 1000; // assume 1 second per picture, if device does not specify interval nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_BURSTNUMBER); if (nIndex != -1) { dwBurstNumber = m_PropDescs[nIndex].m_lCurrent; } nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_BURSTINTERVAL); if (nIndex != -1) { dwBurstInterval = m_PropDescs[nIndex].m_lCurrent; } dwCaptureTimeout += (dwBurstNumber - 1) * dwBurstInterval; } else if (dwFuncMode == PTP_CAPTUREMODE_TIMELAPSE) { // // Calculate how much time timelapse operation may take ((TimelapseNumber - 1) * TimelapseInterval) // DWORD dwTimelapseNumber = 1; DWORD dwTimelapseInterval = 1000; // assume 1 second per picture, if device does not specify interval nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_TIMELAPSENUMBER); if (nIndex != -1) { dwTimelapseNumber = m_PropDescs[nIndex].m_lCurrent; } nIndex = m_DeviceInfo.m_SupportedProps.Find(PTP_PROPERTYCODE_TIMELAPSEINTERVAL); if (nIndex != -1) { dwTimelapseInterval = m_PropDescs[nIndex].m_lCurrent; } dwCaptureTimeout += (dwTimelapseNumber - 1) * dwTimelapseInterval; } } // // Wait for the TakePicture command to be done, indicated by CaptureComplete or StoreFull event. // wiauDbgTrace("TakePicture", "Calling WaitForSingleObject with %d ms timeout", dwCaptureTimeout); if (WaitForSingleObject(m_TakePictureDoneEvent, dwCaptureTimeout) != WAIT_OBJECT_0) { wiauDbgWarning("TakePicture", "WaitForSingleObject timed out"); return S_FALSE; } // // Process all objects reported during the capture (there may be many if camera supports burst capture) // CPtpMutex cpm(m_hPtpMutex); // Grab mutex until the end of the function int nCapturedObjects = m_CapturedObjects.GetSize(); if (nCapturedObjects > 0) { wiauDbgTrace("TakePicture", "Processing %d objects", nCapturedObjects); // // Add the first object (in case of burst capture it should folder) // hr = AddObject(m_CapturedObjects[0], TRUE); if (FAILED(hr)) { wiauDbgErrorHr(hr, "TakePicture", "AddObject failed"); return hr; } // // The last item added to the m_HandleItem map will be the new object // // In case of burst capture, new images will be stored in a folder (TimeSequence association) // Handle of the folder must come first. Return the corresponding WIA item as a result of TakePicture. // wiauDbgTrace("TakePicture", "new item is 0x%08x", m_HandleItem.GetKeyAt(m_HandleItem.GetSize() - 1)); *ppNewItem = m_HandleItem.GetValueAt(m_HandleItem.GetSize() - 1); // // Add the remaining objects // for (int i = 1; i < nCapturedObjects; i++) { hr = AddObject(m_CapturedObjects[i], TRUE); if (FAILED(hr)) { wiauDbgErrorHr(hr, "TakePicture", "AddObject failed"); return hr; } } } else { // // There are no new objects, storage filled up too quickly // wiauDbgError("TakePicture", "InitiateCapture did not produce any new objects"); return HRESULT_FROM_PTP(PTP_RESPONSECODE_STOREFULL); } return hr; } // // This function add up all the free image space on each storage. // LONG CWiaMiniDriver::GetTotalFreeImageSpace() { DBG_FN("CWiaMiniDriver::GetTotalFreeImageSpace"); int count; LONG imageSpace = 0; for (count = 0; count < m_StorageInfos.GetSize(); count++) { imageSpace += m_StorageInfos[count].m_FreeSpaceInImages; } return imageSpace; } // // This function gets the item context from the given wias context and // optionally return the target IWiaDrvItem. At least one of ppItemContext // and ppDrvItem must be valid. // // Input: // pWiasContext -- wias context obtained from every drvxxxx method // ppItemContext -- optional parameter to receive the item context // ppDrvItem -- optional parameter to receive the IWiaDrvItem // HRESULT CWiaMiniDriver::WiasContextToItemContext( BYTE *pWiasContext, DRVITEM_CONTEXT **ppItemContext, IWiaDrvItem **ppDrvItem ) { DBG_FN("CWiaMiniDriver::WiasContextToItemContext"); HRESULT hr = S_OK; IWiaDrvItem *pWiaDrvItem; if (!pWiasContext || (!ppItemContext && !ppDrvItem)) { wiauDbgError("WiasContextToItemContext", "invalid arg"); return E_INVALIDARG; } if (ppDrvItem) *ppDrvItem = NULL; hr = wiasGetDrvItem(pWiasContext, &pWiaDrvItem); if (FAILED(hr)) { wiauDbgErrorHr(hr, "WiasContextToItemContext", "wiasGetDrvItem failed"); return hr; } if (ppDrvItem) *ppDrvItem = pWiaDrvItem; if (ppItemContext) { *ppItemContext = NULL; hr = pWiaDrvItem->GetDeviceSpecContext((BYTE **)ppItemContext); if (FAILED(hr)) { wiauDbgError("WiasContextToItemContext", "GetDeviceSpecContext failed"); return hr; } } return hr; } // // This function loads all the object name strings // HRESULT CWiaMiniDriver::LoadStrings() { HRESULT hr = S_OK; if (UnknownString[0] != L'\0') { // // The strings are already loaded // return hr; } hr = GetResourceString(IDS_UNKNOWNSTRING, UnknownString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_FOLDERSTRING, FolderString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_SCRIPTSTRING, ScriptString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_EXECSTRING, ExecString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_TEXTSTRING, TextString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_HTMLSTRING, HtmlString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_DPOFSTRING, DpofString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_AUDIOSTRING, AudioString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_VIDEOSTRING, VideoString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_UNKNOWNIMGSTRING, UnknownImgString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_IMAGESTRING, ImageString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_ALBUMSTRING, AlbumString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_BURSTSTRING, BurstString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_PANORAMASTRING, PanoramaString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_DEVICECONNECTED, DeviceConnectedString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_DEVICEDISCONNECTED, DeviceDisconnectedString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_ITEMCREATED, ItemCreatedString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_ITEMDELETED, ItemDeletedString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_TAKEPICTURE, TakePictureString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_SYNCHRONIZE, SynchronizeString, MAX_PATH); if (FAILED(hr)) return hr; hr = GetResourceString(IDS_TREEUPDATED, TreeUpdatedString, MAX_PATH); if (FAILED(hr)) return hr; // // Concatenate %ld on the end of each object name string so they can be used in a sprintf statement // hr = StringCchCatW(UnknownString, ARRAYSIZE(UnknownString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(UnknownString, ARRAYSIZE(UnknownString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(FolderString, ARRAYSIZE(FolderString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(ScriptString, ARRAYSIZE(ScriptString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(ExecString, ARRAYSIZE(ExecString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(TextString, ARRAYSIZE(TextString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(HtmlString, ARRAYSIZE(HtmlString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(DpofString, ARRAYSIZE(DpofString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(AudioString, ARRAYSIZE(AudioString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(VideoString, ARRAYSIZE(VideoString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(UnknownImgString, ARRAYSIZE(UnknownImgString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(ImageString, ARRAYSIZE(ImageString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(AlbumString, ARRAYSIZE(AlbumString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(BurstString, ARRAYSIZE(BurstString), L"%ld"); if (SUCCEEDED(hr)) hr = StringCchCatW(PanoramaString, ARRAYSIZE(PanoramaString), L"%ld"); return hr; } // // This function retrieves a string from the resource file and returns a Unicode string. The caller // is responsible for allocating space for the string before calling this function. // // Input: // lResourceID -- resource id of the string // pString -- pointer to receive the string // length -- length of the string in characters // HRESULT CWiaMiniDriver::GetResourceString( LONG lResourceID, WCHAR *pString, int length ) { HRESULT hr = S_OK; #ifdef UNICODE if (::LoadString(g_hInst, lResourceID, pString, length) == 0) { hr = HRESULT_FROM_WIN32(::GetLastError()); wiauDbgErrorHr(hr, "GetResourceString", "LoadString failed"); return hr; } #else TCHAR szStringValue[255]; if (::LoadString(g_hInst,lResourceID,szStringValue,255) == 0) { hr = HRESULT_FROM_WIN32(::GetLastError()); wiauDbgErrorHr(hr, "GetResourceString", "LoadString failed"); return hr; } // // convert szStringValue from char* to unsigned short* (ANSI only) // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szStringValue, lstrlenA(szStringValue)+1, pString, sizeof(length)); #endif return hr; } // // To support vendor extension, new registry entries are defined under the // DeviceData subkey. These entries are created from the vendor INF // during device setup. Sample INF entries: // // [DeviceData] // VendorExtID=0x12345678 // PropCode="0xD001,0xD002,0xD003" // PropCodeD001="0x1C01,Vendor property 1" // PropCodeD002="0x1C02,Vendor property 2" // PropCodeD003="0x1C03,Vendor property 3" // EventCode="0xC001,0xC002" // EventCodeC001={191D9AE7-EE8C-443c-B3E8-A3F87E0CF3CC} // EventCodeC002={8162F5ED-62B7-42c5-9C2B-B1625AC0DB93} // // The VendorExtID entry should be the PIMA assigned vendor extension code. // // The PropCode entry must list all of the vendor extended PropCodes. // For each value in PropCode, an entry of the form PropCodeXXXX must be // present, where XXXX is the hex value of the prop code (uppercase). The // value for that entry is the WIA property ID and description (which does not // need to be localized). // // The EventCode entry work similarly, where each EventCodeXXXX entry lists the event // GUID that will be posted when the event occurs. // const TCHAR REGSTR_DEVICEDATA[] = TEXT("DeviceData"); const TCHAR REGSTR_VENDORID[] = TEXT("VendorExtID"); const TCHAR REGSTR_TWODIGITSMILLISECONDS[] = TEXT("TwoDigitsMillisecondsOutput"); const TCHAR REGSTR_PROPCODE[] = TEXT("PropCode"); const TCHAR REGSTR_PROPCODE_MASK[] = TEXT("PropCode%04X"); const TCHAR REGSTR_EVENTCODE[] = TEXT("EventCode"); const TCHAR REGSTR_EVENTCODE_MASK[] = TEXT("EventCode%04X"); const TCHAR REGSTR_EVENTS_MASK[] = TEXT("Events\\%s"); // // This function initializes vendor extentions from the provided // registry key // // Input: // hkDevParams -- the registry key under which the vendor extentions are defined. // HRESULT CWiaMiniDriver::InitVendorExtentions(HKEY hkDevParams) { USES_CONVERSION; DBG_FN("CWiaMiniDriver::InitVendorExtentions"); HRESULT hr = S_OK; if (!hkDevParams) { wiauDbgError("InitVendorExtentions", "invalid arg"); return E_INVALIDARG; } CPTPRegistry regDevData; hr = regDevData.Open(hkDevParams, REGSTR_DEVICEDATA); if (FAILED(hr)) { wiauDbgError("InitVendorExtentions", "Open DeviceData failed"); return hr; } // // Check if this device requires two digits for milliseconds in DATETIME string // DWORD dwTwoDigitsMs = 0; hr = regDevData.GetValueDword(REGSTR_TWODIGITSMILLISECONDS, &dwTwoDigitsMs); if (SUCCEEDED(hr) && dwTwoDigitsMs) { wiauDbgTrace("InitVendorExtensions", "This device requires two digits for milliseconds in DATETIME string"); m_bTwoDigitsMillisecondsOutput = TRUE; } // // Get the vendor extension ID // hr = regDevData.GetValueDword(REGSTR_VENDORID, &m_VendorExtId); if (FAILED(hr)) wiauDbgWarning("InitVendorExtentions", "couldn't read vendor extension id"); wiauDbgTrace("InitVendorExtentions", "vendor extension id = 0x%08x", m_VendorExtId); // // Get the list of vendor extended property codes // CArray16 VendorPropCodes; hr = regDevData.GetValueCodes(REGSTR_PROPCODE, &VendorPropCodes); wiauDbgTrace("InitVendorExtentions", "%d vendor prop codes found", VendorPropCodes.GetSize()); // // For each property code, get it's information, i.e. the WIA prop id and string // int count = 0; TCHAR name[MAX_PATH]; TCHAR nameFormat[MAX_PATH]; TCHAR value[MAX_PATH]; DWORD valueLen = MAX_PATH; PROP_INFO *pPropInfo = NULL; WCHAR *pPropStr = NULL; const cchPropStrBuf = MAX_PATH; #ifndef UNICODE TCHAR PropStrBuf[cchPropStrBuf]; #else #define PropStrBuf pPropStr #endif int num; if (SUCCEEDED(hr)) { // // Read vendor property info. // sample key = "PropCodeD001", sample value = "0x00009802,Vendor property 1" // for (count = 0; count < VendorPropCodes.GetSize(); count++) { hr = StringCchPrintf(name, ARRAYSIZE(name), REGSTR_PROPCODE_MASK, VendorPropCodes[count]); if (FAILED(hr)) { wiauDbgErrorHr(hr, "InitVendorExtensions", "StringCchPrintf failed"); return hr; } valueLen = sizeof(value); hr = regDevData.GetValueStr(name, value, &valueLen); if (FAILED(hr)) { wiauDbgError("InitVendorExtentions", "vendor extended PropCode not found 0x%04x", VendorPropCodes[count]); return hr; } pPropInfo = new PROP_INFO; pPropStr = new WCHAR[cchPropStrBuf]; if (!pPropInfo || !pPropStr) { wiauDbgError("InitVendorExtentions", "memory allocation failed"); return E_OUTOFMEMORY; } pPropInfo->PropName = pPropStr; *PropStrBuf = TEXT('\0'); // // Parse property info // hr = E_FAIL; // assume string is bad TCHAR *pTemp = NULL; pPropInfo->PropId = _tcstoul(value, &pTemp, 0); // determine number base automatically if (pPropInfo->PropId != 0) { pTemp = _tcschr(value, TEXT(',')); if (pTemp != NULL && *(pTemp + 1) != TEXT('\0')) // empty property name is bad { hr = StringCchCopy(PropStrBuf, cchPropStrBuf, pTemp + 1); } } if (FAILED(hr)) { wiauDbgError("InitVendorExtentions", "invalid vendor property format"); delete pPropInfo; delete [] pPropStr; return hr; } #ifndef UNICODE wcscpy(pPropStr, A2W(PropStrBuf)); #endif m_VendorPropMap.Add(VendorPropCodes[count], pPropInfo); } } else wiauDbgWarning("InitVendorExtentions", "couldn't read vendor prop codes"); // // Get the list of vendor extended event codes // hr = S_OK; CArray16 VendorEventCodes; regDevData.GetValueCodes(REGSTR_EVENTCODE, &VendorEventCodes); wiauDbgTrace("InitVendorExtentions", "%d vendor event codes found", VendorEventCodes.GetSize()); int nVendorEvents = VendorEventCodes.GetSize(); if (nVendorEvents > MAX_VENDOR_EVENTS) { wiauDbgWarning("InitVendorExtensions", "vendor events limit exceeded, ignoring events over limit"); nVendorEvents = MAX_VENDOR_EVENTS; } // // For each event code, get it's information, i.e. the WIA event GUID and event name // for (count = 0; count < nVendorEvents; count++) { hr = StringCchPrintf(name, ARRAYSIZE(name), REGSTR_EVENTCODE_MASK, VendorEventCodes[count]); if (FAILED(hr)) { wiauDbgErrorHr(hr, "InitVendorExtensions", "StringCchPrintf failed"); return hr; } valueLen = sizeof(value); hr = regDevData.GetValueStr(name, value, &valueLen); if (FAILED(hr)) { wiauDbgError("InitVendorExtentions", "vendor extended EventCode not found 0x%04x", VendorEventCodes[count]); return hr; } CVendorEventInfo *pEventInfo = new CVendorEventInfo; if (!pEventInfo) { wiauDbgError("InitVendorExtentions", "memory allocation failed"); return E_OUTOFMEMORY; } pEventInfo->pGuid = new GUID; if (!pEventInfo->pGuid) { wiauDbgError("InitVendorExtentions", "memory allocation failed"); delete pEventInfo; pEventInfo = NULL; return E_OUTOFMEMORY; } hr = CLSIDFromString(T2W(value), pEventInfo->pGuid); if (FAILED(hr)) { wiauDbgError("InitVendorExtentions", "invalid guid format"); delete pEventInfo; pEventInfo = NULL; return hr; } // // Open DevParams\Events\EventCodeXXXX key and read event's name - default value of the key // TCHAR szEventKey[MAX_PATH] = TEXT(""); CPTPRegistry regEventKey; hr = StringCchPrintf(szEventKey, ARRAYSIZE(szEventKey), REGSTR_EVENTS_MASK, name); if (SUCCEEDED(hr)) { hr = regEventKey.Open(hkDevParams, szEventKey); if (SUCCEEDED(hr)) { valueLen = sizeof(value); hr = regEventKey.GetValueStr(_T(""), value, &valueLen); if (SUCCEEDED(hr)) { pEventInfo->EventName = SysAllocString(T2W(value)); if (pEventInfo->EventName == NULL) { hr = E_OUTOFMEMORY; } } } } if (FAILED(hr)) { // // if event name is not provided, the event info will not be added to the map // just proceed to the next event in VendorEventCodes // wiauDbgError("InitVendorExtensions", "can't read vendor event name"); delete pEventInfo; pEventInfo = NULL; hr = S_OK; } else { // // Add the EventInfo to the map. Map will be responsible for freeing EventInfo // m_VendorEventMap.Add(VendorEventCodes[count], pEventInfo); } } return hr; } // // Event callback function // HRESULT EventCallback( LPVOID pCallbackParam, PPTP_EVENT pEvent ) { HRESULT hr = S_OK; if (pEvent == NULL) { hr = CoInitialize(NULL); wiauDbgTrace("EventCallback", "CoInitialize called"); } else { DBG_FN("EventCallback"); CWiaMiniDriver *pDriver = (CWiaMiniDriver *) pCallbackParam; if (pDriver) { hr = pDriver->EventCallbackDispatch(pEvent); if (FAILED(hr)) { wiauDbgError("EventCallback", "ProcessEvent failed"); return hr; } } } return hr; } // // Constructor // CPtpMutex::CPtpMutex(HANDLE hMutex) : m_hMutex(hMutex) { DWORD ret = 0; const DWORD MUTEX_WAIT = 30 * 1000; // 30 seconds ret = WaitForSingleObject(hMutex, MUTEX_WAIT); if (ret == WAIT_TIMEOUT) wiauDbgError("CPtpMutex", "wait for mutex expired"); else if (ret == WAIT_FAILED) wiauDbgError("CPtpMutex", "WaitForSingleObject failed"); wiauDbgTrace("CPtpMutex", "Entering mutex"); } // // Destructor // CPtpMutex::~CPtpMutex() { wiauDbgTrace("~CPtpMutex", "Leaving mutex"); if (!ReleaseMutex(m_hMutex)) wiauDbgError("~CPtpMutex", "ReleaseMutex failed"); } // // Notify WIA server on changes in camera's state, like "camera was reset" // HRESULT CWiaMiniDriver::NotifyWiaOnStateChanges() { HRESULT hr = S_OK; if (m_pPTPCamera == NULL) { return E_UNEXPECTED; } // // Check if camera was reset // PBOOL pbWasReset = m_pPTPCamera->CameraWasReset(); if (*pbWasReset) { // // Since device was reset, its context is invalid now // First, remove all objects // while (m_StorageIds.GetSize() > 0) { hr = RemoveStorage(m_StorageIds[0]); if (FAILED(hr)) { wiauDbgError("NotifyWiaOnStateChanges", "Failed to remove storage"); } } // // Unlink tree. // if (m_pDrvItemRoot) { hr = m_pDrvItemRoot->UnlinkItemTree(WiaItemTypeDisconnected); if (FAILED(hr)) { wiauDbgError("NotifyWiaOnStateChanges", "Failed to unlink tree"); } m_pDrvItemRoot->Release(); m_pDrvItemRoot = NULL; } // // Invalidate all property values // m_PropDescs.RemoveAll(); // // Next call to drvInitializeWia should be able to re-initialize camera // m_OpenApps = 0; // // Notify WIA service and application that camera needs to be reinitialized // hr = wiasQueueEvent(m_bstrDeviceId, &WIA_EVENT_TREE_UPDATED, NULL); if (FAILED(hr)) { wiauDbgError("NotifyWiaOnStateChanges", "Failed to queue WIA_EVENT_TREE_UPDATED event"); } *pbWasReset = FALSE; } return hr; }