3074 lines
88 KiB
C++
3074 lines
88 KiB
C++
/*++
|
|
|
|
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 <wiaconv.h>
|
|
#include <wiatempl.h>
|
|
#include <stiregi.h>
|
|
#include "utils.h"
|
|
|
|
//
|
|
// Locations for holding resource strings
|
|
//
|
|
extern WCHAR UnknownString[];
|
|
extern WCHAR FolderString[];
|
|
extern WCHAR ScriptString[];
|
|
extern WCHAR ExecString[];
|
|
extern WCHAR TextString[];
|
|
extern WCHAR HtmlString[];
|
|
extern WCHAR DpofString[];
|
|
extern WCHAR AudioString[];
|
|
extern WCHAR VideoString[];
|
|
extern WCHAR UnknownImgString[];
|
|
extern WCHAR ImageString[];
|
|
extern WCHAR AlbumString[];
|
|
extern WCHAR BurstString[];
|
|
extern WCHAR PanoramaString[];
|
|
|
|
|
|
//
|
|
// 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 VendorEventIconString[MAX_PATH] = WIA_ICON_DEVICE_CONNECTED;
|
|
|
|
const BYTE NUMEVENTCAPS = 4;
|
|
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
|
|
}
|
|
};
|
|
|
|
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_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();
|
|
|
|
m_VendorPropMap.RemoveAll();
|
|
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);
|
|
}
|
|
|
|
|
|
//
|
|
// GetDeviceName
|
|
//
|
|
// This function gets the name of the device from the
|
|
// camera without a session. We need this because the
|
|
// Initialize function needs to fix up registry
|
|
// settings set up by the class installer so that the
|
|
// model name is used instead of the generic
|
|
// "Digital Still Camera" name.
|
|
//
|
|
|
|
HRESULT CWiaMiniDriver::GetDeviceName(const WCHAR *pwszPortName,
|
|
WCHAR *pwszManufacturer,
|
|
DWORD cchManufacturer,
|
|
WCHAR *pwszModelName,
|
|
DWORD cchModelName)
|
|
{
|
|
DBG_FN("CWiaMiniDriver::GetDeviceName");
|
|
|
|
HRESULT hr = S_OK;
|
|
CPTPCamera *pPTPCamera = NULL;
|
|
CPtpDeviceInfo DeviceInfo;
|
|
|
|
//
|
|
// If we did receive a valid port name, abort.
|
|
//
|
|
if ((pwszPortName == NULL) || (pwszPortName[0] == 0))
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Create a new camera object.
|
|
//
|
|
if (hr == S_OK)
|
|
{
|
|
pPTPCamera = new CUsbCamera;
|
|
|
|
if (pPTPCamera == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
wiauDbgError("", "CWiaMiniDriver::GetDeviceName, failed to create "
|
|
"new camera object, hr = 0x%08lx", hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open a connection to the camera
|
|
//
|
|
if (hr == S_OK)
|
|
{
|
|
if (!pPTPCamera->IsCameraOpen())
|
|
{
|
|
hr = pPTPCamera->Open((LPWSTR) pwszPortName, NULL, NULL, NULL, FALSE);
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
wiauDbgError("", "CWiaMiniDriver::GetDeviceName, failed to open connection "
|
|
"to camera, hr = 0x%08lx", hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query the camera for its DeviceInfo
|
|
//
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPTPCamera->GetDeviceInfo(&DeviceInfo);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
wiauDbgError("", "CWiaMiniDriver::GetDeviceName, GetDeviceInfo failed, "
|
|
"hr = 0x%08lx", hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the returned Manufacturer and/or ModelName into the OUT params.
|
|
//
|
|
if (hr == S_OK)
|
|
{
|
|
//
|
|
// If user is requesting manufacturer name, and camera returned a value
|
|
// for it, copy the name into OUT param.
|
|
//
|
|
if ((pwszManufacturer != NULL) &&
|
|
(cchManufacturer > 0) &&
|
|
(DeviceInfo.m_cbstrManufacturer.String() != NULL))
|
|
{
|
|
wcsncpy(pwszManufacturer,
|
|
DeviceInfo.m_cbstrManufacturer.String(),
|
|
cchManufacturer);
|
|
}
|
|
|
|
//
|
|
// If user is requesting Model name, and camera returned a value
|
|
// for it, copy the name into OUT param.
|
|
//
|
|
if ((pwszModelName != NULL) &&
|
|
(cchModelName > 0) &&
|
|
(DeviceInfo.m_cbstrModel.String() != NULL))
|
|
{
|
|
wcsncpy(pwszModelName,
|
|
DeviceInfo.m_cbstrModel.String(),
|
|
cchModelName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the connection to the camera and delete the camera object.
|
|
//
|
|
if (pPTPCamera)
|
|
{
|
|
pPTPCamera->Close();
|
|
delete pPTPCamera;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
///////////////////////////////////////
|
|
// Begin Workaround
|
|
//
|
|
// This workaround queries the camera
|
|
// for its name, then resets the FriendlyName
|
|
// and DeviceDesc registry values to reflect
|
|
// this new name. We do this because PTP
|
|
// cameras can have a generic class ID, in
|
|
// which case the device name from the INF
|
|
// will be "Digital Still Camera".
|
|
//
|
|
// This is what we do:
|
|
// - Get Friendly Name and Driver Desc from Registry
|
|
// - If their values are "Digital Still Camera", then attempt to update.
|
|
// - Query device for its model name
|
|
// - Extract suffix from FriendlyName that class installer added to device name
|
|
// - Set FriendlyName to the retrieved ModelName - after appending suffix to it.
|
|
// - Set DriverDesc to retrieved ModelName.
|
|
//
|
|
|
|
HRESULT hrNameChange = S_OK;
|
|
BOOL bUpdateDeviceName = FALSE;
|
|
HKEY hDeviceData = NULL;
|
|
LRESULT lResult = ERROR_SUCCESS;
|
|
DWORD dwType = 0;
|
|
DWORD dwSize = 0;
|
|
WCHAR wszPortName[MAX_PATH] = {0};
|
|
WCHAR wszManufacturer[255 + 1] = {0}; // As per PTP spec, max string length is 255
|
|
WCHAR wszModelName[255 + 1] = {0}; // As per PTP spec, max string length is 255
|
|
TCHAR szFriendlyName[511 + 1] = {0}; // This is size of ModelName, plus suffix added by Class Installer
|
|
TCHAR szDriverDesc[255 + 1] = {0}; // This is size of ModelName
|
|
|
|
//
|
|
// Get the "QueryDeviceForName" registry entry.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
lResult = RegOpenKeyEx(hParametersKey,
|
|
W2T(REGSTR_VAL_DATA_W),
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hDeviceData);
|
|
|
|
//
|
|
// if we failed to open the DeviceData key, assume it doesn't exist
|
|
// and therefore we should not query the device for its name.
|
|
//
|
|
|
|
if (lResult == ERROR_SUCCESS)
|
|
{
|
|
dwType = REG_DWORD;
|
|
dwSize = sizeof(bUpdateDeviceName);
|
|
lResult = RegQueryValueEx(hDeviceData,
|
|
REGSTR_VAL_QUERYDEVICEFORNAME,
|
|
NULL,
|
|
&dwType,
|
|
(BYTE*) &bUpdateDeviceName,
|
|
&dwSize);
|
|
}
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
bUpdateDeviceName = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bUpdateDeviceName)
|
|
{
|
|
//
|
|
// Get the friendly name from the registry.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
dwType = REG_SZ;
|
|
dwSize = sizeof(szFriendlyName);
|
|
lResult = RegQueryValueEx(hParametersKey,
|
|
W2T(REGSTR_VAL_FRIENDLY_NAME_W),
|
|
NULL,
|
|
&dwType,
|
|
(BYTE*) szFriendlyName,
|
|
&dwSize);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// for some reason we couldn't get the value of the FriendlyName. Abort in this
|
|
// case since there is no reason for this value to not be present and
|
|
// accessible.
|
|
//
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, the RegQueryValueEx attempt for "
|
|
"the 'FriendlyName' failed, this should never happen. Aborting "
|
|
"attempt to update PTP device name, lResult = %lu", lResult);
|
|
|
|
hrNameChange = E_FAIL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the DriverDesc from the registry.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
dwType = REG_SZ;
|
|
dwSize = sizeof(szDriverDesc);
|
|
lResult = RegQueryValueEx(hParametersKey,
|
|
W2T(REGSTR_VAL_DRIVER_DESC_W),
|
|
NULL,
|
|
&dwType,
|
|
(BYTE*) szDriverDesc,
|
|
&dwSize);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// for some reason we couldn't get the value of the DriverDesc. Abort in this
|
|
// case since there is no reason for this value to not be present and
|
|
// accessible.
|
|
//
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, the RegQueryValueEx attempt for "
|
|
"the 'DriverDesc' failed, this should never happen. Aborting "
|
|
"attempt to update PTP device name, lResult = %lu", lResult);
|
|
|
|
hrNameChange = E_FAIL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the port to speak to the device with.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
hrNameChange = m_pDcb->GetMyDevicePortName(wszPortName, sizeof(wszPortName));
|
|
}
|
|
|
|
//
|
|
// Query the device name for its model and manufacturer name.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
hrNameChange = GetDeviceName(wszPortName,
|
|
wszManufacturer,
|
|
sizeof(wszManufacturer) / sizeof(WCHAR),
|
|
wszModelName,
|
|
sizeof(wszModelName) / sizeof(WCHAR));
|
|
|
|
//
|
|
// If we successfully queried the device, also verify that the model name
|
|
// is not empty. As per the PTP spec, the "Model" variable in the device
|
|
// info is OPTIONAL.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
wiauDbgTrace("", "CWiaMiniDriver::Initialize, retrieved device "
|
|
"info: Name='%ls' Manufacturer='%ls'",
|
|
wszModelName, wszManufacturer);
|
|
|
|
if (wszModelName[0] == 0)
|
|
{
|
|
wiauDbgWarning("","CWiaMiniDriver::Initialize, successfully queried "
|
|
"the device for its device name, but the device returned "
|
|
"an empty model name. Camera name will NOT be modified");
|
|
|
|
hrNameChange = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, could not get the name "
|
|
"of the device from the PTP device. hr = 0x%08lx", hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the FriendlyName registry setting
|
|
//
|
|
// Get the suffix appended to the friendly name. This suffix is
|
|
// will be preceeded with a '#'.
|
|
//
|
|
// NOTE: This code is dependant on the Class Installer appending
|
|
// a '#' as a suffix to the PTP friendly name. If this
|
|
// ever changes, this will have to change too.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
TCHAR szTemp[63 + 1] = {0};
|
|
TCHAR *pszSuffix = NULL;
|
|
|
|
//
|
|
// Look for the suffix on the friendly name
|
|
//
|
|
pszSuffix = _tcsrchr(szFriendlyName, '#');
|
|
|
|
//
|
|
// If we found the suffix store the suffix in a temp location.
|
|
//
|
|
if (pszSuffix)
|
|
{
|
|
_tcsncpy(szTemp, pszSuffix, sizeof(szTemp) / sizeof(TCHAR));
|
|
}
|
|
|
|
//
|
|
// Build the new friendly name based on the device's model name.
|
|
//
|
|
ZeroMemory(szFriendlyName, sizeof(szFriendlyName));
|
|
|
|
if (pszSuffix)
|
|
{
|
|
_sntprintf(szFriendlyName,
|
|
sizeof(szFriendlyName) / sizeof(TCHAR),
|
|
TEXT("%ls %s"),
|
|
wszModelName,
|
|
szTemp);
|
|
}
|
|
else
|
|
{
|
|
_sntprintf(szFriendlyName,
|
|
sizeof(szFriendlyName) / sizeof(TCHAR),
|
|
TEXT("%ls"),
|
|
wszModelName);
|
|
}
|
|
|
|
//
|
|
// Write the updated friendly name value into the registry.
|
|
//
|
|
lResult = RegSetValueEx(hParametersKey,
|
|
W2T(REGSTR_VAL_FRIENDLY_NAME_W),
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*) szFriendlyName,
|
|
(_tcslen(szFriendlyName) * sizeof(TCHAR)) + sizeof(TCHAR));
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hrNameChange = E_FAIL;
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, failed to write the "
|
|
"FriendlyName value '%ls' into the registry",
|
|
szFriendlyName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the device's DriverDesc registry value.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
lResult = RegSetValueEx(hParametersKey,
|
|
W2T(REGSTR_VAL_DRIVER_DESC_W),
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*) W2T(wszModelName),
|
|
(wcslen(wszModelName) * sizeof(WCHAR)) + sizeof(TCHAR));
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hrNameChange = E_FAIL;
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, failed to write the "
|
|
"DriverDesc value '%ls' into the registry",
|
|
wszModelName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the QueryDeviceForName value so we don't have to do this
|
|
// next time this function is called.
|
|
//
|
|
if (hrNameChange == S_OK)
|
|
{
|
|
DWORD dwQueryDeviceForName = 0;
|
|
|
|
dwType = REG_DWORD;
|
|
dwSize = sizeof(dwQueryDeviceForName);
|
|
lResult = RegSetValueEx(hDeviceData,
|
|
REGSTR_VAL_QUERYDEVICEFORNAME,
|
|
0,
|
|
REG_DWORD,
|
|
(BYTE*) &dwQueryDeviceForName,
|
|
dwSize);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
hrNameChange = E_FAIL;
|
|
wiauDbgError("", "CWiaMiniDriver::Initialize, failed to update the "
|
|
"'QueryDeviceForName' value into the registry");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the HKEY if it was opened.
|
|
//
|
|
if (hDeviceData)
|
|
{
|
|
RegCloseKey(hDeviceData);
|
|
hDeviceData = NULL;
|
|
}
|
|
|
|
//
|
|
// End Workaround
|
|
///////////////////////////////////////
|
|
///////////////////////////////////////
|
|
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// TakePicture only works on the root
|
|
//
|
|
|
|
if (WiaItemTypeRoot & ItemType)
|
|
{
|
|
hr = WriteDeviceProperties(pWiasContext);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvDeviceCommand", "WriteDeviceProperties failed");
|
|
return hr;
|
|
}
|
|
|
|
hr = TakePicture(pWiasContext, ppDrvItem);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvDeviceCommand", "TakePicture failed");
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Delete the object on the camera
|
|
//
|
|
hr = m_pPTPCamera->DeleteObject(pItemCtx->pObjectInfo->m_ObjectHandle, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvDeleteItem", "DeleteObject failed");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// 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");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
if (ItemType & WiaItemTypeRoot)
|
|
{
|
|
hr = InitDeviceProperties(pWiasContext);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvInitItemProperties", "InitDeviceProperties failed");
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = InitItemProperties(pWiasContext);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvInitItemProperties", "InitItemProperties failed");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
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 (m_pStiDevice->LockDevice(INFINITE));
|
|
}
|
|
|
|
//
|
|
// 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 (m_pStiDevice->UnLockDevice());
|
|
}
|
|
|
|
//
|
|
// 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");
|
|
return hr;
|
|
}
|
|
|
|
if (!pItemCtx)
|
|
{
|
|
wiauDbgError("drvGetWiaFormatInfo", "item context is null");
|
|
return E_FAIL;
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
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");
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// 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");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
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");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
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;
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
if (WiaItemTypeRoot & ItemType)
|
|
hr = ReadDeviceProperties(pWiasContext, NumPropSpecs, pPropSpecs);
|
|
else
|
|
hr = ReadItemProperties(pWiasContext, NumPropSpecs, pPropSpecs);
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Only properties to write are on the root
|
|
//
|
|
|
|
if (WiaItemTypeRoot & ItemType)
|
|
{
|
|
hr = WriteDeviceProperties(pWiasContext);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvWriteItemProperties", "WriteDeviceProperties failed");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
if (WiaItemTypeRoot & ItemType)
|
|
{
|
|
hr = ValidateDeviceProperties(pWiasContext, NumPropSpecs, pPropSpecs);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvValidateItemProperties", "ValidateDeviceProperties failed");
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = ValidateItemProperties(pWiasContext, NumPropSpecs, pPropSpecs, ItemType);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvValidateItemProperties", "ValidateItemProperties failed");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
|
|
DRVITEM_CONTEXT *pItemCtx;
|
|
hr = WiasContextToItemContext(pWiasContext, &pItemCtx);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("AcquireData", "WiasContextToItemContext failed");
|
|
return hr;
|
|
}
|
|
|
|
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");
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = AcquireData(pItemCtx, pmdtc);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("drvAcquireItemData", "AcquireData failed");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
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 = NULL;
|
|
}
|
|
|
|
if (m_pPTPCamera)
|
|
{
|
|
delete m_pPTPCamera;
|
|
m_pPTPCamera = NULL;
|
|
}
|
|
|
|
m_StorageIds.RemoveAll();
|
|
m_StorageInfos.RemoveAll();
|
|
m_PropDescs.RemoveAll();
|
|
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();
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Start the image capture
|
|
//
|
|
hr = m_pPTPCamera->InitiateCapture(PTP_STORAGEID_DEFAULT, FormatCode);
|
|
if (FAILED(hr))
|
|
{
|
|
wiauDbgError("TakePicture", "InitiateCapture failed");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait for the TakePicture command to be done, indicated by CaptureComplete or StoreFull event.
|
|
// Time out after 30 seconds.
|
|
//
|
|
|
|
if (WaitForSingleObject(m_TakePictureDoneEvent, 30 * 1000) != WAIT_OBJECT_0)
|
|
{
|
|
wiauDbgWarning("TakePicture", "WaitForSingleObject timed out");
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// The last item added to the m_HandleItem map will be the new object
|
|
//
|
|
wiauDbgTrace("TakePicture", "new picture is 0x%08x", m_HandleItem.GetKeyAt(m_HandleItem.GetSize() - 1));
|
|
|
|
*ppNewItem = m_HandleItem.GetValueAt(m_HandleItem.GetSize() - 1);
|
|
|
|
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;
|
|
|
|
//
|
|
// Concatenate %ld on the end of each object name string so they can be used in a sprintf statement
|
|
//
|
|
wcscat(UnknownString, L"%ld");
|
|
wcscat(UnknownString, L"%ld");
|
|
wcscat(FolderString, L"%ld");
|
|
wcscat(ScriptString, L"%ld");
|
|
wcscat(ExecString, L"%ld");
|
|
wcscat(TextString, L"%ld");
|
|
wcscat(HtmlString, L"%ld");
|
|
wcscat(DpofString, L"%ld");
|
|
wcscat(AudioString, L"%ld");
|
|
wcscat(VideoString, L"%ld");
|
|
wcscat(UnknownImgString, L"%ld");
|
|
wcscat(ImageString, L"%ld");
|
|
wcscat(AlbumString, L"%ld");
|
|
wcscat(BurstString, L"%ld");
|
|
wcscat(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_PROPCODE[] = TEXT("PropCode");
|
|
const TCHAR REGSTR_EVENTCODE[] = TEXT("EventCode");
|
|
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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
#ifndef UNICODE
|
|
TCHAR PropStrBuf[MAX_PATH];
|
|
#else
|
|
#define PropStrBuf pPropStr
|
|
#endif
|
|
|
|
int num;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
lstrcpy(nameFormat, REGSTR_PROPCODE);
|
|
lstrcat(nameFormat, TEXT("%04X"));
|
|
for (count = 0; count < VendorPropCodes.GetSize(); count++)
|
|
{
|
|
wsprintf(name, nameFormat, VendorPropCodes[count]);
|
|
valueLen = MAX_PATH;
|
|
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[MAX_PATH];
|
|
if (!pPropInfo || !pPropStr)
|
|
{
|
|
wiauDbgError("InitVendorExtentions", "memory allocation failed");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pPropInfo->PropName = pPropStr;
|
|
*PropStrBuf = TEXT('\0');
|
|
num = _stscanf(value, TEXT("%li,%s"), &pPropInfo->PropId, PropStrBuf);
|
|
|
|
#ifndef UNICODE
|
|
wcscpy(pPropStr, A2W(PropStrBuf));
|
|
#endif
|
|
|
|
if (num != 2)
|
|
{
|
|
wiauDbgError("InitVendorExtentions", "invalid vendor property format");
|
|
delete pPropInfo;
|
|
delete [] pPropStr;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
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
|
|
//
|
|
lstrcpy(nameFormat, REGSTR_EVENTCODE);
|
|
lstrcat(nameFormat, TEXT("%04X"));
|
|
for (count = 0; count < nVendorEvents; count++)
|
|
{
|
|
wsprintf(name, nameFormat, VendorEventCodes[count]);
|
|
valueLen = MAX_PATH;
|
|
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 + 1] = TEXT("");
|
|
CPTPRegistry regEventKey;
|
|
|
|
if (_sntprintf(szEventKey, MAX_PATH, REGSTR_EVENTS_MASK, name) > 0)
|
|
{
|
|
hr = regEventKey.Open(hkDevParams, szEventKey);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
valueLen = MAX_PATH;
|
|
hr = regEventKey.GetValueStr(_T(""), value, &valueLen);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pEventInfo->EventName = SysAllocString(T2W(value));
|
|
if (pEventInfo->EventName == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL; // way too long registry key. should not happen
|
|
}
|
|
|
|
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");
|
|
}
|