You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2994 lines
85 KiB
2994 lines
85 KiB
/*++
|
|
|
|
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 <atlbase.h>
|
|
#include <atlconv.h>
|
|
#include <wiatempl.h>
|
|
#include <stiregi.h>
|
|
#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<WORD, PROP_INFO *> 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<WORD, CVendorEventInfo*> 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<INonDelegatingUnknown *>(this);
|
|
else if (IsEqualIID(riid, IID_IStiUSD))
|
|
*ppv = static_cast<IStiUSD *>(this);
|
|
else if (IsEqualIID(riid, IID_IWiaMiniDrv))
|
|
*ppv = static_cast<IWiaMiniDrv *>(this);
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
//
|
|
// Do not call NonDelegatingAddRef() ....
|
|
//
|
|
(reinterpret_cast<IUnknown *>(*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<ULONG, IWiaDrvItem *> 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<ULONG, IWiaDrvItem *> 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;
|
|
}
|
|
|