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.
1712 lines
46 KiB
1712 lines
46 KiB
/**************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 2000
|
|
*
|
|
* TITLE: fakecam.cpp
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* DATE: 18 July, 2000
|
|
*
|
|
* DESCRIPTION:
|
|
* Fake Camera device implementation
|
|
*
|
|
* TODO: Every function in this file must be changed so that it actually
|
|
* talks to a real camera.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
HINSTANCE g_hInst;
|
|
GUID g_guidUnknownFormat;
|
|
|
|
//
|
|
// Initializes access to the camera and allocates the device info
|
|
// structure and private storage area
|
|
//
|
|
HRESULT WiaMCamInit(MCAM_DEVICE_INFO **ppDeviceInfo)
|
|
{
|
|
wiauDbgInit(g_hInst);
|
|
|
|
DBG_FN("WiaMCamInit");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
MCAM_DEVICE_INFO *pDeviceInfo = NULL;
|
|
FAKECAM_DEVICE_INFO *pPrivateDeviceInfo = NULL;
|
|
|
|
REQUIRE_ARGS(!ppDeviceInfo, hr, "WiaMCamInit");
|
|
*ppDeviceInfo = NULL;
|
|
|
|
//
|
|
// Allocate the MCAM_DEVICE_INFO structure
|
|
//
|
|
pDeviceInfo = new MCAM_DEVICE_INFO;
|
|
REQUIRE_ALLOC(pDeviceInfo, hr, "WiaMCamInit");
|
|
|
|
memset(pDeviceInfo, 0, sizeof(MCAM_DEVICE_INFO));
|
|
pDeviceInfo->iSize = sizeof(MCAM_DEVICE_INFO);
|
|
pDeviceInfo->iMcamVersion = MCAM_VERSION;
|
|
|
|
//
|
|
// Allocate the FAKECAM_DEVICE_INFO structure that the
|
|
// microdriver uses to store info
|
|
//
|
|
pPrivateDeviceInfo = new FAKECAM_DEVICE_INFO;
|
|
REQUIRE_ALLOC(pPrivateDeviceInfo, hr, "WiaMCamInit");
|
|
|
|
memset(pPrivateDeviceInfo, 0, sizeof(FAKECAM_DEVICE_INFO));
|
|
pDeviceInfo->pPrivateStorage = (BYTE *) pPrivateDeviceInfo;
|
|
|
|
Cleanup:
|
|
if (FAILED(hr)) {
|
|
if (pDeviceInfo) {
|
|
delete pDeviceInfo;
|
|
pDeviceInfo = NULL;
|
|
}
|
|
if (pPrivateDeviceInfo) {
|
|
delete pPrivateDeviceInfo;
|
|
pPrivateDeviceInfo = NULL;
|
|
}
|
|
}
|
|
|
|
*ppDeviceInfo = pDeviceInfo;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Frees any remaining structures held by the microdriver
|
|
//
|
|
HRESULT WiaMCamUnInit(MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("WiaMCamUnInit");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pDeviceInfo)
|
|
{
|
|
//
|
|
// Free anything that was dynamically allocated in the MCAM_DEVICE_INFO
|
|
// structure
|
|
//
|
|
if (pDeviceInfo->pPrivateStorage) {
|
|
delete pDeviceInfo->pPrivateStorage;
|
|
pDeviceInfo->pPrivateStorage = NULL;
|
|
}
|
|
|
|
delete pDeviceInfo;
|
|
pDeviceInfo = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Open a connection to the device
|
|
//
|
|
HRESULT WiaMCamOpen(MCAM_DEVICE_INFO *pDeviceInfo, PWSTR pwszPortName)
|
|
{
|
|
DBG_FN("WiaMCamOpen");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
TCHAR tszTempStr[MAX_PATH] = TEXT("");
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pwszPortName, hr, "WiaMCamOpen");
|
|
|
|
//
|
|
// Convert the wide port string to a tstr
|
|
//
|
|
hr = wiauStrW2T(pwszPortName, tszTempStr, sizeof(tszTempStr));
|
|
REQUIRE_SUCCESS(hr, "WiaMCamOpen", "wiauStrW2T failed");
|
|
|
|
//
|
|
// Open the camera
|
|
//
|
|
hr = FakeCamOpen(tszTempStr, pDeviceInfo);
|
|
REQUIRE_SUCCESS(hr, "WiaMCamOpen", "FakeCamOpen failed");
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Closes the connection with the camera
|
|
//
|
|
HRESULT WiaMCamClose(MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("WiaMCamClose");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo, hr, "WiaMCamClose");
|
|
|
|
//
|
|
// For a real camera, CloseHandle should be called here
|
|
//
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Returns information about the camera, the list of items on the camera,
|
|
// and starts monitoring events from the camera
|
|
//
|
|
HRESULT WiaMCamGetDeviceInfo(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO **ppItemList)
|
|
{
|
|
DBG_FN("WiaMCamGetDeviceInfo");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
FAKECAM_DEVICE_INFO *pPrivateDeviceInfo = NULL;
|
|
PTSTR ptszRootPath = NULL;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !ppItemList || !pDeviceInfo->pPrivateStorage, hr, "WiaMCamGetDeviceInfo");
|
|
*ppItemList = NULL;
|
|
|
|
pPrivateDeviceInfo = (UNALIGNED FAKECAM_DEVICE_INFO *) pDeviceInfo->pPrivateStorage;
|
|
ptszRootPath = pPrivateDeviceInfo->tszRootPath;
|
|
|
|
//
|
|
// Build a list of all items available. The fields in the item info
|
|
// structure can either be filled in here, or for better performance
|
|
// wait until GetItemInfo is called.
|
|
//
|
|
hr = SearchDir(pPrivateDeviceInfo, NULL, ptszRootPath);
|
|
REQUIRE_SUCCESS(hr, "WiaMCamGetDeviceInfo", "SearchDir failed");
|
|
|
|
//
|
|
// Fill in the MCAM_DEVICE_INFO structure
|
|
//
|
|
//
|
|
// Firmware version should be retrieved from the device, converting
|
|
// to WSTR if necessary
|
|
//
|
|
pDeviceInfo->pwszFirmwareVer = L"04.18.65";
|
|
|
|
// ISSUE-8/4/2000-davepar Put properties into an INI file
|
|
|
|
pDeviceInfo->lPicturesTaken = pPrivateDeviceInfo->iNumImages;
|
|
pDeviceInfo->lPicturesRemaining = 100 - pDeviceInfo->lPicturesTaken;
|
|
pDeviceInfo->lTotalItems = pPrivateDeviceInfo->iNumItems;
|
|
|
|
GetLocalTime(&pDeviceInfo->Time);
|
|
|
|
// pDeviceInfo->lExposureMode = EXPOSUREMODE_MANUAL;
|
|
// pDeviceInfo->lExposureComp = 0;
|
|
|
|
*ppItemList = pPrivateDeviceInfo->pFirstItem;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Called to retrieve an event from the device
|
|
//
|
|
HRESULT WiaMCamReadEvent(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_EVENT_INFO **ppEventList)
|
|
{
|
|
DBG_FN("WiaMCamReadEvent");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Called when events are no longer needed
|
|
//
|
|
HRESULT WiaMCamStopEvents(MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("WiaMCamStopEvents");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Fill in the item info structure
|
|
//
|
|
HRESULT WiaMCamGetItemInfo(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItemInfo)
|
|
{
|
|
DBG_FN("WiaMCamGetItemInfo");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// This is where the driver should fill in all the fields in the
|
|
// ITEM_INFO structure. For this fake driver, the fields are filled
|
|
// in by WiaMCamGetDeviceInfo because it's easier.
|
|
//
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Frees the item info structure
|
|
//
|
|
HRESULT WiaMCamFreeItemInfo(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItemInfo)
|
|
{
|
|
DBG_FN("WiaMCamFreeItemInfo");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
FAKECAM_DEVICE_INFO *pPrivateDeviceInfo = NULL;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pItemInfo || !pDeviceInfo->pPrivateStorage, hr, "WiaMCamFreeItemInfo");
|
|
|
|
if (pItemInfo->pPrivateStorage) {
|
|
PTSTR ptszFullName = (PTSTR) pItemInfo->pPrivateStorage;
|
|
|
|
wiauDbgTrace("WiaMCamFreeItemInfo", "Removing %" WIAU_DEBUG_TSTR, ptszFullName);
|
|
|
|
delete []ptszFullName;
|
|
ptszFullName = NULL;
|
|
pItemInfo->pPrivateStorage = NULL;
|
|
}
|
|
|
|
if (pItemInfo->pwszName)
|
|
{
|
|
delete []pItemInfo->pwszName;
|
|
pItemInfo->pwszName = NULL;
|
|
}
|
|
|
|
pPrivateDeviceInfo = (UNALIGNED FAKECAM_DEVICE_INFO *) pDeviceInfo->pPrivateStorage;
|
|
|
|
hr = RemoveItem(pPrivateDeviceInfo, pItemInfo);
|
|
REQUIRE_SUCCESS(hr, "WiaMCamFreeItemInfo", "RemoveItem failed");
|
|
|
|
if (IsImageType(pItemInfo->pguidFormat)) {
|
|
pPrivateDeviceInfo->iNumImages--;
|
|
}
|
|
|
|
pPrivateDeviceInfo->iNumItems--;
|
|
|
|
delete pItemInfo;
|
|
pItemInfo = NULL;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Retrieves the thumbnail for an item
|
|
//
|
|
HRESULT WiaMCamGetThumbnail(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItem,
|
|
int *pThumbSize, BYTE **ppThumb)
|
|
{
|
|
DBG_FN("WiaMCamGetThumbnail");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
PTSTR ptszFullName = NULL;
|
|
BYTE *pBuffer = NULL;
|
|
LONG ThumbOffset = 0;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pItem || !pThumbSize || !ppThumb, hr, "WiaMCamGetThumbnail");
|
|
*ppThumb = NULL;
|
|
*pThumbSize = 0;
|
|
|
|
ptszFullName = (PTSTR) pItem->pPrivateStorage;
|
|
|
|
hr = ReadJpegHdr(ptszFullName, &pBuffer);
|
|
REQUIRE_SUCCESS(hr, "WiaMCamGetThumbnail", "ReadJpegHdr failed");
|
|
|
|
IFD ImageIfd, ThumbIfd;
|
|
BOOL bSwap;
|
|
hr = ReadExifJpeg(pBuffer, &ImageIfd, &ThumbIfd, &bSwap);
|
|
REQUIRE_SUCCESS(hr, "WiaMCamGetThumbnail", "ReadExifJpeg failed");
|
|
|
|
for (int count = 0; count < ThumbIfd.Count; count++)
|
|
{
|
|
if (ThumbIfd.pEntries[count].Tag == TIFF_JPEG_DATA) {
|
|
ThumbOffset = ThumbIfd.pEntries[count].Offset;
|
|
}
|
|
else if (ThumbIfd.pEntries[count].Tag == TIFF_JPEG_LEN) {
|
|
*pThumbSize = ThumbIfd.pEntries[count].Offset;
|
|
}
|
|
}
|
|
|
|
if (!ThumbOffset || !*pThumbSize)
|
|
{
|
|
wiauDbgError("WiaMCamGetThumbnail", "Thumbnail not found");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*ppThumb = new BYTE[*pThumbSize];
|
|
REQUIRE_ALLOC(*ppThumb, hr, "WiaMCamGetThumbnail");
|
|
|
|
memcpy(*ppThumb, pBuffer + APP1_OFFSET + ThumbOffset, *pThumbSize);
|
|
|
|
Cleanup:
|
|
if (pBuffer) {
|
|
delete []pBuffer;
|
|
}
|
|
|
|
FreeIfd(&ImageIfd);
|
|
FreeIfd(&ThumbIfd);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Retrieves the data for an item
|
|
//
|
|
HRESULT WiaMCamGetItemData(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItem,
|
|
UINT uiState, BYTE *pBuf, DWORD dwLength)
|
|
{
|
|
DBG_FN("WiaMCamGetItemData");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
PTSTR ptszFullName = NULL;
|
|
FAKECAM_DEVICE_INFO *pPrivateDeviceInfo = NULL;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pItem || !pDeviceInfo->pPrivateStorage, hr, "WiaMCamGetItemData");
|
|
|
|
pPrivateDeviceInfo = (UNALIGNED FAKECAM_DEVICE_INFO *) pDeviceInfo->pPrivateStorage;
|
|
|
|
if (uiState & MCAM_STATE_FIRST)
|
|
{
|
|
if (pPrivateDeviceInfo->hFile != NULL)
|
|
{
|
|
wiauDbgError("WiaMCamGetItemData", "File handle is already open");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ptszFullName = (PTSTR) pItem->pPrivateStorage;
|
|
|
|
wiauDbgTrace("WiaMCamGetItemData", "Opening %" WIAU_DEBUG_TSTR " for reading", ptszFullName);
|
|
|
|
pPrivateDeviceInfo->hFile = CreateFile(ptszFullName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
REQUIRE_FILEHANDLE(pPrivateDeviceInfo->hFile, hr, "WiaMCamGetItemData", "CreateFile failed");
|
|
}
|
|
|
|
if (!(uiState & MCAM_STATE_CANCEL))
|
|
{
|
|
DWORD dwReceived = 0;
|
|
|
|
if (!pPrivateDeviceInfo->hFile) {
|
|
wiauDbgError("WiaMCamGetItemData", "File handle is NULL");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!pBuf) {
|
|
wiauDbgError("WiaMCamGetItemData", "Data buffer is NULL");
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ret = ReadFile(pPrivateDeviceInfo->hFile, pBuf, dwLength, &dwReceived, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "WiaMCamGetItemData", "ReadFile failed");
|
|
|
|
if (dwLength != dwReceived)
|
|
{
|
|
wiauDbgError("WiaMCamGetItemData", "Incorrect amount read %d", dwReceived);
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Sleep(100);
|
|
}
|
|
|
|
if (uiState & (MCAM_STATE_LAST | MCAM_STATE_CANCEL))
|
|
{
|
|
if (!pPrivateDeviceInfo->hFile) {
|
|
wiauDbgError("WiaMCamGetItemData", "File handle is NULL");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CloseHandle(pPrivateDeviceInfo->hFile);
|
|
pPrivateDeviceInfo->hFile = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Deletes an item
|
|
//
|
|
HRESULT WiaMCamDeleteItem(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItem)
|
|
{
|
|
DBG_FN("WiaMCamDeleteItem");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
DWORD dwFileAttr = 0;
|
|
PTSTR ptszFullName = NULL;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pItem, hr, "WiaMCamDeleteItem");
|
|
|
|
ptszFullName = (PTSTR) pItem->pPrivateStorage;
|
|
|
|
dwFileAttr = GetFileAttributes(ptszFullName);
|
|
REQUIRE_FILEIO(dwFileAttr != -1, hr, "WiaMCamDeleteItem", "GetFileAttributes failed");
|
|
|
|
dwFileAttr |= FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
ret = SetFileAttributes(ptszFullName, dwFileAttr);
|
|
REQUIRE_FILEIO(ret, hr, "WiaMCamDeleteItem", "SetFileAttributes failed");
|
|
|
|
wiauDbgTrace("WiaMCamDeleteItem", "File %" WIAU_DEBUG_TSTR " is now hidden", ptszFullName);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Sets the protection for an item
|
|
//
|
|
HRESULT WiaMCamSetItemProt(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO *pItem, BOOL bReadOnly)
|
|
{
|
|
DBG_FN("WiaMCamSetItemProt");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
DWORD dwFileAttr = 0;
|
|
PTSTR ptszFullName = NULL;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !pItem, hr, "WiaMCamSetItemProt");
|
|
|
|
ptszFullName = (PTSTR) pItem->pPrivateStorage;
|
|
|
|
dwFileAttr = GetFileAttributes(ptszFullName);
|
|
REQUIRE_FILEIO(dwFileAttr != -1, hr, "WiaMCamSetItemProt", "GetFileAttributes failed");
|
|
|
|
if (bReadOnly)
|
|
dwFileAttr |= FILE_ATTRIBUTE_READONLY;
|
|
else
|
|
dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
|
|
|
|
ret = SetFileAttributes(ptszFullName, dwFileAttr);
|
|
REQUIRE_FILEIO(ret, hr, "WiaMCamSetItemProt", "SetFileAttributes failed");
|
|
|
|
wiauDbgTrace("WiaMCamSetItemProt", "Protection on file %" WIAU_DEBUG_TSTR " set to %d", ptszFullName, bReadOnly);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Captures a new image
|
|
//
|
|
HRESULT WiaMCamTakePicture(MCAM_DEVICE_INFO *pDeviceInfo, MCAM_ITEM_INFO **ppItem)
|
|
{
|
|
DBG_FN("WiaMCamTakePicture");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo || !ppItem, hr, "WiaMCamTakePicture");
|
|
*ppItem = NULL;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// See if the camera is active
|
|
//
|
|
HRESULT WiaMCamStatus(MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("WiaMCamStatus");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo, hr, "WiaMCamStatus");
|
|
|
|
//
|
|
// This sample device is always active, but your driver should contact the
|
|
// device and return S_FALSE if it's not ready.
|
|
//
|
|
// if (NotReady)
|
|
// return S_FALSE;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Reset the camera
|
|
//
|
|
HRESULT WiaMCamReset(MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("WiaMCamReset");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pDeviceInfo, hr, "WiaMCamReset");
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//
|
|
// Helper functions
|
|
//
|
|
////////////////////////////////
|
|
|
|
//
|
|
// This function pretends to open a camera. A real driver
|
|
// would call CreateDevice.
|
|
//
|
|
HRESULT FakeCamOpen(PTSTR ptszPortName, MCAM_DEVICE_INFO *pDeviceInfo)
|
|
{
|
|
DBG_FN("FakeCamOpen");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret = FALSE;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
FAKECAM_DEVICE_INFO *pPrivateDeviceInfo = NULL;
|
|
DWORD dwFileAttr = 0;
|
|
PTSTR ptszRootPath = NULL;
|
|
UINT uiRootPathSize = 0;
|
|
TCHAR tszPathTemplate[] = TEXT("%userprofile%\\image");
|
|
|
|
//
|
|
// Get a pointer to the private storage, so we can put the
|
|
// directory name there
|
|
//
|
|
pPrivateDeviceInfo = (UNALIGNED FAKECAM_DEVICE_INFO *) pDeviceInfo->pPrivateStorage;
|
|
ptszRootPath = pPrivateDeviceInfo->tszRootPath;
|
|
uiRootPathSize = sizeof(pPrivateDeviceInfo->tszRootPath) / sizeof(pPrivateDeviceInfo->tszRootPath[0]);
|
|
|
|
//
|
|
// Unless the port name is set to something other than COMx,
|
|
// LPTx, or AUTO, use %userprofile%\image as the search directory.
|
|
// Since driver runs in LOCAL SERVICE context, %userprofile% points to profile
|
|
// of LOCAL SERVICE account, like "Documents and Settings\Local Service"
|
|
//
|
|
if (_tcsstr(ptszPortName, TEXT("COM")) ||
|
|
_tcsstr(ptszPortName, TEXT("LPT")) ||
|
|
CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, ptszPortName, -1, TEXT("AUTO"), -1) == CSTR_EQUAL)
|
|
{
|
|
DWORD dwResult = ExpandEnvironmentStrings(tszPathTemplate, ptszRootPath, uiRootPathSize);
|
|
if (dwResult == 0 || dwResult > uiRootPathSize)
|
|
{
|
|
wiauDbgError("WiaMCamOpen", "ExpandEnvironmentStrings failed");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(ptszRootPath, ptszPortName, uiRootPathSize);
|
|
}
|
|
|
|
wiauDbgTrace("Open", "Image directory path is %" WIAU_DEBUG_TSTR, ptszRootPath);
|
|
|
|
dwFileAttr = GetFileAttributes(ptszRootPath);
|
|
if (dwFileAttr == -1)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
|
{
|
|
hr = S_OK;
|
|
ret = CreateDirectory(ptszRootPath, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "Open", "CreateDirectory failed");
|
|
}
|
|
else
|
|
{
|
|
wiauDbgErrorHr(hr, "Open", "GetFileAttributes failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// This function searches a directory on the hard drive for
|
|
// items.
|
|
//
|
|
HRESULT SearchDir(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pParent, PTSTR ptszPath)
|
|
{
|
|
DBG_FN("SearchDir");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
HANDLE hFind = NULL;
|
|
WIN32_FIND_DATA FindData;
|
|
const cchTempStrSize = MAX_PATH;
|
|
TCHAR tszTempStr[cchTempStrSize] = TEXT("");
|
|
TCHAR tszFullName[MAX_PATH] = TEXT("");;
|
|
MCAM_ITEM_INFO *pFolder = NULL;
|
|
MCAM_ITEM_INFO *pImage = NULL;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !ptszPath, hr, "SearchDir");
|
|
|
|
//
|
|
// Search for folders first
|
|
//
|
|
|
|
//
|
|
// Make sure search path fits into the buffer and gets zero-terminated
|
|
//
|
|
if (_sntprintf(tszTempStr, cchTempStrSize, _T("%s\\*"), ptszPath) < 0)
|
|
{
|
|
wiauDbgError("SearchDir", "Too long path for search");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
tszTempStr[cchTempStrSize - 1] = 0;
|
|
|
|
wiauDbgTrace("SearchDir", "Searching directory %" WIAU_DEBUG_TSTR, tszTempStr);
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
hFind = FindFirstFile(tszTempStr, &FindData);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
|
{
|
|
hr = S_OK;
|
|
wiauDbgWarning("SearchDir", "Directory %" WIAU_DEBUG_TSTR " is empty", tszTempStr);
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
wiauDbgErrorHr(hr, "SearchDir", "FindFirstFile failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
while (hr == S_OK)
|
|
{
|
|
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
(FindData.cFileName[0] != L'.'))
|
|
{
|
|
hr = MakeFullName(tszFullName, sizeof(tszFullName) / sizeof(tszFullName[0]),
|
|
ptszPath, FindData.cFileName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "MakeFullName failed");
|
|
|
|
hr = CreateFolder(pPrivateDeviceInfo, pParent, &FindData, &pFolder, tszFullName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "CreateFolder failed");
|
|
|
|
hr = AddItem(pPrivateDeviceInfo, pFolder);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "AddItem failed");
|
|
|
|
hr = SearchDir(pPrivateDeviceInfo, pFolder, tszFullName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "Recursive SearchDir failed");
|
|
}
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
if (!FindNextFile(hFind, &FindData))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES))
|
|
{
|
|
wiauDbgErrorHr(hr, "SearchDir", "FindNextFile failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
FindClose(hFind);
|
|
hr = S_OK;
|
|
|
|
//
|
|
// Search next for JPEGs
|
|
//
|
|
|
|
//
|
|
// Make sure search path fits into the buffer and gets zero-terminated
|
|
//
|
|
if (_sntprintf(tszTempStr, cchTempStrSize, _T("%s\\*.jpg"), ptszPath) < 0)
|
|
{
|
|
wiauDbgError("SearchDir", "Too long path for search");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
tszTempStr[cchTempStrSize - 1] = 0;
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
|
|
hFind = FindFirstFile(tszTempStr, &FindData);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
|
{
|
|
hr = S_OK;
|
|
wiauDbgWarning("SearchDir", "No JPEGs in directory %" WIAU_DEBUG_TSTR, tszTempStr);
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
wiauDbgErrorHr(hr, "SearchDir", "FindFirstFile failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
while (hr == S_OK)
|
|
{
|
|
if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
|
|
{
|
|
hr = MakeFullName(tszFullName, sizeof(tszFullName) / sizeof(tszFullName[0]),
|
|
ptszPath, FindData.cFileName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "MakeFullName failed");
|
|
|
|
hr = CreateImage(pPrivateDeviceInfo, pParent, &FindData, &pImage, tszFullName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "CreateImage failed");
|
|
|
|
hr = AddItem(pPrivateDeviceInfo, pImage);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "AddItem failed");
|
|
|
|
hr = SearchForAttachments(pPrivateDeviceInfo, pImage, tszFullName);
|
|
REQUIRE_SUCCESS(hr, "SearchDir", "SearchForAttachments failed");
|
|
|
|
if (hr == S_OK)
|
|
pImage->bHasAttachments = TRUE;
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
if (!FindNextFile(hFind, &FindData))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES))
|
|
{
|
|
wiauDbgErrorHr(hr, "SearchDir", "FindNextFile failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
FindClose(hFind);
|
|
hr = S_OK;
|
|
|
|
//
|
|
// ISSUE-10/18/2000-davepar Do the same for other image types
|
|
//
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Searches for attachments to an image item
|
|
//
|
|
HRESULT SearchForAttachments(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pParent, PTSTR ptszMainItem)
|
|
{
|
|
DBG_FN("SearchForAttachments");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
INT iNumAttachments = 0;
|
|
HANDLE hFind = NULL;
|
|
WIN32_FIND_DATA FindData;
|
|
TCHAR tszTempStr[MAX_PATH] = TEXT("");
|
|
TCHAR tszFullName[MAX_PATH] = TEXT("");
|
|
TCHAR *ptcSlash = NULL;
|
|
TCHAR *ptcDot = NULL;
|
|
MCAM_ITEM_INFO *pNonImage = NULL;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !ptszMainItem, hr, "SearchForAttachments");
|
|
|
|
//
|
|
// Find the last dot in the filename and replace the file extension with * and do the search
|
|
//
|
|
lstrcpyn(tszTempStr, ptszMainItem, sizeof(tszTempStr) / sizeof(tszTempStr[0]) - 1);
|
|
ptcDot = _tcsrchr(tszTempStr, TEXT('.'));
|
|
|
|
if (ptcDot)
|
|
{
|
|
*(ptcDot+1) = TEXT('*');
|
|
*(ptcDot+2) = TEXT('\0');
|
|
}
|
|
else
|
|
{
|
|
wiauDbgError("SearchForAttachments", "Filename did not contain a dot");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Replace the first four "free" characters of the name with ? (attachments only need to match
|
|
// the last four characters of the name)
|
|
//
|
|
ptcSlash = _tcsrchr(tszTempStr, TEXT('\\'));
|
|
if (ptcSlash && ptcDot - ptcSlash > 4)
|
|
{
|
|
for (INT i = 1; i < 5; i++)
|
|
*(ptcSlash+i) = TEXT('?');
|
|
}
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
hFind = FindFirstFile(tszTempStr, &FindData);
|
|
REQUIRE_FILEHANDLE(hFind, hr, "SearchForAttachments", "FindFirstFile failed");
|
|
|
|
while (hr == S_OK)
|
|
{
|
|
if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) &&
|
|
!(_tcsstr(ptszMainItem, FindData.cFileName)))
|
|
{
|
|
//
|
|
// Figure out the full name for the item
|
|
//
|
|
lstrcpyn(tszTempStr, ptszMainItem, sizeof(tszTempStr) / sizeof(tszTempStr[0]));
|
|
ptcSlash = _tcsrchr(tszTempStr, TEXT('\\'));
|
|
if (ptcSlash)
|
|
{
|
|
*ptcSlash = TEXT('\0');
|
|
}
|
|
|
|
hr = MakeFullName(tszFullName, sizeof(tszFullName) / sizeof(tszFullName[0]),
|
|
tszTempStr, FindData.cFileName);
|
|
REQUIRE_SUCCESS(hr, "SearchForAttachments", "MakeFullName failed");
|
|
|
|
hr = CreateNonImage(pPrivateDeviceInfo, pParent, &FindData, &pNonImage, tszFullName);
|
|
REQUIRE_SUCCESS(hr, "SearchForAttachments", "CreateNonImage failed");
|
|
|
|
hr = AddItem(pPrivateDeviceInfo, pNonImage);
|
|
REQUIRE_SUCCESS(hr, "SearchForAttachments", "AddItem failed");
|
|
|
|
iNumAttachments++;
|
|
}
|
|
|
|
memset(&FindData, 0, sizeof(FindData));
|
|
if (!FindNextFile(hFind, &FindData))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
|
if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES))
|
|
{
|
|
wiauDbgErrorHr(hr, "SearchForAttachments", "FindNextFile failed");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
FindClose(hFind);
|
|
if (iNumAttachments > 0)
|
|
hr = S_OK;
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CreateFolder(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pParent,
|
|
WIN32_FIND_DATA *pFindData, MCAM_ITEM_INFO **ppFolder, PTSTR ptszFullName)
|
|
{
|
|
DBG_FN("CreateFolder");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
TCHAR *ptcDot = NULL;
|
|
MCAM_ITEM_INFO *pItem = NULL;
|
|
TCHAR tszTempStr[MAX_PATH] = TEXT("");
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !pFindData || !ppFolder || !ptszFullName, hr, "CreateFolder");
|
|
*ppFolder = NULL;
|
|
|
|
pItem = new MCAM_ITEM_INFO;
|
|
REQUIRE_ALLOC(pItem, hr, "CreateFolder");
|
|
|
|
//
|
|
// Chop off the filename extension from the name, if it exists
|
|
//
|
|
lstrcpyn(tszTempStr, pFindData->cFileName, sizeof(tszTempStr) / sizeof(tszTempStr[0]));
|
|
ptcDot = _tcsrchr(tszTempStr, TEXT('.'));
|
|
if (ptcDot)
|
|
*ptcDot = TEXT('\0');
|
|
|
|
//
|
|
// Fill in the MCAM_ITEM_INFO structure
|
|
//
|
|
hr = SetCommonFields(pItem, tszTempStr, ptszFullName, pFindData);
|
|
REQUIRE_SUCCESS(hr, "CreateFolder", "SetCommonFields failed");
|
|
|
|
pItem->pParent = pParent;
|
|
pItem->iType = WiaMCamTypeFolder;
|
|
|
|
*ppFolder = pItem;
|
|
|
|
pPrivateDeviceInfo->iNumItems++;
|
|
|
|
wiauDbgTrace("CreateFolder", "Created folder %" WIAU_DEBUG_TSTR " at 0x%08x under 0x%08x", pFindData->cFileName, pItem, pParent);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CreateImage(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pParent,
|
|
WIN32_FIND_DATA *pFindData, MCAM_ITEM_INFO **ppImage, PTSTR ptszFullName)
|
|
{
|
|
DBG_FN("CreateImage");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
PTSTR ptszDot = NULL;
|
|
MCAM_ITEM_INFO *pItem = NULL;
|
|
TCHAR tszTempStr[MAX_PATH] = TEXT("");
|
|
WORD width = 0;
|
|
WORD height = 0;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !pFindData || !ppImage || !ptszFullName, hr, "CreateImage");
|
|
*ppImage = NULL;
|
|
|
|
pItem = new MCAM_ITEM_INFO;
|
|
REQUIRE_ALLOC(pItem, hr, "CreateImage");
|
|
|
|
//
|
|
// Chop off the filename extension from the name, if it exists
|
|
//
|
|
lstrcpyn(tszTempStr, pFindData->cFileName, sizeof(tszTempStr) / sizeof(tszTempStr[0]));
|
|
ptszDot = _tcsrchr(tszTempStr, TEXT('.'));
|
|
if (ptszDot)
|
|
*ptszDot = TEXT('\0');
|
|
|
|
//
|
|
// Fill in the MCAM_ITEM_INFO structure
|
|
//
|
|
hr = SetCommonFields(pItem, tszTempStr, ptszFullName, pFindData);
|
|
REQUIRE_SUCCESS(hr, "CreateImage", "SetCommonFields failed");
|
|
|
|
pItem->pParent = pParent;
|
|
pItem->iType = WiaMCamTypeImage;
|
|
pItem->pguidFormat = &WiaImgFmt_JPEG;
|
|
pItem->lSize = pFindData->nFileSizeLow;
|
|
pItem->pguidThumbFormat = &WiaImgFmt_JPEG;
|
|
|
|
//
|
|
// Copy the file extension into the extension field
|
|
//
|
|
if (ptszDot) {
|
|
hr = wiauStrT2W(ptszDot + 1, pItem->wszExt, MCAM_EXT_LEN * sizeof(pItem->wszExt[0]));
|
|
REQUIRE_SUCCESS(hr, "CreateImage", "wiauStrT2W failed");
|
|
}
|
|
|
|
//
|
|
// Interpret the JPEG image to get the image dimensions and thumbnail size
|
|
//
|
|
hr = ReadDimFromJpeg(ptszFullName, &width, &height);
|
|
REQUIRE_SUCCESS(hr, "CreateImage", "ReadDimFromJpeg failed");
|
|
|
|
pItem->lWidth = width;
|
|
pItem->lHeight = height;
|
|
pItem->lDepth = 24;
|
|
pItem->lChannels = 3;
|
|
pItem->lBitsPerChannel = 8;
|
|
|
|
*ppImage = pItem;
|
|
|
|
pPrivateDeviceInfo->iNumItems++;
|
|
pPrivateDeviceInfo->iNumImages++;
|
|
|
|
wiauDbgTrace("CreateImage", "Created image %" WIAU_DEBUG_TSTR " at 0x%08x under 0x%08x", pFindData->cFileName, pItem, pParent);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CreateNonImage(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pParent,
|
|
WIN32_FIND_DATA *pFindData, MCAM_ITEM_INFO **ppNonImage, PTSTR ptszFullName)
|
|
{
|
|
DBG_FN("CreateNonImage");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
PTSTR ptszDot = NULL;
|
|
MCAM_ITEM_INFO *pItem = NULL;
|
|
TCHAR tszTempStr[MAX_PATH] = TEXT("");
|
|
PTSTR ptszExt = NULL;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !pFindData || !ppNonImage || !ptszFullName, hr, "CreateNonImage");
|
|
*ppNonImage = NULL;
|
|
|
|
pItem = new MCAM_ITEM_INFO;
|
|
REQUIRE_ALLOC(pItem, hr, "CreateNonImage");
|
|
|
|
//
|
|
// The name cannot contain a dot and the name needs to be unique
|
|
// wrt the parent image, so replace the dot with an underline character.
|
|
//
|
|
lstrcpyn(tszTempStr, pFindData->cFileName, sizeof(tszTempStr) / sizeof(tszTempStr[0]));
|
|
ptszDot = _tcsrchr(tszTempStr, TEXT('.'));
|
|
if (ptszDot)
|
|
*ptszDot = TEXT('_');
|
|
|
|
//
|
|
// Fill in the MCAM_ITEM_INFO structure
|
|
//
|
|
hr = SetCommonFields(pItem, tszTempStr, ptszFullName, pFindData);
|
|
REQUIRE_SUCCESS(hr, "CreateNonImage", "SetCommonFields failed");
|
|
|
|
pItem->pParent = pParent;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
pItem->lSize = pFindData->nFileSizeLow;
|
|
|
|
//
|
|
// Set the format of the item based on the file extension
|
|
//
|
|
if (ptszDot) {
|
|
ptszExt = ptszDot + 1;
|
|
|
|
//
|
|
// Copy the file extension into the extension field
|
|
//
|
|
hr = wiauStrT2W(ptszExt, pItem->wszExt, MCAM_EXT_LEN * sizeof(pItem->wszExt[0]));
|
|
REQUIRE_SUCCESS(hr, "CreateNonImage", "wiauStrT2W failed");
|
|
|
|
if (_tcsicmp(ptszExt, TEXT("wav")) == 0) {
|
|
pItem->pguidFormat = &WiaAudFmt_WAV;
|
|
pItem->iType = WiaMCamTypeAudio;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("mp3")) == 0) {
|
|
pItem->pguidFormat = &WiaAudFmt_MP3;
|
|
pItem->iType = WiaMCamTypeAudio;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("wma")) == 0) {
|
|
pItem->pguidFormat = &WiaAudFmt_WMA;
|
|
pItem->iType = WiaMCamTypeAudio;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("rtf")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_RTF;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("htm")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_HTML;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("html")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_HTML;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("txt")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_TXT;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("mpg")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_MPG;
|
|
pItem->iType = WiaMCamTypeVideo;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("avi")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_AVI;
|
|
pItem->iType = WiaMCamTypeVideo;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("asf")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_ASF;
|
|
pItem->iType = WiaMCamTypeVideo;
|
|
}
|
|
else if (_tcsicmp(ptszExt, TEXT("exe")) == 0) {
|
|
pItem->pguidFormat = &WiaImgFmt_EXEC;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
else {
|
|
//
|
|
// Generate a random GUID for the format
|
|
//
|
|
if (g_guidUnknownFormat.Data1 == 0) {
|
|
hr = CoCreateGuid(&g_guidUnknownFormat);
|
|
REQUIRE_SUCCESS(hr, "CreateNonImage", "CoCreateGuid failed");
|
|
}
|
|
pItem->pguidFormat = &g_guidUnknownFormat;
|
|
pItem->iType = WiaMCamTypeOther;
|
|
}
|
|
}
|
|
|
|
*ppNonImage = pItem;
|
|
|
|
pPrivateDeviceInfo->iNumItems++;
|
|
|
|
wiauDbgTrace("CreateNonImage", "Created non-image %" WIAU_DEBUG_TSTR " at 0x%08x under 0x%08x", pFindData->cFileName, pItem, pParent);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Sets the fields of the MCAM_ITEM_INFO that are common to all items
|
|
//
|
|
HRESULT SetCommonFields(MCAM_ITEM_INFO *pItem,
|
|
PTSTR ptszShortName,
|
|
PTSTR ptszFullName,
|
|
WIN32_FIND_DATA *pFindData)
|
|
{
|
|
DBG_FN("SetCommonFields");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
PTSTR ptszTempStr = NULL;
|
|
INT iSize = 0;
|
|
|
|
REQUIRE_ARGS(!pItem || !ptszShortName || !ptszFullName || !pFindData, hr, "SetFullName");
|
|
|
|
//
|
|
// Initialize the structure
|
|
//
|
|
memset(pItem, 0, sizeof(MCAM_ITEM_INFO));
|
|
pItem->iSize = sizeof(MCAM_ITEM_INFO);
|
|
|
|
iSize = lstrlen(ptszShortName) + 1;
|
|
pItem->pwszName = new WCHAR[iSize];
|
|
REQUIRE_ALLOC(pItem->pwszName, hr, "SetCommonFields");
|
|
wiauStrT2W(ptszShortName, pItem->pwszName, iSize * sizeof(WCHAR));
|
|
REQUIRE_SUCCESS(hr, "SetCommonFields", "wiauStrT2W failed");
|
|
|
|
FILETIME ftLocalFileTime;
|
|
memset(&pItem->Time, 0, sizeof(pItem->Time));
|
|
memset(&ftLocalFileTime, 0, sizeof(FILETIME));
|
|
ret = FileTimeToLocalFileTime(&pFindData->ftLastWriteTime, &ftLocalFileTime);
|
|
REQUIRE_FILEIO(ret, hr, "SetCommonFields", "FileTimeToLocalFileTime failed");
|
|
ret = FileTimeToSystemTime(&ftLocalFileTime, &pItem->Time);
|
|
REQUIRE_FILEIO(ret, hr, "SetCommonFields", "FileTimeToSystemTime failed");
|
|
|
|
pItem->bReadOnly = pFindData->dwFileAttributes & FILE_ATTRIBUTE_READONLY;
|
|
pItem->bCanSetReadOnly = TRUE;
|
|
|
|
//
|
|
// Set the private storage area of the MCAM_ITEM_INFO structure to the
|
|
// full path name of the item
|
|
//
|
|
iSize = lstrlen(ptszFullName) + 1;
|
|
ptszTempStr = new TCHAR[iSize];
|
|
REQUIRE_ALLOC(ptszTempStr, hr, "SetCommonFields");
|
|
lstrcpy(ptszTempStr, ptszFullName);
|
|
pItem->pPrivateStorage = (BYTE *) ptszTempStr;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT AddItem(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !pItem, hr, "AddItem");
|
|
|
|
if (pPrivateDeviceInfo->pLastItem) {
|
|
//
|
|
// Insert the item at the end of the list
|
|
//
|
|
pPrivateDeviceInfo->pLastItem->pNext = pItem;
|
|
pItem->pPrev = pPrivateDeviceInfo->pLastItem;
|
|
pItem->pNext = NULL;
|
|
pPrivateDeviceInfo->pLastItem = pItem;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// List is currently empty, add this as first and only item
|
|
//
|
|
pPrivateDeviceInfo->pFirstItem = pPrivateDeviceInfo->pLastItem = pItem;
|
|
pItem->pPrev = pItem->pNext = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT RemoveItem(FAKECAM_DEVICE_INFO *pPrivateDeviceInfo, MCAM_ITEM_INFO *pItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
REQUIRE_ARGS(!pPrivateDeviceInfo || !pItem, hr, "RemoveItem");
|
|
|
|
if (pItem->pPrev)
|
|
pItem->pPrev->pNext = pItem->pNext;
|
|
if (pItem->pNext)
|
|
pItem->pNext->pPrev = pItem->pPrev;
|
|
|
|
if (pPrivateDeviceInfo->pFirstItem == pItem)
|
|
pPrivateDeviceInfo->pFirstItem = pItem->pNext;
|
|
if (pPrivateDeviceInfo->pLastItem == pItem)
|
|
pPrivateDeviceInfo->pLastItem = pItem->pPrev;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// This function reads a JPEG file looking for the frame header, which contains
|
|
// the width and height of the image.
|
|
//
|
|
HRESULT ReadDimFromJpeg(PTSTR ptszFullName, WORD *pWidth, WORD *pHeight)
|
|
{
|
|
DBG_FN("ReadDimFromJpeg");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
HANDLE hFile = NULL;
|
|
BYTE *pBuffer = NULL;
|
|
DWORD BytesRead = 0;
|
|
BYTE *pCur = NULL;
|
|
int SegmentLength = 0;
|
|
const int Overlap = 8; // if pCur gets within Overlap bytes of the end, read another chunk
|
|
const DWORD BytesToRead = 32 * 1024;
|
|
|
|
REQUIRE_ARGS(!ptszFullName || !pWidth || !pHeight, hr, "ReadDimFromJpeg");
|
|
|
|
*pWidth = 0;
|
|
*pHeight = 0;
|
|
|
|
hFile = CreateFile(ptszFullName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
REQUIRE_FILEHANDLE(hFile, hr, "ReadDimFromJpeg", "CreateFile failed");
|
|
|
|
pBuffer = new BYTE[BytesToRead];
|
|
REQUIRE_ALLOC(pBuffer, hr, "ReadDimFromJpeg");
|
|
|
|
ret = ReadFile(hFile, pBuffer, BytesToRead, &BytesRead, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "ReadDimFromJpeg", "ReadFile failed");
|
|
|
|
wiauDbgTrace("ReadDimFromJpeg", "Read %d bytes", BytesRead);
|
|
|
|
pCur = pBuffer;
|
|
|
|
//
|
|
// Pretend that we read Overlap fewer bytes than were actually read
|
|
//
|
|
BytesRead -= Overlap;
|
|
|
|
while (SUCCEEDED(hr) &&
|
|
BytesRead != 0 &&
|
|
pCur[1] != 0xc0)
|
|
{
|
|
if (pCur[0] != 0xff)
|
|
{
|
|
wiauDbgError("ReadDimFromJpeg", "Not a JFIF format image");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// if the marker is >= 0xd0 and <= 0xd9 or is equal to 0x01
|
|
// there is no length field
|
|
//
|
|
if (((pCur[1] & 0xf0) == 0xd0 &&
|
|
(pCur[1] & 0x0f) < 0xa) ||
|
|
pCur[1] == 0x01)
|
|
{
|
|
SegmentLength = 0;
|
|
}
|
|
else
|
|
{
|
|
SegmentLength = ByteSwapWord(*((UNALIGNED WORD *) (pCur + 2)));
|
|
}
|
|
|
|
pCur += SegmentLength + 2;
|
|
|
|
if (pCur >= pBuffer + BytesRead)
|
|
{
|
|
memcpy(pBuffer, pBuffer + BytesRead, Overlap);
|
|
|
|
pCur -= BytesRead;
|
|
|
|
ret = ReadFile(hFile, pBuffer + Overlap, BytesToRead - Overlap, &BytesRead, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "ReadDimFromJpeg", "ReadFile failed");
|
|
|
|
wiauDbgTrace("ReadDimFromJpeg", "Read %d more bytes", BytesRead);
|
|
}
|
|
}
|
|
|
|
if (pCur[0] != 0xff)
|
|
{
|
|
wiauDbgError("ReadDimFromJpeg", "Not a JFIF format image");
|
|
return E_FAIL;
|
|
}
|
|
|
|
*pHeight = ByteSwapWord(*((UNALIGNED WORD *) (pCur + 5)));
|
|
*pWidth = ByteSwapWord(*((UNALIGNED WORD *) (pCur + 7)));
|
|
|
|
Cleanup:
|
|
if (pBuffer) {
|
|
delete []pBuffer;
|
|
}
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// The next section contains functions useful for reading information from
|
|
// Exif files.
|
|
//
|
|
HRESULT ReadJpegHdr(PTSTR ptszFullName, BYTE **ppBuf)
|
|
{
|
|
DBG_FN("ReadJpegHdr");
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL ret;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
HANDLE hFile = NULL;
|
|
BYTE JpegHdr[] = {0xff, 0xd8, 0xff, 0xe1};
|
|
const int JpegHdrSize = sizeof(JpegHdr) + 2;
|
|
BYTE tempBuf[JpegHdrSize];
|
|
DWORD BytesRead = 0;
|
|
WORD TagSize = 0;
|
|
|
|
hFile = CreateFile(ptszFullName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
REQUIRE_FILEHANDLE(hFile, hr, "ReadJpegHdr", "CreateFile failed");
|
|
|
|
|
|
ret = ReadFile(hFile, tempBuf, JpegHdrSize, &BytesRead, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "ReadJpegHdr", "ReadFile failed");
|
|
|
|
if (BytesRead != JpegHdrSize) {
|
|
wiauDbgError("ReadJpegHdr", "Wrong amount read %d", BytesRead);
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (memcmp(tempBuf, JpegHdr, sizeof(JpegHdr)) != 0)
|
|
{
|
|
wiauDbgError("ReadJpegHdr", "JPEG header not found");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TagSize = GetWord(tempBuf + sizeof(JpegHdr), TRUE);
|
|
*ppBuf = new BYTE[TagSize];
|
|
REQUIRE_ALLOC(ppBuf, hr, "ReadJpegHdr");
|
|
|
|
ret = ReadFile(hFile, *ppBuf, TagSize, &BytesRead, NULL);
|
|
REQUIRE_FILEIO(ret, hr, "ReadJpegHdr", "ReadFile failed");
|
|
|
|
if (BytesRead != TagSize)
|
|
{
|
|
wiauDbgError("ReadJpegHdr", "Wrong amount read %d", BytesRead);
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT ReadExifJpeg(BYTE *pBuf, IFD *pImageIfd, IFD *pThumbIfd, BOOL *pbSwap)
|
|
{
|
|
DBG_FN("ReadExifJpeg");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
BYTE ExifTag[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
|
|
|
|
if (memcmp(pBuf, ExifTag, sizeof(ExifTag)) != 0)
|
|
{
|
|
wiauDbgError("ReadExifJpeg", "Exif tag not found");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = ReadTiff(pBuf + APP1_OFFSET, pImageIfd, pThumbIfd, pbSwap);
|
|
REQUIRE_SUCCESS(hr, "ReadExifJpeg", "ReadTiff failed");
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ReadTiff(BYTE *pBuf, IFD *pImageIfd, IFD *pThumbIfd, BOOL *pbSwap)
|
|
{
|
|
DBG_FN("ReadTiff");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Locals
|
|
//
|
|
WORD MagicNumber = 0;
|
|
|
|
*pbSwap = FALSE;
|
|
|
|
if (pBuf[0] == 0x4d) {
|
|
*pbSwap = TRUE;
|
|
if (pBuf[1] != 0x4d)
|
|
{
|
|
wiauDbgError("ReadTiff", "Second TIFF byte swap indicator not present");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (pBuf[0] != 0x49 ||
|
|
pBuf[1] != 0x49)
|
|
{
|
|
wiauDbgError("ReadTiff", "TIFF byte swap indicator not present");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
MagicNumber = GetWord(pBuf+2, *pbSwap);
|
|
if (MagicNumber != 42)
|
|
{
|
|
wiauDbgError("ReadTiff", "TIFF magic number not present");
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wiauDbgTrace("ReadTiff", "Reading image IFD");
|
|
|
|
pImageIfd->Offset = GetDword(pBuf + 4, *pbSwap);
|
|
hr = ReadIfd(pBuf, pImageIfd, *pbSwap);
|
|
REQUIRE_SUCCESS(hr, "ReadTiff", "ReadIfd failed");
|
|
|
|
wiauDbgTrace("ReadTiff", "Reading thumb IFD");
|
|
|
|
pThumbIfd->Offset = pImageIfd->NextIfdOffset;
|
|
hr = ReadIfd(pBuf, pThumbIfd, *pbSwap);
|
|
REQUIRE_SUCCESS(hr, "ReadTiff", "ReadIfd failed");
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ReadIfd(BYTE *pBuf, IFD *pIfd, BOOL bSwap)
|
|
{
|
|
DBG_FN("ReadIfd");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
const int DIR_ENTRY_SIZE = 12;
|
|
|
|
pBuf += pIfd->Offset;
|
|
|
|
pIfd->Count = GetWord(pBuf, bSwap);
|
|
|
|
pIfd->pEntries = new DIR_ENTRY[pIfd->Count];
|
|
if (!pIfd->pEntries)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pBuf += 2;
|
|
for (int count = 0; count < pIfd->Count; count++)
|
|
{
|
|
pIfd->pEntries[count].Tag = GetWord(pBuf, bSwap);
|
|
pIfd->pEntries[count].Type = GetWord(pBuf + 2, bSwap);
|
|
pIfd->pEntries[count].Count = GetDword(pBuf + 4, bSwap);
|
|
pIfd->pEntries[count].Offset = GetDword(pBuf + 8, bSwap);
|
|
pBuf += DIR_ENTRY_SIZE;
|
|
|
|
wiauDbgDump("ReadIfd", "Tag 0x%04x, type %2d offset/value 0x%08x",
|
|
pIfd->pEntries[count].Tag, pIfd->pEntries[count].Type, pIfd->pEntries[count].Offset);
|
|
}
|
|
|
|
pIfd->NextIfdOffset = GetDword(pBuf, bSwap);
|
|
|
|
return hr;
|
|
}
|
|
|
|
VOID FreeIfd(IFD *pIfd)
|
|
{
|
|
if (pIfd->pEntries)
|
|
delete []pIfd->pEntries;
|
|
pIfd->pEntries = NULL;
|
|
}
|
|
|
|
WORD ByteSwapWord(WORD w)
|
|
{
|
|
return (w >> 8) | (w << 8);
|
|
}
|
|
|
|
DWORD ByteSwapDword(DWORD dw)
|
|
{
|
|
return ByteSwapWord((WORD) (dw >> 16)) | (ByteSwapWord((WORD) (dw & 0xffff)) << 16);
|
|
}
|
|
|
|
WORD GetWord(BYTE *pBuf, BOOL bSwap)
|
|
{
|
|
WORD w = *((UNALIGNED WORD *) pBuf);
|
|
|
|
if (bSwap)
|
|
w = ByteSwapWord(w);
|
|
|
|
return w;
|
|
}
|
|
|
|
DWORD GetDword(BYTE *pBuf, BOOL bSwap)
|
|
{
|
|
DWORD dw = *((UNALIGNED DWORD *) pBuf);
|
|
|
|
if (bSwap)
|
|
dw = ByteSwapDword(dw);
|
|
|
|
return dw;
|
|
}
|
|
|
|
/*
|
|
//
|
|
// Set the default and valid values for a property
|
|
//
|
|
VOID
|
|
FakeCamera::SetValidValues(
|
|
INT index,
|
|
CWiaPropertyList *pPropertyList
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
ULONG ExposureModeList[] = {
|
|
EXPOSUREMODE_MANUAL,
|
|
EXPOSUREMODE_AUTO,
|
|
EXPOSUREMODE_APERTURE_PRIORITY,
|
|
EXPOSUREMODE_SHUTTER_PRIORITY,
|
|
EXPOSUREMODE_PROGRAM_CREATIVE,
|
|
EXPOSUREMODE_PROGRAM_ACTION,
|
|
EXPOSUREMODE_PORTRAIT
|
|
};
|
|
|
|
PROPID PropId = pPropertyList->GetPropId(index);
|
|
WIA_PROPERTY_INFO *pPropInfo = pPropertyList->GetWiaPropInfo(index);
|
|
|
|
//
|
|
// Based on the property ID, populate the valid values range or list information
|
|
//
|
|
switch (PropId)
|
|
{
|
|
case WIA_DPC_EXPOSURE_MODE:
|
|
pPropInfo->ValidVal.List.Nom = EXPOSUREMODE_MANUAL;
|
|
pPropInfo->ValidVal.List.cNumList = sizeof(ExposureModeList) / sizeof(ExposureModeList[0]);
|
|
pPropInfo->ValidVal.List.pList = (BYTE*) ExposureModeList;
|
|
break;
|
|
|
|
case WIA_DPC_EXPOSURE_COMP:
|
|
pPropInfo->ValidVal.Range.Nom = 0;
|
|
pPropInfo->ValidVal.Range.Min = -200;
|
|
pPropInfo->ValidVal.Range.Max = 200;
|
|
pPropInfo->ValidVal.Range.Inc = 50;
|
|
break;
|
|
|
|
default:
|
|
WIAS_LERROR(g_pIWiaLog,WIALOG_NO_RESOURCE_ID,("FakeCamera::SetValidValues, property 0x%08x not defined", PropId));
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
*/
|
|
|
|
/**************************************************************************\
|
|
* DllEntryPoint
|
|
*
|
|
* Main library entry point. Receives DLL event notification from OS.
|
|
*
|
|
* We are not interested in thread attaches and detaches,
|
|
* so we disable thread notifications for performance reasons.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hinst -
|
|
* dwReason -
|
|
* lpReserved -
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Returns TRUE to allow the DLL to load.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
|
|
extern "C" __declspec( dllexport )
|
|
BOOL APIENTRY DllEntryPoint(
|
|
HINSTANCE hinst,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
switch (dwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hInst = hinst;
|
|
DisableThreadLibraryCalls(hinst);
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DllCanUnloadNow
|
|
*
|
|
* Determines whether the DLL has any outstanding interfaces.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Returns S_OK if the DLL can unload, S_FALSE if it is not safe to unload.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
extern "C" STDMETHODIMP DllCanUnloadNow(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|