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.
3929 lines
148 KiB
3929 lines
148 KiB
#include "precomp.h"
|
|
|
|
// #define DEBUG_MEMXFER
|
|
|
|
#define PRIVATE_CAP_ARRAY_PADDING 64 // 64 bytes padding
|
|
const TCHAR WIA_STR[] = TEXT("WIA-");
|
|
const CHAR* FAMILY_NAME = "Twain Data Source On WIA";
|
|
|
|
CWiaDataSrc::CWiaDataSrc() :
|
|
m_dsState(DS_STATE_0),
|
|
m_hMemXferBits(NULL),
|
|
m_pMemXferBits(NULL),
|
|
m_pDevice(NULL),
|
|
m_pIWiaItems(NULL),
|
|
m_NumIWiaItems(0),
|
|
m_NextIWiaItemIndex(0),
|
|
m_NumCaps(0),
|
|
m_CapList(NULL),
|
|
m_hCachedImageData(NULL),
|
|
m_bCacheImage(FALSE)
|
|
{
|
|
SetTWAINState(DS_STATE_3);
|
|
memset(m_FileXferName,0,sizeof(m_FileXferName));
|
|
memset(&m_AppIdentity,0,sizeof(m_AppIdentity));
|
|
memset(&m_dsIdentity,0, sizeof(m_dsIdentity));
|
|
memset(&m_CurFrame, 0,sizeof(m_CurFrame));
|
|
memset(&m_CurImageLayout, 0,sizeof(m_CurImageLayout));
|
|
memset(&m_MemoryTransferInfo,0,sizeof(m_MemoryTransferInfo));
|
|
|
|
ResetMemXfer();
|
|
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
m_CurImageLayout.DocumentNumber = 1;
|
|
m_CurImageLayout.PageNumber = 1;
|
|
m_CurImageLayout.FrameNumber = 1;
|
|
m_CurImageLayout.Frame.Top.Whole = 0;
|
|
m_CurImageLayout.Frame.Top.Frac = 0;
|
|
m_CurImageLayout.Frame.Left.Whole = 0;
|
|
m_CurImageLayout.Frame.Left.Frac = 0;
|
|
m_CurImageLayout.Frame.Right.Whole = 8;
|
|
m_CurImageLayout.Frame.Right.Frac = 5;
|
|
m_CurImageLayout.Frame.Bottom.Whole = 11;
|
|
m_CurImageLayout.Frame.Bottom.Frac = 0;
|
|
m_pCurrentIWiaItem = NULL;
|
|
}
|
|
|
|
CWiaDataSrc::~CWiaDataSrc()
|
|
{
|
|
ResetMemXfer();
|
|
if (m_pDevice){
|
|
delete m_pDevice;
|
|
m_pDevice = NULL;
|
|
}
|
|
|
|
if (m_pIWiaItems){
|
|
delete [] m_pIWiaItems;
|
|
m_pIWiaItems = NULL;
|
|
}
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::IWiaDataSrc(LPCTSTR DeviceId)
|
|
{
|
|
if (!DeviceId) {
|
|
m_twStatus.ConditionCode = TWCC_BADVALUE;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Initialize m_dsIdentity. This is required because
|
|
// DG_CONTROL/DAT_IDENTITY/MSG_GET could be called
|
|
// before we are opened.
|
|
//
|
|
|
|
m_pDevice = new CWiaDevice;
|
|
|
|
if (!m_pDevice) {
|
|
m_twStatus.ConditionCode = TWCC_LOWMEMORY;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TCHAR szTempString[MAX_PATH];
|
|
memset(szTempString,0,sizeof(szTempString));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = m_pDevice->Initialize(DeviceId);
|
|
if (FAILED(hr)) {
|
|
m_twStatus.ConditionCode = TWCC_LOWMEMORY;
|
|
delete m_pDevice;
|
|
m_pDevice = NULL;
|
|
DBG_ERR(("CWiaDataSrc::IWiaDataSrc(), WIA Device object Initialize failed"));
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
//
|
|
// We don't need to attach a callback here because we will
|
|
// close the device after we are done with it.
|
|
//
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
m_dsIdentity.Id = 0;
|
|
m_dsIdentity.Version.MajorNum = 1;
|
|
m_dsIdentity.Version.MinorNum = 0;
|
|
m_dsIdentity.Version.Language = TWLG_USA;
|
|
m_dsIdentity.Version.Country = TWCY_USA;
|
|
m_dsIdentity.ProtocolMajor = TWON_PROTOCOLMAJOR;
|
|
m_dsIdentity.ProtocolMinor = TWON_PROTOCOLMINOR;
|
|
m_dsIdentity.SupportedGroups = DG_CONTROL | DG_IMAGE;
|
|
lstrcpyA(m_dsIdentity.Version.Info,"26 June 2000");
|
|
|
|
//
|
|
// Use a specific product family name so that applications
|
|
// can differentiate between a data source on WIA and
|
|
// a *pure* TWAIN data source
|
|
//
|
|
|
|
lstrcpyA(m_dsIdentity.ProductFamily, FAMILY_NAME);
|
|
lstrcpyA(m_dsIdentity.ProductName, "UnknownProduct");
|
|
lstrcpyA(m_dsIdentity.Manufacturer, "UnknownMfg");
|
|
|
|
#ifdef UNICODE
|
|
|
|
//
|
|
// UNICODE specific
|
|
//
|
|
|
|
// This assumes that ProductName, FamilyName and Manufacturer
|
|
// are all in TW_STR32 (TWAIN data type for a string).
|
|
//
|
|
|
|
UINT Len = 0;
|
|
memset(szTempString,0,sizeof(szTempString));
|
|
hr = m_pDevice->GetDeviceDesc(szTempString,sizeof(szTempString),&Len);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// Add "WIA-" to beginning of ProductName string, to separate
|
|
// TWAIN installed data sources, from WIA data sources
|
|
//
|
|
|
|
AddWIAPrefixToString(szTempString,sizeof(szTempString));
|
|
Len += lstrlen(WIA_STR); // add prefix size to length
|
|
|
|
//
|
|
// set ProductName in TW_IDENTITY structure
|
|
//
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szTempString, Len + 1,m_dsIdentity.ProductName,
|
|
(sizeof(m_dsIdentity.ProductName)/sizeof(m_dsIdentity.ProductName[0])),NULL, NULL);
|
|
|
|
Len = 0;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::IWiaDataSrc(), GetDeviceDesc failed"));
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
memset(szTempString,0,sizeof(szTempString));
|
|
hr = m_pDevice->GetDeviceVendorName(szTempString,sizeof(szTempString),&Len);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// set Manufacturer in TW_IDENTITY structure
|
|
//
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szTempString, Len + 1,m_dsIdentity.Manufacturer,
|
|
(sizeof(m_dsIdentity.Manufacturer)/sizeof(m_dsIdentity.Manufacturer[0])),NULL, NULL);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::IWiaDataSrc(), GetDeviceVendorName failed"));
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// ANSI specific
|
|
//
|
|
|
|
memset(szTempString,0,sizeof(szTempString));
|
|
hr = m_pDevice->GetDeviceDesc(szTempString,sizeof(szTempString),NULL);
|
|
if (SUCCEEDED(hr) ) {
|
|
|
|
//
|
|
// Add "WIA-" to beginning of ProductName string, to separate
|
|
// TWAIN installed data sources, from WIA data sources
|
|
//
|
|
|
|
AddWIAPrefixToString(szTempString,sizeof(szTempString));
|
|
|
|
//
|
|
// set ProductName in TW_IDENTITY structure
|
|
//
|
|
|
|
strncpy(m_dsIdentity.ProductName,szTempString,sizeof(TW_STR32) - 1);
|
|
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::IWiaDataSrc(),GetDeviceDesc failed"));
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
memset(szTempString,0,sizeof(szTempString));
|
|
hr = m_pDevice->GetDeviceVendorName(szTempString,sizeof(szTempString),NULL);
|
|
if (SUCCEEDED(hr) ) {
|
|
|
|
//
|
|
// set Manufacturer in TW_IDENTITY structure
|
|
//
|
|
|
|
strncpy(m_dsIdentity.Manufacturer,szTempString,sizeof(TW_STR32) - 1);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::IWiaDataSrc(),GetDeviceVendorName failed"));
|
|
}
|
|
#endif
|
|
}
|
|
return(SUCCEEDED(hr)) ? TWRC_SUCCESS : TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::AddWIAPrefixToString(LPTSTR szString,UINT uSize)
|
|
{
|
|
TCHAR szTempBuffer[512];
|
|
memset(szTempBuffer,0,sizeof(szTempBuffer));
|
|
_sntprintf(szTempBuffer,(sizeof(szTempBuffer)/sizeof(szTempBuffer[0])),TEXT("%s%s"),WIA_STR,szString);
|
|
szTempBuffer[(sizeof(szTempBuffer)/sizeof(szTempBuffer[0])) - 1] = 0;
|
|
|
|
//
|
|
// copy, and truncate New string to TWAIN's required
|
|
// restricted size.
|
|
//
|
|
|
|
memset(szString,0,uSize);
|
|
|
|
#ifdef UNICODE
|
|
wcsncpy(szString,szTempBuffer,(sizeof(TW_STR32) - 1));
|
|
#else
|
|
strncpy(szString,szTempBuffer,(sizeof(TW_STR32) - 1));
|
|
#endif
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::DSEntry(pTW_IDENTITY pOrigin,TW_UINT32 DG,TW_UINT16 DAT,
|
|
TW_UINT16 MSG,TW_MEMREF pData)
|
|
{
|
|
TWAIN_MSG twMsg;
|
|
twMsg.AppId = pOrigin;
|
|
twMsg.DG = DG;
|
|
twMsg.DAT = DAT;
|
|
twMsg.MSG = MSG;
|
|
twMsg.pData = pData;
|
|
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (MSG_PROCESSEVENT == MSG) {
|
|
//
|
|
// Since we rely on WIA UI to provide the user interface and since
|
|
// the WIA UI is a modal dialog box(meaning it has its own
|
|
// messge loop), every event we receive here is not DS event.
|
|
//
|
|
|
|
//((TW_EVENT*)twMsg.pData)->TWMessage = MSG_NULL;
|
|
|
|
twRc = TWRC_NOTDSEVENT;
|
|
} else {
|
|
//
|
|
// Dispatch message based on group
|
|
//
|
|
switch (twMsg.DG) {
|
|
case DG_CONTROL:
|
|
twRc = DispatchControlMsg(&twMsg);
|
|
break;
|
|
case DG_IMAGE:
|
|
twRc = DispatchImageMsg(&twMsg);
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
break;
|
|
}
|
|
DBG_TRC(("Sent to TWAIN Application, DG = %X, DT = %X, MSG = %X, ( TWRC = %X, TWCC = %X)",DG,DAT,MSG,twRc,m_twStatus.ConditionCode));
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
#ifdef _USE_NONSPRINTF_CONVERSION
|
|
|
|
float CWiaDataSrc::Fix32ToFloat(TW_FIX32 fix32)
|
|
{
|
|
float ffloat = 0.0f;
|
|
|
|
//
|
|
// TWAIN spec implementation
|
|
//
|
|
|
|
ffloat = (float)fix32.Whole + (float)fix32.Frac / 65536.0f;
|
|
|
|
/*
|
|
|
|
//
|
|
// original implementation
|
|
//
|
|
|
|
int iexp = 1;
|
|
int frac = fix32.Frac;
|
|
while(frac/10 > 0){
|
|
iexp++;
|
|
frac = (frac/10);
|
|
}
|
|
ffloat = (float)fix32.Whole + (float) ( (float) fix32.Frac / (float) pow(10,iexp));
|
|
|
|
*/
|
|
|
|
return ffloat;
|
|
}
|
|
|
|
TW_FIX32 CWiaDataSrc::FloatToFix32(float ffloat)
|
|
{
|
|
TW_FIX32 fix32;
|
|
memset(&fix32,0,sizeof(fix32));
|
|
|
|
//
|
|
// TWAIN spec implementation
|
|
//
|
|
|
|
TW_INT32 value = (TW_INT32) (ffloat * 65536.0f + 0.5f);
|
|
fix32.Whole = (TW_INT16)(value >> 16);
|
|
fix32.Frac = (TW_UINT16)(value & 0x0000ffffL);
|
|
|
|
/*
|
|
|
|
//
|
|
// original implementation
|
|
//
|
|
|
|
fix32.Whole = (TW_INT16)ffloat;
|
|
//float fVal = -((float)fix32.Whole - ffloat);
|
|
float fVal = (ffloat - (float)fix32.Whole);
|
|
fVal = (fVal * 100000.0f);
|
|
fix32.Frac = (TW_UINT16)(fVal);
|
|
*/
|
|
|
|
return fix32;
|
|
}
|
|
|
|
#else // _USE_NONSPRINTF_CONVERSION
|
|
|
|
TW_FIX32 CWiaDataSrc::FloatToFix32(float f)
|
|
{
|
|
char fstr[64];
|
|
memset(fstr,0,sizeof(fstr));
|
|
char *p = NULL;
|
|
TW_FIX32 f32;
|
|
memset(&f32,0,sizeof(f32));
|
|
|
|
sprintf(fstr, "%f", f);
|
|
p = strchr(fstr, '.');
|
|
if (p != NULL) {
|
|
*p = '\0';
|
|
f32.Whole = (TW_INT16)atoi(fstr);
|
|
f32.Frac = (TW_UINT16)atoi(p + 1);
|
|
}
|
|
return f32;
|
|
}
|
|
|
|
float CWiaDataSrc::Fix32ToFloat(TW_FIX32 fix32)
|
|
{
|
|
|
|
// (full precision)
|
|
char fstr[64];
|
|
memset(fstr,0,sizeof(fstr));
|
|
float fReturnValue = 0.0f;
|
|
sprintf(fstr,"%d.%d",fix32.Whole,fix32.Frac);
|
|
sscanf(fstr,"%f",&fReturnValue);
|
|
|
|
// original (loses precision)
|
|
// fReturnValue = (float)fix32.Whole + (float)(fix32.Frac / 65536.0);
|
|
|
|
return fReturnValue;
|
|
}
|
|
#endif // _USE_NONSPRINTF_CONVERSION
|
|
|
|
void CWiaDataSrc::NotifyCloseReq()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::NotifyCloseReq());
|
|
if(m_DSM.Notify(&m_dsIdentity, &m_AppIdentity,
|
|
(TW_UINT32)DG_CONTROL, DAT_NULL,MSG_CLOSEDSREQ, (TW_MEMREF)NULL)){
|
|
DBG_TRC(("CWiaDataSrc::NotifyCloseReq(), MSG_CLOSEDSREQ is sent to application"));
|
|
} else {
|
|
DBG_WRN(("CWiaDataSrc::NotifyCloseReq(), could not notify application for MSG_CLOSEDSREQ"));
|
|
}
|
|
}
|
|
|
|
void CWiaDataSrc::NotifyXferReady()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::NotifyXferReady());
|
|
if (m_DSM.Notify(&m_dsIdentity,&m_AppIdentity,
|
|
(TW_UINT32)DG_CONTROL,DAT_NULL,MSG_XFERREADY,(TW_MEMREF)NULL)) {
|
|
|
|
DBG_TRC(("CWiaDataSrc::NotifyXferReady(), MSG_XFERREADY is sent to application"));
|
|
|
|
//
|
|
// transition to STATE_6
|
|
//
|
|
|
|
SetTWAINState(DS_STATE_6);
|
|
|
|
} else {
|
|
DBG_WRN(("CWiaDataSrc::NotifyXferReady(), could not notify application for MSG_XFERREADY"));
|
|
}
|
|
}
|
|
|
|
void CWiaDataSrc::ResetMemXfer()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::ResetMemXfer());
|
|
if (IS_VALID_HANDLE(m_hMemXferBits)) {
|
|
if (m_pMemXferBits) {
|
|
if (GlobalUnlock(m_hMemXferBits)) {
|
|
m_pMemXferBits = NULL;
|
|
}
|
|
}
|
|
// Now free block always
|
|
GlobalFree(m_hMemXferBits);
|
|
}
|
|
m_hMemXferBits = NULL;
|
|
m_hCachedImageData = NULL;
|
|
m_LinesTransferred = 0;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::DispatchControlMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (!ptwMsg) {
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
return TWRC_FAILURE;
|
|
}
|
|
switch (ptwMsg->DAT) {
|
|
case DAT_IDENTITY:
|
|
twRc = OnIdentityMsg(ptwMsg);
|
|
break;
|
|
case DAT_USERINTERFACE:
|
|
twRc = OnUserInterfaceMsg(ptwMsg);
|
|
break;
|
|
case DAT_CAPABILITY:
|
|
twRc = OnCapabilityMsg(ptwMsg);
|
|
break;
|
|
case DAT_STATUS:
|
|
twRc = OnStatusMsg(ptwMsg);
|
|
break;
|
|
case DAT_PENDINGXFERS:
|
|
twRc = OnPendingXfersMsg(ptwMsg);
|
|
break;
|
|
case DAT_SETUPMEMXFER:
|
|
twRc = OnSetupMemXferMsg(ptwMsg);
|
|
break;
|
|
case DAT_SETUPFILEXFER:
|
|
twRc = OnSetupFileXferMsg(ptwMsg);
|
|
break;
|
|
case DAT_XFERGROUP:
|
|
twRc = OnXferGroupMsg(ptwMsg);
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::DispatchImageMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (!ptwMsg) {
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
switch (ptwMsg->DAT) {
|
|
case DAT_IMAGEINFO:
|
|
twRc = OnImageInfoMsg(ptwMsg);
|
|
break;
|
|
case DAT_IMAGELAYOUT:
|
|
twRc = OnImageLayoutMsg(ptwMsg);
|
|
break;
|
|
case DAT_IMAGEMEMXFER:
|
|
twRc = OnImageMemXferMsg(ptwMsg);
|
|
break;
|
|
case DAT_IMAGENATIVEXFER:
|
|
twRc = OnImageNativeXferMsg(ptwMsg);
|
|
break;
|
|
case DAT_IMAGEFILEXFER:
|
|
twRc = OnImageFileXferMsg(ptwMsg);
|
|
break;
|
|
case DAT_PALETTE8:
|
|
twRc = OnPalette8Msg(ptwMsg);
|
|
break;
|
|
case DAT_GRAYRESPONSE:
|
|
twRc = OnGrayResponseMsg(ptwMsg);
|
|
break;
|
|
case DAT_RGBRESPONSE:
|
|
twRc = OnRGBResponseMsg(ptwMsg);
|
|
break;
|
|
case DAT_CIECOLOR:
|
|
twRc = OnCIEColorMsg(ptwMsg);;
|
|
break;
|
|
case DAT_JPEGCOMPRESSION:
|
|
twRc = OnJPEGCompressionMsg(ptwMsg);
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnPalette8Msg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnPalette8Msg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_4:
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
case DS_STATE_7:
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_GET:
|
|
|
|
// TWPA_RGB - color palette
|
|
// TWPA_GRAY - grayscale palette
|
|
// TWPA_CMY - CMY palette
|
|
|
|
((TW_PALETTE8 *)ptwMsg->pData)->NumColors = 0;
|
|
((TW_PALETTE8 *)ptwMsg->pData)->PaletteType = TWPA_RGB;
|
|
break;
|
|
case MSG_GETDEFAULT:
|
|
case MSG_RESET:
|
|
case MSG_SET:
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
}
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnSetupMemXferMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnSetupMemXferMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
TW_SETUPMEMXFER *pMemSetup = (TW_SETUPMEMXFER *)ptwMsg->pData;
|
|
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_4:
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
if (pMemSetup) {
|
|
pMemSetup->MinBufSize = MIN_MEMXFER_SIZE;
|
|
pMemSetup->MaxBufSize = MAX_MEMXFER_SIZE;
|
|
pMemSetup->Preferred = PREFERRED_MEMXFER_SIZE;
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_BADVALUE;
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
}
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnSetupFileXferMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnSetupFileXferMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
CCap *pImageXferCap = NULL;
|
|
TW_SETUPFILEXFER *pFileXfer = NULL;
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_4:
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
pFileXfer = (TW_SETUPFILEXFER *)ptwMsg->pData;
|
|
pImageXferCap = FindCap(ICAP_IMAGEFILEFORMAT);
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_GET:
|
|
case MSG_GETDEFAULT:
|
|
case MSG_GETCURRENT:
|
|
if (pImageXferCap) {
|
|
pFileXfer->Format = (TW_UINT16)pImageXferCap->GetCurrent();
|
|
pFileXfer->VRefNum = 0;
|
|
pFileXfer->FileName[0] = 0;
|
|
strcpy(pFileXfer->FileName, m_FileXferName);
|
|
} else {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
}
|
|
break;
|
|
case MSG_SET:
|
|
{
|
|
strcpy(m_FileXferName, pFileXfer->FileName);
|
|
pImageXferCap->SetCurrent((VOID*)&pFileXfer->Format);
|
|
}
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
DSError();
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnXferGroupMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnXferGroupMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_4:
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_GET:
|
|
case MSG_GETDEFAULT:
|
|
case MSG_GETCURRENT:
|
|
case MSG_RESET:
|
|
*((TW_UINT16 *)ptwMsg->pData) = DG_IMAGE;
|
|
break;
|
|
case MSG_SET:
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnImageInfoMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnImageInfoMsg());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
TW_IMAGEINFO *ptwImageInfo = NULL;
|
|
if (DS_STATE_6 == GetTWAINState()) {
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
ptwImageInfo = (TW_IMAGEINFO *)ptwMsg->pData;
|
|
HRESULT hr = S_OK;
|
|
hr = m_pDevice->GetImageInfo(m_pCurrentIWiaItem, &m_MemoryTransferInfo);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
ptwImageInfo->ImageWidth = (TW_INT32)m_MemoryTransferInfo.mtiWidthPixels;
|
|
ptwImageInfo->ImageLength = (TW_INT32)m_MemoryTransferInfo.mtiHeightPixels;
|
|
ptwImageInfo->BitsPerPixel = (TW_INT16)m_MemoryTransferInfo.mtiBitsPerPixel;
|
|
ptwImageInfo->SamplesPerPixel = (TW_INT16)m_MemoryTransferInfo.mtiNumChannels;
|
|
ptwImageInfo->Planar = (TW_BOOL)m_MemoryTransferInfo.mtiPlanar;
|
|
|
|
//
|
|
// adjust height for unknown length acquisitions
|
|
//
|
|
|
|
if(ptwImageInfo->ImageLength == 0){
|
|
DBG_WRN(("CWiaDataSrc::OnImageInfoMsg(), Possible unknown length device detected..checking cached height value"));
|
|
ptwImageInfo->ImageLength = m_ImageHeight;
|
|
if(ptwImageInfo->ImageLength == 0){
|
|
DBG_WRN(("CWiaDataSrc::OnImageInfoMsg(), no cached height available, defaulting to -1 (ICAP_UNDEFINEDIMAGESIZE support only)"));
|
|
ptwImageInfo->ImageLength = -1; // unknown page length (only valid if TWAIN applications support ICAP_UNDEFINEDIMAGESIZE)
|
|
} else {
|
|
DBG_TRC(("CWiaDataSrc::OnImageInfoMsg(), new height = %d",ptwImageInfo->ImageLength));
|
|
}
|
|
}
|
|
|
|
//
|
|
// set PixelType to corresponding TWAIN pixel type
|
|
//
|
|
|
|
switch(m_MemoryTransferInfo.mtiDataType) {
|
|
case WIA_DATA_THRESHOLD:
|
|
ptwImageInfo->PixelType = TWPT_BW;
|
|
break;
|
|
case WIA_DATA_GRAYSCALE:
|
|
ptwImageInfo->PixelType = TWPT_GRAY;
|
|
break;
|
|
case WIA_DATA_COLOR:
|
|
default:
|
|
ptwImageInfo->PixelType = TWPT_RGB;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// set compression to NONE
|
|
//
|
|
|
|
ptwImageInfo->Compression = TWCP_NONE;
|
|
|
|
//
|
|
// Unit conversion.......
|
|
//
|
|
|
|
ptwImageInfo->XResolution = FloatToFix32((float)m_MemoryTransferInfo.mtiXResolution);
|
|
ptwImageInfo->YResolution = FloatToFix32((float)m_MemoryTransferInfo.mtiYResolution);
|
|
|
|
twRc = TWRC_SUCCESS;
|
|
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_OPERATIONERROR;
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
|
|
if (TWRC_SUCCESS == twRc) {
|
|
DBG_TRC(("CWiaDataSrc::OnImageInfoMsg(), Reported Image Information from data source"));
|
|
DBG_TRC(("XResolution = %d.%d",ptwImageInfo->XResolution.Whole,ptwImageInfo->XResolution.Frac));
|
|
DBG_TRC(("YResolution = %d.%d",ptwImageInfo->YResolution.Whole,ptwImageInfo->YResolution.Frac));
|
|
DBG_TRC(("ImageWidth = %d",ptwImageInfo->ImageWidth));
|
|
DBG_TRC(("ImageLength = %d",ptwImageInfo->ImageLength));
|
|
DBG_TRC(("SamplesPerPixel = %d",ptwImageInfo->SamplesPerPixel));
|
|
|
|
memset(ptwImageInfo->BitsPerSample,0,sizeof(ptwImageInfo->BitsPerSample));
|
|
|
|
if (ptwImageInfo->BitsPerPixel < 24) {
|
|
ptwImageInfo->BitsPerSample[0] = ptwImageInfo->BitsPerPixel;
|
|
} else {
|
|
for (int i = 0; i < ptwImageInfo->SamplesPerPixel; i++) {
|
|
ptwImageInfo->BitsPerSample[i] = (ptwImageInfo->BitsPerPixel/ptwImageInfo->SamplesPerPixel);
|
|
}
|
|
}
|
|
// (bpp / spp) = bps
|
|
DBG_TRC(("BitsPerSample = [%d],[%d],[%d],[%d],[%d],[%d],[%d],[%d]",ptwImageInfo->BitsPerSample[0],
|
|
ptwImageInfo->BitsPerSample[1],
|
|
ptwImageInfo->BitsPerSample[2],
|
|
ptwImageInfo->BitsPerSample[3],
|
|
ptwImageInfo->BitsPerSample[4],
|
|
ptwImageInfo->BitsPerSample[5],
|
|
ptwImageInfo->BitsPerSample[6],
|
|
ptwImageInfo->BitsPerSample[7]));
|
|
DBG_TRC(("BitsPerPixel = %d",ptwImageInfo->BitsPerPixel));
|
|
DBG_TRC(("Planar = %d",ptwImageInfo->Planar));
|
|
DBG_TRC(("PixelType = %d",ptwImageInfo->PixelType));
|
|
DBG_TRC(("Compression = %d",ptwImageInfo->Compression));
|
|
}
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
if (TWRC_SUCCESS != twRc) {
|
|
DSError();
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnImageLayoutMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnGrayResponseMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnRGBResponseMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnCIEColorMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnJPEGCompressionMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnIdentityMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnIdentityMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
if (ptwMsg) {
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_OPENDS:
|
|
|
|
#ifdef DEBUG_ME
|
|
MessageBox(NULL,TEXT("MSG_OPENDS - Attach me to a debugger"),TEXT("Attach me to a debugger"),MB_OK);
|
|
#endif
|
|
|
|
twRc = OpenDS(ptwMsg);
|
|
break;
|
|
case MSG_CLOSEDS:
|
|
twRc = CloseDS(ptwMsg);
|
|
break;
|
|
case MSG_GET:
|
|
if (!IsBadWritePtr(ptwMsg->pData, sizeof(TW_IDENTITY))) {
|
|
*(TW_IDENTITY*)ptwMsg->pData = m_dsIdentity;
|
|
DBG_TRC(("CWiaDataSrc::OnIdentityMsg(), Reported TW_IDENTITY from data source"));
|
|
DBG_TRC(("Id = %d",m_dsIdentity.Id));
|
|
DBG_TRC(("Manufacturer = %s",m_dsIdentity.Manufacturer));
|
|
DBG_TRC(("ProductFamily = %s",m_dsIdentity.ProductFamily));
|
|
DBG_TRC(("ProductName = %s",m_dsIdentity.ProductName));
|
|
DBG_TRC(("ProtocolMajor = %d",m_dsIdentity.ProtocolMajor));
|
|
DBG_TRC(("ProtocolMinor = %d",m_dsIdentity.ProtocolMinor));
|
|
DBG_TRC(("SupportedGrps = %d",m_dsIdentity.SupportedGroups));
|
|
DBG_TRC(("Ver Country = %d",m_dsIdentity.Version.Country));
|
|
DBG_TRC(("Ver Info = %s",m_dsIdentity.Version.Info));
|
|
DBG_TRC(("Ver Language = %d",m_dsIdentity.Version.Language));
|
|
DBG_TRC(("Ver MajorNum = %d",m_dsIdentity.Version.MajorNum));
|
|
DBG_TRC(("Ver MinorNum = %d",m_dsIdentity.Version.MinorNum));
|
|
twRc = TWRC_SUCCESS;
|
|
} else {
|
|
twRc = TWCC_BADVALUE;
|
|
}
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnUserInterfaceMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnUserInterfaceMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_ENABLEDS:
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
case DS_STATE_7:
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
break;
|
|
default:
|
|
twRc = EnableDS((TW_USERINTERFACE*)ptwMsg->pData);
|
|
break;
|
|
}
|
|
break;
|
|
case MSG_DISABLEDS:
|
|
twRc = DisableDS((TW_USERINTERFACE*)ptwMsg->pData);
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnCapabilityMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnCapabilityMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
TW_UINT16 twCC = TWCC_SUCCESS;
|
|
|
|
TW_CAPABILITY *ptwCap = (TW_CAPABILITY *)ptwMsg->pData;
|
|
if (!ptwCap) {
|
|
m_twStatus.ConditionCode = TWCC_BADCAP;
|
|
return TWRC_FAILURE;
|
|
}
|
|
if (CAP_SUPPORTEDCAPS == ptwCap->Cap) {
|
|
switch(ptwMsg->MSG) {
|
|
case MSG_SET:
|
|
case MSG_RESET:
|
|
{
|
|
//
|
|
// MSG_SET, MSG_RESET shouldn't be able to be called on CAP_SUPPORTEDCAPS!!
|
|
//
|
|
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_CAPBADOPERATION;
|
|
return twRc;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// get number of PRIVATE TWAIN capabilities from WIA driver
|
|
// and add them to our CAP_SUPPORTEDCAPS list.
|
|
//
|
|
|
|
LONG lNumPrivateCaps = 0;
|
|
LONG *pPrivateCapArray = NULL;
|
|
lNumPrivateCaps = GetPrivateSupportedCapsFromWIADevice(&pPrivateCapArray);
|
|
|
|
ptwCap->ConType = TWON_ARRAY;
|
|
ptwCap->hContainer = GlobalAlloc(GHND, sizeof(TW_ARRAY) + sizeof(TW_UINT16) * (m_NumCaps + lNumPrivateCaps) );
|
|
if (ptwCap->hContainer) {
|
|
TW_ARRAY *pCapIdArray = (TW_ARRAY *) GlobalLock(ptwCap->hContainer);
|
|
if (pCapIdArray) {
|
|
TW_UINT32 i = 0;
|
|
pCapIdArray->ItemType = TWTY_UINT16;
|
|
TW_UINT16 *ItemList;
|
|
ItemList = (TW_UINT16 *)pCapIdArray->ItemList;
|
|
|
|
//
|
|
// fill in TWAIN compat layer's supported CAPS first
|
|
//
|
|
|
|
for (i = 0; i < m_NumCaps; i++) {
|
|
ItemList[i] = m_CapList[i].GetCapId();
|
|
}
|
|
|
|
//
|
|
// fill in WIA driver's private supported CAPS next
|
|
//
|
|
|
|
int PrivateCapIndex = 0;
|
|
for(i = m_NumCaps; i < (m_NumCaps + lNumPrivateCaps);i++){
|
|
ItemList[i] = (TW_UINT16)pPrivateCapArray[PrivateCapIndex];
|
|
DBG_TRC(("(%d) Private Capability ID reported = %x",(PrivateCapIndex + 1), ItemList[i]));
|
|
PrivateCapIndex++;
|
|
}
|
|
|
|
//
|
|
// finally set NumItems
|
|
//
|
|
|
|
pCapIdArray->NumItems = (m_NumCaps + lNumPrivateCaps);
|
|
|
|
GlobalUnlock(ptwCap->hContainer);
|
|
} else {
|
|
GlobalFree(ptwCap->hContainer);
|
|
ptwCap->hContainer = NULL;
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_LOWMEMORY;
|
|
}
|
|
} else {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_LOWMEMORY;
|
|
}
|
|
|
|
//
|
|
// delete Private capability array, if it was allocated
|
|
//
|
|
|
|
if(pPrivateCapArray){
|
|
GlobalFree(pPrivateCapArray);
|
|
pPrivateCapArray = NULL;
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
CCap *pCap = FindCap(ptwCap->Cap);
|
|
if (!pCap) {
|
|
DBG_TRC(("Couldn't find the CCap object for CAP ID %x in TWAIN Compat layer CAP list, try WIA driver's private TWAIN cap list", ptwCap->Cap));
|
|
if (m_pDevice->TwainCapabilityPassThrough()) {
|
|
return OnPrivateCapabilityMsg(ptwMsg);
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_BADCAP;
|
|
return TWRC_FAILURE;
|
|
}
|
|
}
|
|
switch (ptwMsg->MSG) {
|
|
case MSG_GET:
|
|
if(ptwCap->Cap == ICAP_IMAGEDATASET)
|
|
twCC = pCap->GetCurrent(ptwCap);
|
|
else
|
|
twCC = pCap->Get(ptwCap);
|
|
break;
|
|
case MSG_GETDEFAULT:
|
|
twCC = pCap->GetDefault(ptwCap);
|
|
break;
|
|
case MSG_GETCURRENT:
|
|
twCC = pCap->GetCurrent(ptwCap);
|
|
break;
|
|
case MSG_SET:
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_7:
|
|
twCC = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
break;
|
|
default:
|
|
twCC = SetCapability(pCap, ptwCap);
|
|
break;
|
|
}
|
|
break;
|
|
case MSG_RESET:
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_5:
|
|
case DS_STATE_6:
|
|
case DS_STATE_7:
|
|
twCC = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
break;
|
|
default:
|
|
{
|
|
// ptwCap->Cap,
|
|
// ptwCap->ConType,
|
|
// ptwCap->hContainer);
|
|
|
|
twCC = pCap->Reset();
|
|
|
|
//
|
|
// According to the TWAIN spec, a MSG_RESET can be sent down meaning more than just
|
|
// RESET!!! It is stated that it can mean GET_DEFAULT/CURRENT, and RESET in a single call.
|
|
// Applications choose to ignore the value returned if they don't care, But if they
|
|
// attempt to read the value as the DEFAULT/CURRENT value...it must be set correctly in the
|
|
// container.
|
|
//
|
|
|
|
//
|
|
// fill the container with the current value, after the
|
|
// RESET call.
|
|
//
|
|
|
|
twCC = pCap->GetCurrent(ptwCap);
|
|
|
|
// ptwCap->ConType);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
twCC = TWCC_BADPROTOCOL;
|
|
DSError();
|
|
break;
|
|
}
|
|
|
|
m_twStatus.ConditionCode = twCC;
|
|
|
|
if (TWCC_SUCCESS != twCC) {
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnPrivateCapabilityMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnPrivateCapabilityMsg());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADCAP;
|
|
|
|
if (ptwMsg) {
|
|
|
|
if (ptwMsg->MSG == MSG_SET) {
|
|
TW_CAPABILITY *ptwCap = (TW_CAPABILITY *)ptwMsg->pData;
|
|
if (ptwCap) {
|
|
if ((NULL == ptwCap->hContainer)||(INVALID_HANDLE_VALUE == ptwCap->hContainer)) {
|
|
return twRc;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pCurrentIWiaItem) {
|
|
|
|
//
|
|
// Get the IWiaItemExtras Interface
|
|
//
|
|
|
|
IWiaItemExtras *pIWiaItemExtras = NULL;
|
|
HRESULT hr = m_pCurrentIWiaItem->QueryInterface(IID_IWiaItemExtras,(void **)&pIWiaItemExtras);
|
|
if (S_OK == hr) {
|
|
|
|
//
|
|
// we have an IWiaItemExtras Interface, so lets talk to the WIA device about
|
|
// the capability message
|
|
//
|
|
|
|
TW_CAPABILITY *ptwCap = (TW_CAPABILITY *)ptwMsg->pData;
|
|
if (ptwCap) {
|
|
|
|
//
|
|
// Initialize the common header
|
|
//
|
|
|
|
TWAIN_CAPABILITY twCap;
|
|
twCap.lSize = sizeof(twCap); // size of TWAIN_CAPABILITY structure
|
|
twCap.lMSG = ptwMsg->MSG; // TWAIN message
|
|
twCap.lCapID = ptwCap->Cap; // TWAIN capability ID
|
|
twCap.lConType = ptwCap->ConType; // TWAIN container type
|
|
twCap.lCC = TWCC_BADCAP; // TWAIN return code
|
|
twCap.lRC = TWRC_FAILURE; // TWAIN condition code
|
|
twCap.lDataSize= 0; // TWAIN capability data size
|
|
twCap.Data[0] = 0; // TWAIN capability data (first byte)
|
|
|
|
DBG_TRC(("== Private TWAIN_CAPABILITY data Header =="));
|
|
DBG_TRC(("twCap.lSize = %d", twCap.lSize));
|
|
DBG_TRC(("twCap.lMSG = %d", twCap.lMSG));
|
|
DBG_TRC(("twCap.lCapID = %x", twCap.lCapID));
|
|
DBG_TRC(("twCap.lConType = %d", twCap.lConType));
|
|
DBG_TRC(("twCap.lCC = %d", twCap.lCC));
|
|
DBG_TRC(("twCap.lRC = %d", twCap.lRC));
|
|
|
|
DWORD dwInDataSize = 0;
|
|
DWORD dwOutDataSize = 0;
|
|
DWORD dwContainerSize = 0;
|
|
DWORD dwActualOutDataSize = 0;
|
|
|
|
BYTE *pInData = NULL;
|
|
BYTE *pOutData = NULL;
|
|
BYTE *pContainerData = NULL;
|
|
TWAIN_CAPABILITY *pHeader = NULL;
|
|
|
|
//
|
|
// Depending on the Message type GET ot SET we do different things
|
|
//
|
|
|
|
//
|
|
// For a SET or RESET message, we just send the IN buffer, with an OUT buffer
|
|
// containing the header.
|
|
//
|
|
|
|
if ((ptwMsg->MSG == MSG_SET) ||
|
|
(ptwMsg->MSG == MSG_RESET)) {
|
|
|
|
dwContainerSize = 0;
|
|
|
|
if (ptwMsg->MSG == MSG_SET) {
|
|
|
|
//
|
|
// only check container size, when the TWAIN message is a MSG_SET
|
|
// MSG_RESET operations do not have containers attached.
|
|
//
|
|
|
|
dwContainerSize = (DWORD)GlobalSize(ptwCap->hContainer);
|
|
}
|
|
|
|
dwInDataSize = dwContainerSize + sizeof(twCap);
|
|
dwOutDataSize = sizeof(twCap);
|
|
dwActualOutDataSize = dwOutDataSize;
|
|
|
|
twCap.lDataSize = dwContainerSize;
|
|
DBG_TRC(("twCap.lDataSize = %d", twCap.lDataSize));
|
|
|
|
DBG_TRC(("== Processing MSG_SET or MSG_RESET Capability Message =="));
|
|
DBG_TRC(("dwInDataSize = %d",dwInDataSize));
|
|
DBG_TRC(("dwOutDataSize = %d",dwOutDataSize));
|
|
DBG_TRC(("dwActualOutDataSize = %d",dwActualOutDataSize));
|
|
DBG_TRC(("dwContainerSize = %d",dwContainerSize));
|
|
|
|
//
|
|
// allocate IN buffer and write TWAIN_CAPABILITY header
|
|
//
|
|
|
|
if (TWRC_SUCCESS == AllocatePrivateCapBuffer(&twCap,&pInData,dwInDataSize)) {
|
|
|
|
if (ptwMsg->MSG == MSG_SET) {
|
|
|
|
//
|
|
// copy TWAIN container data to IN buffer
|
|
//
|
|
|
|
if (TWRC_SUCCESS == CopyContainerToPrivateCapBuffer(pInData,ptwCap->hContainer)) {
|
|
|
|
//
|
|
// container data was copied to IN buffer
|
|
//
|
|
|
|
DBG_TRC(("Container data was successfully copied, we are processing a MSG_SET"));
|
|
|
|
} else {
|
|
|
|
//
|
|
// could not copy TWAIN container data into private capability IN buffer
|
|
//
|
|
|
|
DBG_ERR(("could not copy TWAIN container data into private capability IN buffer"));
|
|
|
|
if(pInData){
|
|
GlobalFree(pInData);
|
|
pInData = NULL;
|
|
}
|
|
|
|
return twRc; // return here, becuase we can not continue
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// no container data needs to be copied
|
|
//
|
|
|
|
DBG_TRC(("No Container data was copied, because we are processing a MSG_RESET"));
|
|
|
|
}
|
|
|
|
//
|
|
// allocate OUT buffer and write TWAIN_CAPABILITY header
|
|
//
|
|
|
|
if (TWRC_SUCCESS == AllocatePrivateCapBuffer(&twCap,&pOutData,dwOutDataSize)) {
|
|
hr = pIWiaItemExtras->Escape(ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize);
|
|
if (S_OK == hr) {
|
|
pHeader = (TWAIN_CAPABILITY*)pOutData;
|
|
DBG_TRC(("== Returned TWAIN_CAPABILITY data Header from WIA device =="));
|
|
DBG_TRC(("pHeader->lSize = %d", pHeader->lSize));
|
|
DBG_TRC(("pHeader->lMSG = %d", pHeader->lMSG));
|
|
DBG_TRC(("pHeader->lCapID = %x", pHeader->lCapID));
|
|
DBG_TRC(("pHeader->lConType = %d", pHeader->lConType));
|
|
DBG_TRC(("pHeader->lCC = %d", pHeader->lCC));
|
|
DBG_TRC(("pHeader->lRC = %d", pHeader->lRC));
|
|
DBG_TRC(("pHeader->lDataSize = %d", pHeader->lDataSize));
|
|
|
|
twRc = (TW_UINT16)pHeader->lRC;
|
|
m_twStatus.ConditionCode = (TW_UINT16)pHeader->lCC;
|
|
} else {
|
|
|
|
//
|
|
// pIWiaItemExtras->Escape call failed,
|
|
// a failure means that we do not respond with a success to the TWAIN application
|
|
//
|
|
|
|
DBG_ERR(("pIWiaItemExtras->Escape Failed"));
|
|
DBG_TRC(("Escape(code = %d, pInData = %p, dwInDataSize = %d, pOutData = %p, dwOutDataSize = %d,dwActualOutDataSize = %p)",
|
|
ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize));
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// could not allocate memory for private capability OUT buffer
|
|
//
|
|
|
|
DBG_ERR(("could not allocate memory for private capability OUT buffer"));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// could not allocate memory for private capability IN buffer
|
|
//
|
|
|
|
DBG_ERR(("could not allocate memory for private capability IN buffer"));
|
|
|
|
}
|
|
} else if ((ptwMsg->MSG == MSG_GET) ||
|
|
(ptwMsg->MSG == MSG_GETCURRENT) ||
|
|
(ptwMsg->MSG == MSG_GETDEFAULT)) {
|
|
|
|
dwContainerSize = 0;
|
|
dwInDataSize = sizeof(twCap);
|
|
dwOutDataSize = dwInDataSize;
|
|
dwActualOutDataSize = dwInDataSize;
|
|
|
|
twCap.lDataSize = dwContainerSize;
|
|
DBG_TRC(("twCap.lDataSize = %d", twCap.lDataSize));
|
|
|
|
DBG_TRC(("== Processing MSG_GET, MSG_GETCURRENT, or MSG_GETDEFAULT Capability Message =="));
|
|
DBG_TRC(("dwInDataSize = %d",dwInDataSize));
|
|
DBG_TRC(("dwOutDataSize = %d",dwOutDataSize));
|
|
DBG_TRC(("dwActualOutDataSize = %d",dwActualOutDataSize));
|
|
DBG_TRC(("dwContainerSize = %d",dwContainerSize));
|
|
|
|
//
|
|
// allocate IN buffer and write TWAIN_CAPABILITY header
|
|
//
|
|
|
|
if (TWRC_SUCCESS == AllocatePrivateCapBuffer(&twCap,&pInData,dwInDataSize)) {
|
|
|
|
//
|
|
// ask the WIA driver how large is the data, so
|
|
// we can allocate the proper OUT buffer
|
|
//
|
|
|
|
//
|
|
// allocate OUT buffer and write TWAIN_CAPABILITY header
|
|
//
|
|
|
|
if (TWRC_SUCCESS == AllocatePrivateCapBuffer(&twCap,&pOutData,dwOutDataSize)) {
|
|
|
|
hr = pIWiaItemExtras->Escape(ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize);
|
|
if (S_OK == hr) {
|
|
|
|
//
|
|
// make sure that the returned data is large enough to
|
|
// contain a proper header.
|
|
//
|
|
|
|
if (dwActualOutDataSize == dwInDataSize) {
|
|
|
|
pHeader = (TWAIN_CAPABILITY*)pOutData;
|
|
DBG_TRC(("== Returned TWAIN_CAPABILITY data Header from WIA device =="));
|
|
DBG_TRC(("pHeader->lSize = %d", pHeader->lSize));
|
|
DBG_TRC(("pHeader->lMSG = %d", pHeader->lMSG));
|
|
DBG_TRC(("pHeader->lCapID = %x", pHeader->lCapID));
|
|
DBG_TRC(("pHeader->lConType = %d", pHeader->lConType));
|
|
DBG_TRC(("pHeader->lCC = %d", pHeader->lCC));
|
|
DBG_TRC(("pHeader->lRC = %d", pHeader->lRC));
|
|
DBG_TRC(("pHeader->lDataSize = %d", pHeader->lDataSize));
|
|
|
|
if (pHeader->lDataSize > 0) {
|
|
|
|
//
|
|
// update common header data size from information returned
|
|
// to create OUT buffer header
|
|
//
|
|
|
|
twCap.lDataSize = pHeader->lDataSize;
|
|
|
|
//
|
|
// set new out data size to (data + header) size
|
|
//
|
|
|
|
dwOutDataSize = (pHeader->lDataSize + sizeof(twCap));
|
|
|
|
//
|
|
// update InBuffer header data size from the common header
|
|
//
|
|
|
|
pHeader = (TWAIN_CAPABILITY*)pInData;
|
|
pHeader->lDataSize = twCap.lDataSize;
|
|
|
|
//
|
|
// free old out buffer, before allocating new one
|
|
//
|
|
|
|
if (pOutData) {
|
|
GlobalFree(pOutData);
|
|
pOutData = NULL;
|
|
}
|
|
|
|
//
|
|
// allocate OUT buffer and write TWAIN_CAPABILITY header
|
|
//
|
|
|
|
if (TWRC_SUCCESS == AllocatePrivateCapBuffer(&twCap,&pOutData,dwOutDataSize)) {
|
|
hr = pIWiaItemExtras->Escape(ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize);
|
|
if (S_OK == hr) {
|
|
pHeader = (TWAIN_CAPABILITY*)pOutData;
|
|
DBG_TRC(("== Returned TWAIN_CAPABILITY data Header from WIA device =="));
|
|
DBG_TRC(("pHeader->lSize = %d", pHeader->lSize));
|
|
DBG_TRC(("pHeader->lMSG = %d", pHeader->lMSG));
|
|
DBG_TRC(("pHeader->lCapID = %x", pHeader->lCapID));
|
|
DBG_TRC(("pHeader->lConType = %d", pHeader->lConType));
|
|
DBG_TRC(("pHeader->lCC = %d", pHeader->lCC));
|
|
DBG_TRC(("pHeader->lRC = %d", pHeader->lRC));
|
|
DBG_TRC(("pHeader->lDataSize = %d", pHeader->lDataSize));
|
|
twRc = (TW_UINT16)pHeader->lRC;
|
|
m_twStatus.ConditionCode = (TW_UINT16)pHeader->lCC;
|
|
if (TWRC_SUCCESS == twRc) {
|
|
if (TWRC_SUCCESS == CopyPrivateCapBufferToContainer(&ptwCap->hContainer,pOutData,pHeader->lDataSize)) {
|
|
ptwCap->ConType = (TW_UINT16)pHeader->lConType;
|
|
} else {
|
|
|
|
//
|
|
// could not copy private capability buffer into TWAIN container data
|
|
//
|
|
|
|
DBG_ERR(("could not copy private capability buffer into TWAIN container data"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// WIA driver failed the TWAIN capability request, by returning a TWAIN failure
|
|
// return code in the OUT header.
|
|
//
|
|
|
|
DBG_ERR(("WIA driver failed the TWAIN capability request, by returning a TWAIN failure return code in the OUT header."));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// pIWiaItemExtras->Escape call failed, (sending passthrough operation)
|
|
// a failure means that we do not respond with a success to the TWAIN application
|
|
//
|
|
|
|
DBG_ERR(("pIWiaItemExtras->Escape Failed (sending passthrough operation)"));
|
|
DBG_TRC(("Escape(code = %d, pInData = %p, dwInDataSize = %d, pOutData = %p, dwOutDataSize = %d,dwActualOutDataSize = %d)",
|
|
ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
dwActualOutDataSize));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// could not allocate memory for private capability OUT buffer
|
|
//
|
|
|
|
DBG_ERR(("could not allocate memory for private capability OUT buffer"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// OUT buffer size returned from the WIA driver is too small to contain a
|
|
// proper header.
|
|
//
|
|
|
|
DBG_ERR(("OUT buffer size (%d) returned from the WIA driver is too small to contain data",pHeader->lDataSize));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// OUT buffer size returned from the WIA driver is too small to contain a
|
|
// proper header.
|
|
//
|
|
|
|
DBG_ERR(("OUT buffer size (%d) returned from the WIA driver is too small to contain a proper header",dwActualOutDataSize));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// pIWiaItemExtras->Escape call failed, (requesting OUT buffer size)
|
|
// a failure means that we do not respond with a success to the TWAIN application
|
|
//
|
|
|
|
DBG_ERR(("pIWiaItemExtras->Escape Failed (requesting OUT buffer size)"));
|
|
DBG_TRC(("Escape(code = %d, pInData = %p, dwInDataSize = %d, pOutData = %p, dwOutDataSize = %d,dwActualOutDataSize = %d)",
|
|
ESC_TWAIN_CAPABILITY,
|
|
pInData,
|
|
dwInDataSize,
|
|
pInData,
|
|
dwInDataSize,
|
|
dwActualOutDataSize));
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// could not allocate memory for private capability OUT buffer
|
|
//
|
|
|
|
DBG_ERR(("could not allocate memory for private capability OUT buffer"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// could not allocate memory for private capability IN buffer
|
|
//
|
|
|
|
DBG_ERR(("could not allocate memory for private capability IN buffer"));
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// free IN buffer
|
|
//
|
|
|
|
if (pInData) {
|
|
GlobalFree(pInData);
|
|
pInData = NULL;
|
|
}
|
|
|
|
//
|
|
// free OUT buffer
|
|
//
|
|
|
|
if (pOutData) {
|
|
GlobalFree(pOutData);
|
|
pOutData = NULL;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// could not get TWAIN capability from TWAIN message
|
|
//
|
|
|
|
DBG_ERR(("could not get TWAIN capability from TWAIN message"));
|
|
}
|
|
|
|
//
|
|
// release IWiaItemExtras Interface
|
|
//
|
|
|
|
if (pIWiaItemExtras) {
|
|
pIWiaItemExtras->Release();
|
|
pIWiaItemExtras = NULL;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// QI for IWiaItemExtras Failed
|
|
//
|
|
|
|
DBG_ERR(("QueryInterface for IWiaItemExtras Failed"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// no current item selected
|
|
//
|
|
|
|
DBG_ERR(("no current item selected for use"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// imcoming TWAIN capability is NULL
|
|
//
|
|
|
|
DBG_ERR(("incoming TWAIN capability is NULL"));
|
|
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::AllocatePrivateCapBuffer(TWAIN_CAPABILITY *pHeader, BYTE** ppBuffer, DWORD dwSize)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::AllocatePrivateCapBuffer());
|
|
if(dwSize < sizeof(TWAIN_CAPABILITY) || (!ppBuffer)|| (!pHeader)){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
*ppBuffer = (BYTE*)GlobalAlloc(GPTR,dwSize);
|
|
if(*ppBuffer){
|
|
memcpy(*ppBuffer, pHeader,sizeof(TWAIN_CAPABILITY));
|
|
} else {
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::CopyContainerToPrivateCapBuffer(BYTE* pBuffer, HGLOBAL hContainer)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::CopyContainerToPrivateCapBuffer());
|
|
if((!pBuffer)||(!hContainer)){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
DWORD dwContainerSize = (DWORD)GlobalSize(hContainer);
|
|
BYTE *pContainerBuffer = (BYTE*)GlobalLock(hContainer);
|
|
if(!pContainerBuffer){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TWAIN_CAPABILITY *pHeader = (TWAIN_CAPABILITY*)pBuffer;
|
|
memcpy((BYTE*)pHeader->Data,pContainerBuffer,dwContainerSize);
|
|
|
|
//
|
|
// unlock handle before returning
|
|
//
|
|
|
|
GlobalUnlock(hContainer);
|
|
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::CopyPrivateCapBufferToContainer(HGLOBAL *phContainer, BYTE* pBuffer, DWORD dwSize)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::CopyPrivateCapBufferToContainer());
|
|
if((!phContainer) || (!pBuffer) || (dwSize <= 0)){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
*phContainer = NULL;
|
|
*phContainer = (HGLOBAL)GlobalAlloc(GHND,dwSize);
|
|
if(!*phContainer){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
BYTE *pContainerBuffer = (BYTE*)GlobalLock(*phContainer);
|
|
if(!pContainerBuffer){
|
|
GlobalFree(*phContainer);
|
|
*phContainer = NULL;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TWAIN_CAPABILITY *pHeader = (TWAIN_CAPABILITY*)pBuffer;
|
|
|
|
memcpy(pContainerBuffer,(BYTE*)pHeader->Data,dwSize);
|
|
|
|
//
|
|
// unlock handle before returning
|
|
//
|
|
|
|
GlobalUnlock(*phContainer);
|
|
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::SetCapability(CCap *pCap,TW_CAPABILITY *ptwCap)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::SetCapability());
|
|
if (!pCap || !ptwCap) {
|
|
m_twStatus.ConditionCode = TWCC_BADCAP;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
return pCap->Set(ptwCap);
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnStatusMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnStatusMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
*((TW_STATUS*)ptwMsg->pData) = m_twStatus;
|
|
twRc = TWRC_SUCCESS;
|
|
} else {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
DSError();
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnPendingXfersMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnImageMemXferMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnImageMemXferMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
HRESULT hr = E_FAIL;
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_6:
|
|
case DS_STATE_7:
|
|
{
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
TW_IMAGEMEMXFER *pMemXfer = (TW_IMAGEMEMXFER *)ptwMsg->pData;
|
|
|
|
if ((m_hMemXferBits == NULL)) {
|
|
|
|
m_LinesTransferred = 0;
|
|
|
|
GUID guidFormat = GUID_NULL;
|
|
|
|
DBG_WRN(("Transferring %d bit data",m_MemoryTransferInfo.mtiBitsPerPixel));
|
|
if (m_MemoryTransferInfo.mtiBitsPerPixel > 32) {
|
|
|
|
//
|
|
// Load image into memory for memory transfer (hi-color images)
|
|
//
|
|
|
|
guidFormat = WiaImgFmt_RAWRGB;
|
|
} else {
|
|
|
|
//
|
|
// The TWAIN compatibility layer has the ability to transfer images
|
|
// 1,2,4,8,16,24 and 32 bit when using MEMORYBMP.
|
|
//
|
|
|
|
guidFormat = WiaImgFmt_MEMORYBMP;
|
|
}
|
|
|
|
twRc = TransferToMemory(guidFormat);
|
|
|
|
if (TWRC_SUCCESS != twRc) {
|
|
return twRc;
|
|
} else {
|
|
|
|
//
|
|
// transition to STATE_7
|
|
//
|
|
|
|
SetTWAINState(DS_STATE_7);
|
|
}
|
|
}
|
|
|
|
//
|
|
// turn off the Image caching flag
|
|
//
|
|
|
|
m_bCacheImage = FALSE;
|
|
|
|
//
|
|
// Lock down the memory and get the address to the bits
|
|
//
|
|
|
|
GetMemoryTransferBits((BYTE*)GlobalLock(m_hMemXferBits));
|
|
|
|
m_pMemXferBits = m_MemoryTransferInfo.mtipBits;
|
|
|
|
if (m_pMemXferBits) {
|
|
if(m_MemoryTransferInfo.mtiHeightPixels == 0){
|
|
m_MemoryTransferInfo.mtiHeightPixels = m_ImageHeight;
|
|
}
|
|
|
|
if(m_MemoryTransferInfo.mtiguidFormat == WiaImgFmt_MEMORYBMP){
|
|
|
|
//
|
|
// adjust the image information to report the actual information
|
|
// reported in the BITMAPINFO header.
|
|
//
|
|
|
|
//
|
|
// only change these values, if the current information does not
|
|
// match the image header. (always take the header's values)
|
|
//
|
|
|
|
if(m_MemoryTransferInfo.mtiHeightPixels != m_ImageHeight){
|
|
m_MemoryTransferInfo.mtiHeightPixels = m_ImageHeight;
|
|
}
|
|
|
|
if(m_MemoryTransferInfo.mtiWidthPixels != m_ImageWidth){
|
|
m_MemoryTransferInfo.mtiWidthPixels = m_ImageWidth;
|
|
}
|
|
|
|
}
|
|
|
|
DBG_TRC(("CWiaDataSrc::OnImageMemXferMsg(), Transferring (%d) of (%d) total lines of image data.",m_LinesTransferred,m_MemoryTransferInfo.mtiHeightPixels));
|
|
if (m_LinesTransferred >= (TW_UINT32)m_MemoryTransferInfo.mtiHeightPixels) {
|
|
|
|
//
|
|
// we have completed the transfer, or we are out
|
|
// of scan lines to copy..so return XFERDONE
|
|
//
|
|
|
|
//
|
|
// unlock memory before bailing
|
|
//
|
|
|
|
// Keep unlock and NULLing together
|
|
GlobalUnlock(m_hMemXferBits);
|
|
m_pMemXferBits = NULL;
|
|
|
|
ResetMemXfer();
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
return TWRC_XFERDONE;
|
|
} else {
|
|
|
|
//
|
|
// looks like we are working with transfer data
|
|
//
|
|
|
|
BYTE * pAppBuffer = NULL;
|
|
if (pMemXfer->Memory.Flags & TWMF_HANDLE) {
|
|
|
|
DBG_TRC(("TWAIN Application wants to work with a HANDLE"));
|
|
|
|
//
|
|
// if the memory is a HANDLE, lock it first
|
|
//
|
|
|
|
pAppBuffer = (LPBYTE)GlobalLock(pMemXfer->Memory.TheMem);
|
|
} else if (pMemXfer->Memory.Flags & TWMF_POINTER) {
|
|
|
|
DBG_TRC(("TWAIN Application wants to work with a POINTER"));
|
|
|
|
//
|
|
// if the memory is a POINTER, then proceed
|
|
//
|
|
|
|
pAppBuffer = (LPBYTE)pMemXfer->Memory.TheMem;
|
|
} else {
|
|
|
|
DBG_TRC(("TWAIN Application gave us nothing to work with"));
|
|
|
|
//
|
|
// we have no memory, so set it to NULL
|
|
//
|
|
|
|
pAppBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// if (there is no Memory to write to), or
|
|
// (the app doesn't own the memory), or
|
|
// (the length is less than MIN_ ), or
|
|
// (the length is greater than MAX),
|
|
// return a FAILURE!, and a CC of BADVALUE
|
|
//
|
|
|
|
if (!pAppBuffer ||
|
|
!(pMemXfer->Memory.Flags & TWMF_APPOWNS) ||
|
|
pMemXfer->Memory.Length < MIN_MEMXFER_SIZE ||
|
|
pMemXfer->Memory.Length > MAX_MEMXFER_SIZE) {
|
|
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADVALUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// set memory Xfer values
|
|
//
|
|
|
|
UINT ScanlinesToCopy = 0;
|
|
pMemXfer->BytesPerRow = GetLineSize(&m_MemoryTransferInfo);
|
|
ScanlinesToCopy = min(pMemXfer->Memory.Length / pMemXfer->BytesPerRow,
|
|
(TW_UINT32)(m_MemoryTransferInfo.mtiHeightPixels - m_LinesTransferred));
|
|
|
|
pMemXfer->Compression = TWCP_NONE;
|
|
pMemXfer->Columns = m_MemoryTransferInfo.mtiWidthPixels;
|
|
pMemXfer->Rows = ScanlinesToCopy;
|
|
pMemXfer->XOffset = 0;
|
|
pMemXfer->YOffset = m_LinesTransferred;
|
|
pMemXfer->BytesWritten = pMemXfer->BytesPerRow * ScanlinesToCopy;
|
|
|
|
#ifdef DEBUG_MEMXFER
|
|
DBG_TRC(("CWiaDataSrc::OnImageMemXferMsg(), Reports TW_IMAGEMEMXFER"));
|
|
DBG_TRC(("pMemXfer->Compression = %d",pMemXfer->Compression));
|
|
DBG_TRC(("pMemXfer->Columns = %d",pMemXfer->Columns));
|
|
DBG_TRC(("pMemXfer->Rows = %d",pMemXfer->Rows));
|
|
DBG_TRC(("pMemXfer->XOffset = %d",pMemXfer->XOffset));
|
|
DBG_TRC(("pMemXfer->YOffset = %d",pMemXfer->YOffset));
|
|
DBG_TRC(("pMemXfer->BytesPerRow = %d",pMemXfer->BytesPerRow));
|
|
DBG_TRC(("pMemXfer->BytesWritten = %d",pMemXfer->BytesWritten));
|
|
DBG_TRC(("pAppBuffer = %p, m_pMemXferBits = %p",pAppBuffer,m_pMemXferBits));
|
|
#endif
|
|
//
|
|
// Transfer one-line strips in a loop to Application supplied buffer
|
|
//
|
|
|
|
LPBYTE pTo = pAppBuffer;
|
|
LPBYTE pFrom = m_pMemXferBits + m_LinesTransferred * GetLineSize(&m_MemoryTransferInfo);
|
|
for (UINT i=0;i < ScanlinesToCopy;i++ ) {
|
|
|
|
//
|
|
// swap color values, if needed
|
|
//
|
|
|
|
if (m_MemoryTransferInfo.mtiBitsPerPixel == 24) {
|
|
for (ULONG ulIndex = 0; ulIndex < pMemXfer->BytesPerRow; ulIndex+= 3) {
|
|
|
|
// 1 2 3
|
|
// RED-GREEN-BLUE
|
|
//
|
|
|
|
BYTE bFirst = pFrom[ulIndex];
|
|
pFrom[ulIndex] = pFrom[ulIndex+2];
|
|
pFrom[ulIndex+2] = bFirst;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if(m_MemoryTransferInfo.mtiBitsPerPixel == 48){
|
|
for(j = 0; j < pMemXfer->BytesPerRow; j+=6){
|
|
|
|
// 1 2 1 2 1 2
|
|
// REDRED-GREENGREEN-BLUEBLUE
|
|
//
|
|
|
|
BYTE bFirst = pFrom[j];
|
|
BYTE bSecond = pFrom[j+1];
|
|
pFrom[j] = pFrom[j+4];
|
|
pFrom[j+1] = pFrom[j+5];
|
|
pFrom[j+4] = bFirst;
|
|
pFrom[j+5] = bSecond;
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// copy line to application supplied buffer
|
|
//
|
|
|
|
memcpy(pTo,pFrom,pMemXfer->BytesPerRow);
|
|
pFrom+=GetLineSize(&m_MemoryTransferInfo);
|
|
pTo+=pMemXfer->BytesPerRow;
|
|
}
|
|
|
|
//
|
|
// calculate lines transferred
|
|
//
|
|
|
|
m_LinesTransferred += ScanlinesToCopy;
|
|
if (m_LinesTransferred >= (TW_UINT32)m_MemoryTransferInfo.mtiHeightPixels) {
|
|
|
|
//
|
|
// we have completed the transfer, or we are out
|
|
// of scan lines to copy..so return XFERDONE
|
|
//
|
|
|
|
twRc = TWRC_XFERDONE;
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
// Keep unlock and NULLing together
|
|
GlobalUnlock(m_hMemXferBits);
|
|
m_pMemXferBits = NULL;
|
|
|
|
ResetMemXfer();
|
|
|
|
//
|
|
// if we are working with an application provided HANDLE,
|
|
// GlobalUnlock it before continuing
|
|
//
|
|
|
|
if (pMemXfer->Memory.Flags & TWMF_HANDLE) {
|
|
GlobalUnlock(pMemXfer->Memory.TheMem);
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
//
|
|
// if we are working with an application provided HANDLE,
|
|
// GlobalUnlock it before continuing
|
|
//
|
|
|
|
if (pMemXfer->Memory.Flags & TWMF_HANDLE) {
|
|
GlobalUnlock(pMemXfer->Memory.TheMem);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// unlock buffer when finished
|
|
//
|
|
|
|
// Keep unlock and NULLing together
|
|
GlobalUnlock(m_hMemXferBits);
|
|
m_pMemXferBits = NULL;
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Could not lock down memory for transfer
|
|
//
|
|
|
|
m_twStatus.ConditionCode = TWCC_LOWMEMORY;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// we recieved a message other than the expected MSG_GET
|
|
//
|
|
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
return twRc;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if we failed, report it properly
|
|
//
|
|
|
|
if (TWRC_FAILURE == twRc)
|
|
DSError();
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnImageFileXferMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnImageFileXferMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
GUID guidFileFormat = WiaImgFmt_BMP;
|
|
|
|
CCap *pCap = FindCap(ICAP_IMAGEFILEFORMAT);
|
|
if(pCap){
|
|
guidFileFormat = ICAP_IMAGEFILEFORMAT_TO_WIA_IPA_FORMAT((TW_UINT16)pCap->GetCurrent());
|
|
}
|
|
|
|
twRc = TransferToFile(guidFileFormat);
|
|
if(TWRC_XFERDONE == twRc) {
|
|
SetTWAINState(DS_STATE_7);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::OnImageFileXferMsg(), TransferToFile() failed"));
|
|
}
|
|
} else {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
}
|
|
|
|
if (TWRC_FAILURE == twRc){
|
|
DSError();
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OnImageNativeXferMsg(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OnImageNativeXferMsg());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
HGLOBAL hDIB = NULL;
|
|
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_6:
|
|
{
|
|
if (MSG_GET == ptwMsg->MSG) {
|
|
twRc = TransferToDIB(&hDIB);
|
|
if (TWRC_XFERDONE == twRc) {
|
|
SetTWAINState(DS_STATE_7);
|
|
*(TW_UINT32*)ptwMsg->pData = (TW_UINT32)(INT_PTR)hDIB;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::OnImageNativeXferMsg(), TransferToDIB() failed"));
|
|
}
|
|
} else {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BADPROTOCOL;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
break;
|
|
}
|
|
|
|
if (TWRC_FAILURE == twRc){
|
|
DSError();
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::OpenDS(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::OpenDS());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
if (DS_STATE_3 == GetTWAINState()) {
|
|
//
|
|
// No multiple clients are allowed.
|
|
// This is enforced by making sure that our identity's id field
|
|
// has a value of 0.
|
|
if (m_dsIdentity.Id) {
|
|
|
|
m_twStatus.ConditionCode = TWCC_MAXCONNECTIONS;
|
|
twRc = TWRC_FAILURE;
|
|
} else {
|
|
//
|
|
// make a copy of the caller's identity
|
|
//
|
|
m_AppIdentity = *ptwMsg->AppId;
|
|
m_dsIdentity = *((TW_IDENTITY *)ptwMsg->pData);
|
|
HRESULT hr = S_OK;
|
|
hr = m_pDevice->Open(CWiaDataSrc::DeviceEventCallback,
|
|
(LPARAM)this);
|
|
if (FAILED(hr)) {
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_BUMMER;
|
|
}
|
|
}
|
|
} else {
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
|
|
if (TWRC_SUCCESS == twRc) {
|
|
|
|
//
|
|
// transition to STATE_4
|
|
//
|
|
|
|
SetTWAINState(DS_STATE_4);
|
|
}
|
|
|
|
if (TWRC_SUCCESS != twRc) {
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::CloseDS(PTWAIN_MSG ptwMsg)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::CloseDS());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_7:
|
|
case DS_STATE_6:
|
|
case DS_STATE_5:
|
|
case DS_STATE_4:
|
|
m_pDevice->Close();
|
|
//DBG_TRC(("Calling ResetMemXfer because CLOSEDS was called"));
|
|
ResetMemXfer();
|
|
//
|
|
// We are up for sale again.
|
|
//
|
|
m_AppIdentity.Id = 0;
|
|
|
|
//
|
|
// transition to STATE_3
|
|
//
|
|
|
|
SetTWAINState(DS_STATE_3);
|
|
|
|
if (m_pIWiaItems)
|
|
delete [] m_pIWiaItems;
|
|
m_pIWiaItems = NULL;
|
|
m_NumIWiaItems = 0;
|
|
m_NextIWiaItemIndex = 0;
|
|
break;
|
|
default:
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
twRc = TWRC_FAILURE;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::EnableDS(TW_USERINTERFACE *pUI)
|
|
{
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::DisableDS(TW_USERINTERFACE *pUI)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::DisableDS());
|
|
TW_UINT16 twRc = TWRC_SUCCESS;
|
|
switch (GetTWAINState()) {
|
|
case DS_STATE_5:
|
|
|
|
//
|
|
// transition to STATE_4
|
|
//
|
|
|
|
SetTWAINState(DS_STATE_4);
|
|
break;
|
|
default:
|
|
twRc = TWRC_FAILURE;
|
|
m_twStatus.ConditionCode = TWCC_SEQERROR;
|
|
DSError();
|
|
break;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::CreateCapList(TW_UINT32 NumCaps,PCAPDATA pCapData)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::CreateCapList());
|
|
if (!NumCaps || !pCapData)
|
|
return TWCC_BADVALUE;
|
|
|
|
TW_UINT16 twCc = TWCC_SUCCESS;
|
|
|
|
DestroyCapList();
|
|
m_CapList = new CCap[NumCaps];
|
|
if (m_CapList) {
|
|
|
|
for (m_NumCaps = 0; m_NumCaps < NumCaps; m_NumCaps++) {
|
|
twCc = m_CapList[m_NumCaps].ICap(&pCapData[m_NumCaps]);
|
|
if (TWCC_SUCCESS != twCc) {
|
|
break;
|
|
}
|
|
}
|
|
m_NumCaps = NumCaps;
|
|
|
|
} else {
|
|
twCc = TWCC_LOWMEMORY;
|
|
}
|
|
|
|
if (TWCC_SUCCESS != twCc && m_CapList) {
|
|
DestroyCapList();
|
|
}
|
|
|
|
return twCc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::DestroyCapList()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::DestroyCapList());
|
|
if (m_CapList) {
|
|
delete [] m_CapList;
|
|
m_CapList = NULL;
|
|
}
|
|
m_NumCaps = 0;
|
|
return TWCC_SUCCESS;
|
|
}
|
|
|
|
CCap * CWiaDataSrc::FindCap(TW_UINT16 CapId)
|
|
{
|
|
TW_UINT32 ui32;
|
|
for (ui32 = 0; ui32 < m_NumCaps; ui32++) {
|
|
if (m_CapList[ui32].GetCapId() == CapId)
|
|
return &m_CapList[ui32];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CWiaDataSrc::DSError()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::DSError());
|
|
NotifyCloseReq();
|
|
}
|
|
|
|
HRESULT CALLBACK CWiaDataSrc::DeviceEventCallback(LONG lEvent,LPARAM lParam)
|
|
{
|
|
CWiaDataSrc *pDataSrc = NULL;
|
|
pDataSrc = (CWiaDataSrc *)lParam;
|
|
if (pDataSrc) {
|
|
pDataSrc->NotifyCloseReq();
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
DS_STATE CWiaDataSrc::SetTWAINState(DS_STATE NewTWAINState)
|
|
{
|
|
DBG_TRC(("(Transitioning From TWAIN STATE %d to TWAIN STATE %d)",m_dsState,NewTWAINState));
|
|
m_dsState = NewTWAINState;
|
|
return m_dsState;
|
|
}
|
|
|
|
DS_STATE CWiaDataSrc::GetTWAINState()
|
|
{
|
|
return m_dsState;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::SetStatusTWCC(TW_UINT16 NewConditionCode)
|
|
{
|
|
m_twStatus.ConditionCode = NewConditionCode;
|
|
return NewConditionCode;
|
|
}
|
|
|
|
float CWiaDataSrc::ConvertToTWAINUnits(LONG lValue, LONG lResolution)
|
|
{
|
|
float fReturnValue = 0.0f;
|
|
CCap *pUnits = FindCap(ICAP_UNITS);
|
|
if(pUnits){
|
|
switch (pUnits->GetCurrent()) {
|
|
case TWUN_INCHES:
|
|
fReturnValue = (float)lValue / (float)lResolution;
|
|
break;
|
|
case TWUN_CENTIMETERS:
|
|
fReturnValue = (float)((lValue * 2.54) / (float)lResolution);
|
|
break;
|
|
case TWUN_PICAS:
|
|
fReturnValue = (float)((lValue * 6.00) / (float)lResolution);
|
|
break;
|
|
case TWUN_POINTS:
|
|
fReturnValue = (float)(((float)lValue * 72.0) / (float)lResolution);
|
|
break;
|
|
case TWUN_PIXELS:
|
|
default:
|
|
fReturnValue = (float)lValue;
|
|
break;
|
|
}
|
|
}
|
|
return fReturnValue;
|
|
}
|
|
|
|
LONG CWiaDataSrc::ConvertFromTWAINUnits(float fValue, LONG lResolution)
|
|
{
|
|
LONG lReturnValue = 0;
|
|
CCap *pUnits = FindCap(ICAP_UNITS);
|
|
if (pUnits) {
|
|
switch (pUnits->GetCurrent()) {
|
|
case TWUN_INCHES:
|
|
lReturnValue = (LONG)((float)fValue * (float)lResolution);
|
|
break;
|
|
case TWUN_CENTIMETERS:
|
|
lReturnValue = (LONG)(((float)fValue / 2.54) * (float)lResolution);
|
|
break;
|
|
case TWUN_PICAS:
|
|
lReturnValue = (LONG)(((float)fValue / 6.00) * (float)lResolution);
|
|
break;
|
|
case TWUN_POINTS:
|
|
lReturnValue = (LONG)(((float)fValue / 72.0) * (float)lResolution);
|
|
break;
|
|
case TWUN_PIXELS:
|
|
default:
|
|
lReturnValue = (LONG)fValue;
|
|
break;
|
|
}
|
|
}
|
|
return lReturnValue;
|
|
}
|
|
|
|
DWORD CWiaDataSrc::ReadTwainRegistryDWORDValue(LPTSTR szRegValue, DWORD dwDefault)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::ReadTwainRegistryDWORDValue());
|
|
DWORD dwValue = 0;
|
|
DWORD dwType = REG_DWORD;
|
|
DWORD dwDataSize = sizeof(DWORD);
|
|
DWORD dwDisposition = REG_OPENED_EXISTING_KEY;
|
|
HKEY hTwainRootKey = NULL;
|
|
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER,TWAIN_REG_KEY,0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,
|
|
NULL,&hTwainRootKey,&dwDisposition)){
|
|
|
|
if(dwDisposition == REG_CREATED_NEW_KEY){
|
|
DBG_WRN(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Created Root Twain Registry Key"));
|
|
}
|
|
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hTwainRootKey,szRegValue,NULL,&dwType,(BYTE*)&dwValue,&dwDataSize)) {
|
|
#ifdef UNICODE
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Reading %ws Registry Key Value = %d",szRegValue,dwValue));
|
|
#else
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Reading %s Registry Key Value = %d",szRegValue,dwValue));
|
|
#endif
|
|
} else {
|
|
// reset sizes, just for safety
|
|
dwType = REG_DWORD;
|
|
dwDataSize = sizeof(DWORD);
|
|
dwValue = dwDefault;
|
|
if(ERROR_SUCCESS == RegSetValueEx(hTwainRootKey,szRegValue,NULL,dwType,(BYTE*)&dwDefault,dwDataSize)){
|
|
#ifdef UNICODE
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Writing Default Value for %ws Registry Key Value = %d",szRegValue,dwDefault));
|
|
#else
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Writing Default Value for %s Registry Key Value = %d",szRegValue,dwDefault));
|
|
#endif
|
|
} else {
|
|
#ifdef UNICODE
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Error Reading %ws Registry Key Value",szRegValue));
|
|
#else
|
|
DBG_TRC(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), Error Reading %s Registry Key Value",szRegValue));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hTwainRootKey);
|
|
hTwainRootKey = NULL;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::ReadTwainRegistryDWORDValue(), could not open Root TWAIN Registry Key"));
|
|
}
|
|
return dwValue;
|
|
}
|
|
|
|
LONG CWiaDataSrc::GetPrivateSupportedCapsFromWIADevice(LONG **ppCapArray)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::GetPrivateSupportedCapsFromWIADevice());
|
|
if (!ppCapArray) {
|
|
DBG_ERR(("CWiaDataSrc::GetPrivateSupportedCapsFromWIADevice(), ppCapArray is NULL"));
|
|
return 0;
|
|
}
|
|
|
|
*ppCapArray = NULL;
|
|
LONG lNumPrivateCaps = 0;
|
|
if (m_pDevice) {
|
|
if (m_pDevice->TwainCapabilityPassThrough()) {
|
|
if (m_pCurrentIWiaItem) {
|
|
|
|
//
|
|
// Get the IWiaItemExtras Interface
|
|
//
|
|
|
|
IWiaItemExtras *pIWiaItemExtras = NULL;
|
|
HRESULT hr = m_pCurrentIWiaItem->QueryInterface(IID_IWiaItemExtras,(void **)&pIWiaItemExtras);
|
|
if (S_OK == hr) {
|
|
|
|
//
|
|
// set data sizes
|
|
//
|
|
|
|
DWORD dwInDataSize = 0;
|
|
DWORD dwOutDataSize = 0;
|
|
DWORD dwActualOutDataSize = 0;
|
|
|
|
BYTE *pInData = NULL;
|
|
BYTE *pOutData = NULL;
|
|
|
|
LONG lCapabilityDataSize = sizeof(LONG);
|
|
pOutData = (BYTE*)&lCapabilityDataSize;
|
|
pInData = (BYTE*)&lCapabilityDataSize;
|
|
|
|
dwActualOutDataSize = sizeof(LONG);
|
|
dwInDataSize = dwActualOutDataSize;
|
|
dwOutDataSize = dwActualOutDataSize;
|
|
|
|
//
|
|
// ask how many bytes are needed to store the private TWAIN capabilities the WIA driver supports
|
|
//
|
|
|
|
hr = pIWiaItemExtras->Escape(ESC_TWAIN_PRIVATE_SUPPORTED_CAPS,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize);
|
|
if (S_OK == hr) {
|
|
|
|
lCapabilityDataSize = (LONG)(*pOutData);
|
|
|
|
lNumPrivateCaps = (lCapabilityDataSize / sizeof(LONG));
|
|
|
|
DBG_TRC(("WIA device reported %d private TWAIN supported CAPS",lNumPrivateCaps));
|
|
|
|
if (lNumPrivateCaps > 0) {
|
|
|
|
//
|
|
// allocate an array of LONGs for the WIA driver to fill with
|
|
// CAP ids.
|
|
//
|
|
|
|
dwOutDataSize = (lCapabilityDataSize + PRIVATE_CAP_ARRAY_PADDING);
|
|
dwActualOutDataSize = dwOutDataSize;
|
|
|
|
*ppCapArray = (LONG*)GlobalAlloc(GPTR,dwOutDataSize);
|
|
if (*ppCapArray) {
|
|
|
|
pOutData = (BYTE*)*ppCapArray;
|
|
|
|
//
|
|
// ask the WIA driver to fill the array of LONGS
|
|
//
|
|
|
|
hr = pIWiaItemExtras->Escape(ESC_TWAIN_PRIVATE_SUPPORTED_CAPS,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
&dwActualOutDataSize);
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// pIWiaItemExtras->Escape call failed,
|
|
// a failure means that there are no private supported capabilities
|
|
//
|
|
|
|
DBG_ERR(("pIWiaItemExtras->Escape Failed (sending a request for the cability array data)"));
|
|
DBG_TRC(("Escape(code = %d, pInData = %p, dwInDataSize = %d, pOutData = %p, dwOutDataSize = %d,dwActualOutDataSize = %d)",
|
|
ESC_TWAIN_PRIVATE_SUPPORTED_CAPS,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
dwActualOutDataSize));
|
|
}
|
|
} else {
|
|
DBG_ERR(("could not allocate memory for private capability array of %d items (%d bytes - this includes padding)",lNumPrivateCaps,dwOutDataSize));
|
|
lNumPrivateCaps = 0;
|
|
*ppCapArray = NULL;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// no supported caps
|
|
//
|
|
|
|
DBG_TRC(("No private supported caps reported from WIA device"));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// pIWiaItemExtras->Escape call failed,
|
|
// a failure means that there are no private supported capabilities
|
|
//
|
|
|
|
DBG_ERR(("pIWiaItemExtras->Escape Failed (sending a request for the number of capabilities)"));
|
|
DBG_TRC(("Escape(code = %d, pInData = %p, dwInDataSize = %d, pOutData = %p, dwOutDataSize = %d,dwActualOutDataSize = %d)",
|
|
ESC_TWAIN_PRIVATE_SUPPORTED_CAPS,
|
|
pInData,
|
|
dwInDataSize,
|
|
pOutData,
|
|
dwOutDataSize,
|
|
dwActualOutDataSize));
|
|
}
|
|
|
|
//
|
|
// release IWiaItemExtras Interface
|
|
//
|
|
|
|
if (pIWiaItemExtras) {
|
|
pIWiaItemExtras->Release();
|
|
pIWiaItemExtras = NULL;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// QI for IWiaItemExtras Failed
|
|
//
|
|
|
|
DBG_ERR(("QueryInterface for IWiaItemExtras Failed"));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// no current item selected
|
|
//
|
|
|
|
DBG_ERR(("no current item selected for use"));
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// m_pDevice is NULL
|
|
//
|
|
|
|
DBG_ERR(("CWiaDataSrc::GetPrivateSupportedCapsFromWIADevice(), m_pDevice is NULL"));
|
|
}
|
|
return lNumPrivateCaps;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::TransferToFile(GUID guidFormatID)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::TransferToFile());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
CWiaDataCallback DataCallback;
|
|
CCap *pCap = NULL;
|
|
pCap = FindCap(CAP_INDICATORS);
|
|
if(pCap){
|
|
DataCallback.Initialize(NULL,pCap->GetCurrent());
|
|
} else {
|
|
DataCallback.Initialize(NULL,TRUE);
|
|
}
|
|
|
|
IWiaDataCallback *pIDataCB = NULL;
|
|
hr = DataCallback.QueryInterface(IID_IWiaDataCallback,(void **)&pIDataCB);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pDevice->LoadImageToDisk(m_pCurrentIWiaItem, m_FileXferName, guidFormatID, pIDataCB);
|
|
if (SUCCEEDED(hr)) {
|
|
twRc = TWRC_XFERDONE;
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
}
|
|
pIDataCB->Release();
|
|
pIDataCB = NULL;
|
|
}
|
|
|
|
//
|
|
// check for a cancel, or out-of-paper error (scanners could return this)
|
|
//
|
|
|
|
if ((S_FALSE == hr) || (WIA_ERROR_PAPER_EMPTY == hr)) {
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
if(WIA_ERROR_PAPER_EMPTY == hr) {
|
|
DBG_TRC(("CWiaDataSrc::TransferToFile(), WIA_ERROR_PAPER_EMPTY returned from source."));
|
|
}
|
|
|
|
//
|
|
// set XFERCOUNT
|
|
//
|
|
|
|
CCap *pxferCap = FindCap(CAP_XFERCOUNT);
|
|
if (pxferCap) {
|
|
pxferCap->SetCurrent((TW_UINT32)0);
|
|
}
|
|
|
|
//
|
|
// return a cancel to abort the transfer.
|
|
// Applications will most commonly delete the current
|
|
// image, and keep the previous images.
|
|
//
|
|
|
|
twRc = TWRC_CANCEL;
|
|
} else if (FAILED(hr)) {
|
|
m_twStatus.ConditionCode = TWCC_FROM_HRESULT(hr);
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::TransferToDIB(HGLOBAL *phDIB)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::TransferToDIB());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
CWiaDataCallback DataCallback;
|
|
CCap *pCap = NULL;
|
|
pCap = FindCap(CAP_INDICATORS);
|
|
if(pCap){
|
|
DataCallback.Initialize(NULL,pCap->GetCurrent());
|
|
} else {
|
|
DataCallback.Initialize(NULL,TRUE);
|
|
}
|
|
|
|
IWiaDataCallback *pIDataCB = NULL;
|
|
hr = DataCallback.QueryInterface(IID_IWiaDataCallback,(void **)&pIDataCB);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pDevice->LoadImage(m_pCurrentIWiaItem, WiaImgFmt_MEMORYBMP, pIDataCB); // memory bmp only
|
|
if (SUCCEEDED(hr)) {
|
|
if(SUCCEEDED(DataCallback.GetImage(phDIB, NULL))){
|
|
|
|
//
|
|
// DIB data (special case) - NATIVE TWAIN transfers are in DIB format always
|
|
// If we are acquiring DIB data, then we have to apply the
|
|
// height rules:
|
|
// positive = image is right side up
|
|
// negative = image is up side down
|
|
// zero = image has an unknown length (and assumed to be upside down)
|
|
|
|
if(FlipDIB(*phDIB)){
|
|
twRc = TWRC_XFERDONE;
|
|
m_ImageHeight = (TW_UINT32)DataCallback.GetImageHeight();
|
|
m_ImageWidth = (TW_UINT32)DataCallback.GetImageWidth();
|
|
}
|
|
}
|
|
}
|
|
pIDataCB->Release();
|
|
pIDataCB = NULL;
|
|
}
|
|
|
|
//
|
|
// check for a cancel, or out-of-paper error (scanners could return this)
|
|
//
|
|
|
|
if ((S_FALSE == hr) || (WIA_ERROR_PAPER_EMPTY == hr)) {
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
if(WIA_ERROR_PAPER_EMPTY == hr) {
|
|
DBG_TRC(("CWiaDataSrc::TransferToDIB(), WIA_ERROR_PAPER_EMPTY returned from source."));
|
|
}
|
|
|
|
//
|
|
// set XFERCOUNT
|
|
//
|
|
|
|
CCap *pxferCap = FindCap(CAP_XFERCOUNT);
|
|
if (pxferCap) {
|
|
pxferCap->SetCurrent((TW_UINT32)0);
|
|
}
|
|
|
|
//
|
|
// return a cancel to abort the transfer.
|
|
// Applications will most commonly delete the current
|
|
// image, and keep the previous images.
|
|
//
|
|
|
|
twRc = TWRC_CANCEL;
|
|
} else if (FAILED(hr)) {
|
|
m_twStatus.ConditionCode = TWCC_FROM_HRESULT(hr);
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::TransferToMemory(GUID guidFormatID)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::TransferToMemory());
|
|
|
|
//
|
|
// set WIA format in Transfer Information structure
|
|
//
|
|
|
|
m_MemoryTransferInfo.mtiguidFormat = guidFormatID;
|
|
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
HRESULT hr = E_FAIL;
|
|
CWiaDataCallback DataCallback;
|
|
CCap *pCap = NULL;
|
|
pCap = FindCap(CAP_INDICATORS);
|
|
if(pCap){
|
|
DataCallback.Initialize(NULL,pCap->GetCurrent());
|
|
} else {
|
|
DataCallback.Initialize(NULL,TRUE);
|
|
}
|
|
|
|
IWiaDataCallback *pIDataCB = NULL;
|
|
hr = DataCallback.QueryInterface(IID_IWiaDataCallback,(void **)&pIDataCB);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pDevice->LoadImage(m_pCurrentIWiaItem, guidFormatID, pIDataCB);
|
|
if (SUCCEEDED(hr)) {
|
|
if(SUCCEEDED(DataCallback.GetImage(&m_hMemXferBits, NULL))){
|
|
|
|
//
|
|
// check for DIB data (special case)
|
|
// If we are acquiring DIB data, then we have to apply the
|
|
// height rules:
|
|
// positive = image is right side up
|
|
// negative = image is up side down
|
|
// zero = image has an unknown length (and assumed to be upside down)
|
|
|
|
if(WiaImgFmt_MEMORYBMP == guidFormatID){
|
|
|
|
//
|
|
// for memory transfers we need to make sure that the image
|
|
// is upside down in memory, so the application can assemble
|
|
// the bands correctly.
|
|
//
|
|
|
|
FlipDIB(m_hMemXferBits, TRUE);
|
|
}
|
|
|
|
m_ImageHeight = (TW_UINT32)DataCallback.GetImageHeight();
|
|
m_ImageWidth = (TW_UINT32)DataCallback.GetImageWidth();
|
|
m_hCachedImageData = m_hMemXferBits;
|
|
twRc = TWRC_SUCCESS;
|
|
}
|
|
}
|
|
pIDataCB->Release();
|
|
pIDataCB = NULL;
|
|
}
|
|
|
|
//
|
|
// check for a cancel, or out-of-paper error (scanners could return this)
|
|
//
|
|
|
|
if ((S_FALSE == hr) || (WIA_ERROR_PAPER_EMPTY == hr)) {
|
|
m_twStatus.ConditionCode = TWCC_SUCCESS;
|
|
|
|
if(WIA_ERROR_PAPER_EMPTY == hr) {
|
|
DBG_TRC(("CWiaDataSrc::TransferToMemory(), WIA_ERROR_PAPER_EMPTY returned from source."));
|
|
}
|
|
|
|
//
|
|
// set XFERCOUNT
|
|
//
|
|
|
|
CCap *pxferCap = FindCap(CAP_XFERCOUNT);
|
|
if (pxferCap) {
|
|
pxferCap->SetCurrent((TW_UINT32)0);
|
|
}
|
|
|
|
//
|
|
// return a cancel to abort the transfer.
|
|
// Applications will most commonly delete the current
|
|
// image, and keep the previous images.
|
|
//
|
|
|
|
twRc = TWRC_CANCEL;
|
|
} else if (FAILED(hr)) {
|
|
m_twStatus.ConditionCode = TWCC_FROM_HRESULT(hr);
|
|
twRc = TWRC_FAILURE;
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetCachedImage(HGLOBAL *phImage)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::GetCachedImage());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
if(phImage){
|
|
if (m_hCachedImageData) {
|
|
*phImage = m_hCachedImageData;
|
|
|
|
//
|
|
// since we are giving out the cached data
|
|
// reset the cache handle to NULL;
|
|
//
|
|
|
|
m_hCachedImageData = NULL;
|
|
m_hMemXferBits = NULL;
|
|
twRc = TWRC_SUCCESS;
|
|
}
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::TransferToThumbnail(HGLOBAL *phThumbnail)
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::TransferToThumbnail());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
HRESULT hr = E_FAIL;
|
|
hr = m_pDevice->LoadThumbnail(m_pCurrentIWiaItem,phThumbnail,NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
twRc = TWRC_XFERDONE;
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetCommonSettings()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::GetCommonSettings());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
|
|
//
|
|
// Some TWAIN applications make the assumption that the TWAIN data source
|
|
// defaults to BMP/DIB data formats. This is on the basis that TWAIN
|
|
// spec minimal requirements are BMP/DIB. WIA minimal requirements are
|
|
// BMP/DIB. Set the current Format GUID to MEMORYBMP, and TYMED to
|
|
// TYMED_CALLBACK. This will set the WIA driver to transfer bitmap data
|
|
// by default. This does not limit the data types in any way. A high
|
|
// end application will properly read the valid TWAIN values and configure
|
|
// the device to do the correct thing.
|
|
//
|
|
|
|
//
|
|
// before configuring TWAIN valid values, set the WIA device to TYMED_CALLBACK, MEMORYBMP.
|
|
//
|
|
|
|
HRESULT hr = S_OK;
|
|
CWiahelper WIA;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), failed to set IWiaItem for property writing"));
|
|
return twRc;
|
|
}
|
|
|
|
hr = WIA.WritePropertyLong(WIA_IPA_TYMED,TYMED_CALLBACK);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), failed to set TYMED_CALLBACK as a default setting"));
|
|
return twRc;
|
|
}
|
|
|
|
hr = WIA.WritePropertyGUID(WIA_IPA_FORMAT,WiaImgFmt_MEMORYBMP);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), failed to set WiaImgFmt_MEMORYBMP as a default setting"));
|
|
return twRc;
|
|
}
|
|
|
|
if (TWRC_SUCCESS == GetPixelTypes()) {
|
|
if (TWRC_SUCCESS == GetCompressionTypes()) {
|
|
if (TWRC_SUCCESS == GetBitDepths()) {
|
|
if (TWRC_SUCCESS == GetImageFileFormats()) {
|
|
twRc = TWRC_SUCCESS;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), GetImageFileFormats()"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), GetBitDepths() failed"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), GetCompressionTypes() failed"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCommonSettings(), GetPixelTypes() failed"));
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetCommonDefaultSettings()
|
|
{
|
|
DBG_FN_DS(CWiaDataSrc::GetCommonDefaultSettings());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
CCap *pCap = NULL;
|
|
TW_UINT16 CapDataArray[1];
|
|
|
|
pCap = FindCap(ICAP_PIXELTYPE);
|
|
if (pCap) {
|
|
CapDataArray[0] = TWPT_RGB;
|
|
twRc = pCap->Set(0,0,1,(BYTE*)CapDataArray);
|
|
if (TWRC_SUCCESS == twRc) {
|
|
|
|
pCap = FindCap(ICAP_COMPRESSION);
|
|
if (pCap) {
|
|
CapDataArray[0] = TWCP_NONE;
|
|
twRc = pCap->Set(0,0,1,(BYTE*)CapDataArray);
|
|
|
|
pCap = FindCap(ICAP_BITDEPTH);
|
|
if (pCap) {
|
|
CapDataArray[0] = 24;
|
|
twRc = pCap->Set(0,0,1,(BYTE*)CapDataArray);
|
|
if (TWRC_SUCCESS == twRc) {
|
|
|
|
pCap = FindCap(ICAP_IMAGEFILEFORMAT);
|
|
if (pCap) {
|
|
CapDataArray[0] = TWFF_BMP;
|
|
twRc = pCap->Set(0,0,1,(BYTE*)CapDataArray);
|
|
if (TWRC_SUCCESS == twRc) {
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetPixelTypes()
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::GetPixelTypes());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
|
|
CCap *pCap = FindCap(ICAP_PIXELTYPE);
|
|
if (pCap) {
|
|
|
|
HRESULT hr = S_OK;
|
|
CWiahelper WIA;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::GetPixelTypes(), failed to set IWiaItem for property reading"));
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT32 ActualCount = 0;
|
|
TW_UINT32 CurrentIndex = 0;
|
|
TW_UINT32 DefaultIndex = 0;
|
|
TW_UINT16 *pPixelTypeArray = NULL;
|
|
|
|
//
|
|
// read current value, for default and current index settings
|
|
//
|
|
|
|
LONG lCurrentDataTypeValue = WIA_DATA_COLOR;
|
|
|
|
//
|
|
// read current WIA_IPA_DATATYPE setting
|
|
//
|
|
|
|
hr = WIA.ReadPropertyLong(WIA_IPA_DATATYPE,&lCurrentDataTypeValue);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// read valid values for WIA_IPA_DATATYPE
|
|
//
|
|
|
|
PROPVARIANT pv;
|
|
memset(&pv,0,sizeof(pv));
|
|
LONG lAccessFlags = 0;
|
|
hr = WIA.ReadPropertyAttributes(WIA_IPA_DATATYPE,&lAccessFlags,&pv);
|
|
if (SUCCEEDED(hr)) {
|
|
if (lAccessFlags & WIA_PROP_LIST) {
|
|
|
|
//
|
|
// for each valid WIA value in the LIST, set a corresponding
|
|
// TWAIN value
|
|
//
|
|
|
|
pPixelTypeArray = new TW_UINT16[WIA_PROP_LIST_COUNT(&pv)];
|
|
if (pPixelTypeArray) {
|
|
memset(pPixelTypeArray,0,(sizeof(TW_UINT16)*WIA_PROP_LIST_COUNT(&pv)));
|
|
for (ULONG i = 0; i < WIA_PROP_LIST_COUNT(&pv);i++) {
|
|
|
|
switch (pv.caul.pElems[i+2]) {
|
|
case WIA_DATA_THRESHOLD:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_BW;
|
|
if (lCurrentDataTypeValue == WIA_DATA_THRESHOLD) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_THERSHOLD -> TWPT_BW"));
|
|
break;
|
|
case WIA_DATA_GRAYSCALE:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_GRAY;
|
|
if (lCurrentDataTypeValue == WIA_DATA_GRAYSCALE) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_GRAYSCALE -> TWPT_GRAY"));
|
|
break;
|
|
case WIA_DATA_COLOR:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_RGB;
|
|
if (lCurrentDataTypeValue == WIA_DATA_COLOR) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_COLOR -> TWPT_RGB"));
|
|
break;
|
|
case WIA_DATA_DITHER:
|
|
case WIA_DATA_COLOR_THRESHOLD:
|
|
case WIA_DATA_COLOR_DITHER:
|
|
////////////////////////////////
|
|
// NO TWAIN -> WIA CONVERSION //
|
|
////////////////////////////////
|
|
//
|
|
// TWPT_PALETTE
|
|
// TWPT_CMY
|
|
// TWPT_CMYK
|
|
// TWPT_YUV
|
|
// TWPT_YUVK
|
|
// TWPT_CIEXYZ
|
|
default:
|
|
DBG_TRC(("WIA Data Type (%d) does not MAP to TWAIN a pixel type",pv.caul.pElems[i+2]));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetPixelTypes(), failed to allocate Pixel Type Array Memory"));
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// we only have 1 value, so make it the current, default and valid value.
|
|
//
|
|
|
|
pPixelTypeArray = new TW_UINT16[1];
|
|
if (pPixelTypeArray) {
|
|
memset(pPixelTypeArray,0,(sizeof(TW_UINT16)));
|
|
|
|
switch (lCurrentDataTypeValue) {
|
|
case WIA_DATA_THRESHOLD:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_BW;
|
|
if (lCurrentDataTypeValue == WIA_DATA_THRESHOLD) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_THERSHOLD -> TWPT_BW"));
|
|
break;
|
|
case WIA_DATA_GRAYSCALE:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_GRAY;
|
|
if (lCurrentDataTypeValue == WIA_DATA_GRAYSCALE) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_GRAYSCALE -> TWPT_GRAY"));
|
|
break;
|
|
case WIA_DATA_COLOR:
|
|
pPixelTypeArray[ActualCount] = (TW_UINT16)TWPT_RGB;
|
|
if (lCurrentDataTypeValue == WIA_DATA_COLOR) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_DATA_COLOR -> TWPT_RGB"));
|
|
break;
|
|
case WIA_DATA_DITHER:
|
|
case WIA_DATA_COLOR_THRESHOLD:
|
|
case WIA_DATA_COLOR_DITHER:
|
|
////////////////////////////////
|
|
// NO TWAIN -> WIA CONVERSION //
|
|
////////////////////////////////
|
|
//
|
|
// TWPT_PALETTE
|
|
// TWPT_CMY
|
|
// TWPT_CMYK
|
|
// TWPT_YUV
|
|
// TWPT_YUVK
|
|
// TWPT_CIEXYZ
|
|
default:
|
|
DBG_TRC(("WIA Data Type (%d) does not MAP to TWAIN a pixel type",lCurrentDataTypeValue));
|
|
break;
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetPixelTypes(), failed to allocate Pixel Type Array Memory"));
|
|
}
|
|
}
|
|
|
|
if (pPixelTypeArray) {
|
|
|
|
//
|
|
// default index is equal to current index, because we are stating that the WIA driver
|
|
// is a fresh start-up state.
|
|
//
|
|
|
|
DefaultIndex = CurrentIndex;
|
|
|
|
twRc = pCap->Set(DefaultIndex,CurrentIndex,ActualCount,(BYTE*)pPixelTypeArray,TRUE); // list
|
|
delete [] pPixelTypeArray;
|
|
pPixelTypeArray = NULL;
|
|
|
|
//twRc = TWRC_SUCCESS;
|
|
}
|
|
|
|
PropVariantClear(&pv);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetPixelTypes(), failed to read WIA_IPS_DATATYPE attributes"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetPixelTypes(), failed to read WIA_IPS_DATATYPE current value"));
|
|
}
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetBitDepths()
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::GetBitDepths());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
|
|
CCap *pCap = FindCap(ICAP_BITDEPTH);
|
|
if (pCap) {
|
|
|
|
HRESULT hr = S_OK;
|
|
CWiahelper WIA;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), failed to set IWiaItem for property reading"));
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT32 ActualCount = 0;
|
|
TW_UINT32 CurrentIndex = 0;
|
|
TW_UINT32 DefaultIndex = 0;
|
|
TW_UINT16 BitDepthArray[MAX_BITDEPTHS];
|
|
memset(BitDepthArray,0,sizeof(BitDepthArray));
|
|
|
|
//
|
|
// read current value, for default and current index settings
|
|
//
|
|
|
|
LONG lCurrentDataTypeValue = WIA_DATA_COLOR;
|
|
LONG lCurrentBitDepthValue = 24;
|
|
|
|
//
|
|
// read current WIA_IPA_DATATYPE setting
|
|
//
|
|
|
|
hr = WIA.ReadPropertyLong(WIA_IPA_DATATYPE,&lCurrentDataTypeValue);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// read current WIA_IPA_DEPTH setting
|
|
//
|
|
|
|
hr = WIA.ReadPropertyLong(WIA_IPA_DEPTH,&lCurrentBitDepthValue);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
PROPVARIANT pv;
|
|
memset(&pv,0,sizeof(pv));
|
|
LONG lAccessFlags = 0;
|
|
|
|
//
|
|
// read valid values for WIA_IPA_DATATYPE
|
|
//
|
|
|
|
hr = WIA.ReadPropertyAttributes(WIA_IPA_DATATYPE,&lAccessFlags,&pv);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// for each valid value, set it to the current setting, and read
|
|
// the valid values for WIA_IPA_DEPTH.
|
|
//
|
|
|
|
if (lAccessFlags & WIA_PROP_LIST) {
|
|
|
|
//
|
|
// set the WIA_IPA_DATATYPE to each valid value in the LIST
|
|
//
|
|
|
|
for (ULONG i = 0; i < WIA_PROP_LIST_COUNT(&pv);i++) {
|
|
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DATATYPE,(LONG)pv.caul.pElems[i+2]);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// read valid values for WIA_IPA_DEPTH
|
|
//
|
|
|
|
lAccessFlags = 0;
|
|
PROPVARIANT pvDepth;
|
|
memset(&pvDepth,0,sizeof(pvDepth));
|
|
hr = WIA.ReadPropertyAttributes(WIA_IPA_DEPTH,&lAccessFlags,&pvDepth);
|
|
if (SUCCEEDED(hr)) {
|
|
LONG lBitDepth = 0;
|
|
if (lAccessFlags & WIA_PROP_LIST) {
|
|
|
|
//
|
|
// copy each valid value in the LIST to the array
|
|
//
|
|
|
|
for (ULONG ulIndex = 0; ulIndex < WIA_PROP_LIST_COUNT(&pvDepth);ulIndex++) {
|
|
lBitDepth = pvDepth.caul.pElems[ulIndex+2];
|
|
for (ULONG BitDepthArrayIndex = 0; BitDepthArrayIndex < MAX_BITDEPTHS; BitDepthArrayIndex++) {
|
|
|
|
if (BitDepthArray[BitDepthArrayIndex] == 0) {
|
|
|
|
//
|
|
// the current slot is (0) zero, so add the new bit depth value
|
|
//
|
|
|
|
BitDepthArray[BitDepthArrayIndex] = (TW_UINT16)lBitDepth;
|
|
DBG_TRC(("WIA driver supports %d bit depth",lBitDepth));
|
|
ActualCount++;
|
|
|
|
//
|
|
// exit the loop
|
|
//
|
|
|
|
BitDepthArrayIndex = MAX_BITDEPTHS;
|
|
} else if (BitDepthArray[BitDepthArrayIndex] == (TW_UINT16)lBitDepth) {
|
|
|
|
//
|
|
// bit depth is already in the list, so exit the loop
|
|
//
|
|
|
|
BitDepthArrayIndex = MAX_BITDEPTHS;
|
|
}
|
|
}
|
|
}
|
|
} else if (lAccessFlags & WIA_PROP_NONE) {
|
|
|
|
//
|
|
// read the current value for WIA_IPA_DEPTH
|
|
// and copy it to the array
|
|
//
|
|
|
|
hr = WIA.ReadPropertyLong(WIA_IPA_DEPTH,&lBitDepth);
|
|
if (SUCCEEDED(hr)) {
|
|
for (ULONG BitDepthArrayIndex = 0; BitDepthArrayIndex < MAX_BITDEPTHS; BitDepthArrayIndex++) {
|
|
|
|
if (BitDepthArray[BitDepthArrayIndex] == 0) {
|
|
|
|
//
|
|
// the current slot is (0) zero, so add the new bit depth value
|
|
//
|
|
|
|
BitDepthArray[BitDepthArrayIndex] = (TW_UINT16)lBitDepth;
|
|
DBG_TRC(("WIA driver supports %d bit depth",lBitDepth));
|
|
ActualCount++;
|
|
|
|
//
|
|
// exit the loop
|
|
//
|
|
|
|
BitDepthArrayIndex = MAX_BITDEPTHS;
|
|
} else if (BitDepthArray[BitDepthArrayIndex] == (TW_UINT16)lBitDepth) {
|
|
|
|
//
|
|
// bit depth is already in the list, so exit the loop
|
|
//
|
|
|
|
BitDepthArrayIndex = MAX_BITDEPTHS;
|
|
}
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), ReadPropertyLong(WIA_IPA_DEPTH) failed"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// clean up the PROPVARIANT structure
|
|
//
|
|
|
|
PropVariantClear(&pvDepth);
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), WritePropertyLong(WIA_IPA_DATATYPE) failed"));
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// we only have 1 value, so make it the current, default and valid value.
|
|
//
|
|
|
|
BitDepthArray[0] = (TW_UINT16)lCurrentBitDepthValue;
|
|
ActualCount = 1;
|
|
DBG_TRC(("WIA driver supports %d bit depth",lCurrentBitDepthValue));
|
|
}
|
|
|
|
//
|
|
// set the current values back
|
|
//
|
|
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DATATYPE,lCurrentDataTypeValue);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DEPTH,lCurrentBitDepthValue);
|
|
}
|
|
|
|
for (ULONG BitDepthArrayIndex = 0; BitDepthArrayIndex < MAX_BITDEPTHS; BitDepthArrayIndex++) {
|
|
if (BitDepthArray[BitDepthArrayIndex] == (TW_UINT16)lCurrentBitDepthValue) {
|
|
CurrentIndex = BitDepthArrayIndex;
|
|
BitDepthArrayIndex = MAX_BITDEPTHS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// default index is equal to current index, because we are stating that the WIA driver
|
|
// is a fresh start-up state.
|
|
//
|
|
|
|
DefaultIndex = CurrentIndex;
|
|
|
|
twRc = pCap->Set(DefaultIndex,CurrentIndex,ActualCount,(BYTE*)BitDepthArray,TRUE); // list
|
|
//twRc = TWRC_SUCCESS;
|
|
|
|
PropVariantClear(&pv);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), failed to read WIA_IPS_DATATYPE attributes"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), ReadPropertyLong(WIA_IPA_DEPTH) failed"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetBitDepths(), ReadPropertyLong(WIA_IPA_DATATYPE) failed"));
|
|
}
|
|
}
|
|
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetImageFileFormats()
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::GetImageFileFormats());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
|
|
CCap *pCap = FindCap(ICAP_IMAGEFILEFORMAT);
|
|
if (pCap) {
|
|
|
|
HRESULT hr = S_OK;
|
|
CWiahelper WIA;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::GetImageFileFormats(), failed to set IWiaItem for property reading"));
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT32 ActualCount = 0;
|
|
TW_UINT32 CurrentIndex = 0;
|
|
TW_UINT32 DefaultIndex = 0;
|
|
TW_UINT16 *pFileTypeArray = NULL;
|
|
|
|
IWiaDataTransfer *pIWiaDataTransfer = NULL;
|
|
TW_UINT32 TotalFileFormats = 0;
|
|
IEnumWIA_FORMAT_INFO *pIEnumWIA_FORMAT_INFO = NULL;
|
|
WIA_FORMAT_INFO pfe;
|
|
|
|
//
|
|
// read current value, for default and current index settings
|
|
//
|
|
|
|
GUID guidCurrentFileFormat = GUID_NULL;
|
|
hr = WIA.ReadPropertyGUID(WIA_IPA_FORMAT,&guidCurrentFileFormat);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// collect valid values for image file format
|
|
//
|
|
|
|
hr = m_pCurrentIWiaItem->QueryInterface(IID_IWiaDataTransfer, (void **)&pIWiaDataTransfer);
|
|
if (S_OK == hr) {
|
|
hr = pIWiaDataTransfer->idtEnumWIA_FORMAT_INFO(&pIEnumWIA_FORMAT_INFO);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// count supported FILE formats
|
|
//
|
|
|
|
do {
|
|
memset(&pfe,0,sizeof(pfe));
|
|
hr = pIEnumWIA_FORMAT_INFO->Next(1, &pfe, NULL);
|
|
if (hr == S_OK) {
|
|
if ((pfe.lTymed == TYMED_FILE) || (pfe.lTymed == TYMED_MULTIPAGE_FILE)) {
|
|
TotalFileFormats++;
|
|
}
|
|
}
|
|
} while (hr == S_OK);
|
|
|
|
//
|
|
// allocate supported FILE format array
|
|
//
|
|
|
|
pFileTypeArray = new TW_UINT16[TotalFileFormats];
|
|
if (pFileTypeArray) {
|
|
memset(pFileTypeArray,0,(sizeof(TW_UINT16) * TotalFileFormats));
|
|
|
|
//
|
|
// reset enuerator
|
|
//
|
|
|
|
hr = pIEnumWIA_FORMAT_INFO->Reset();
|
|
if (SUCCEEDED(hr)) {
|
|
do {
|
|
memset(&pfe,0,sizeof(pfe));
|
|
hr = pIEnumWIA_FORMAT_INFO->Next(1, &pfe, NULL);
|
|
if (hr == S_OK) {
|
|
|
|
if (pfe.lTymed == TYMED_MULTIPAGE_FILE) {
|
|
if (pfe.guidFormatID == WiaImgFmt_TIFF) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_TIFFMULTI;
|
|
if (guidCurrentFileFormat == WiaImgFmt_TIFF) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_TIFF (Multipage) -> TWFF_TIFFMULTI"));
|
|
}
|
|
}
|
|
|
|
if (pfe.lTymed == TYMED_FILE) {
|
|
if (pfe.guidFormatID == WiaImgFmt_BMP) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_BMP;
|
|
if (guidCurrentFileFormat == WiaImgFmt_BMP) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_BMP -> TWFF_BMP"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_JPEG) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_JFIF;
|
|
if (guidCurrentFileFormat == WiaImgFmt_JPEG) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_JPEG -> TWFF_JFIF"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_TIFF) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_TIFF;
|
|
if (guidCurrentFileFormat == WiaImgFmt_TIFF) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_TIFF -> TWFF_TIFF"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_PICT) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_PICT;
|
|
if (guidCurrentFileFormat == WiaImgFmt_PICT) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_PICT -> TWFF_PICT"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_PNG) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_PNG;
|
|
if (guidCurrentFileFormat == WiaImgFmt_PNG) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_PNG -> WiaImgFmt_PNG"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_EXIF) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_EXIF;
|
|
if (guidCurrentFileFormat == WiaImgFmt_EXIF) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_EXIF -> TWFF_EXIF"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_FLASHPIX) {
|
|
pFileTypeArray[ActualCount] = (TW_UINT16)TWFF_FPX;
|
|
if (guidCurrentFileFormat == WiaImgFmt_FLASHPIX) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WiaImgFmt_FLASHPIX -> TWFF_FPX"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_UNDEFINED) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_UNDEFINED does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_EMF) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_EMF does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_WMF) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_WMF does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_GIF) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_GIF does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_PHOTOCD) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_PHOTOCD does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_ICO) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_ICO does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_CIFF) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_CIFF does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_JPEG2K) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_JPEG2K does not MAP to TWAIN a file format"));
|
|
} else if (pfe.guidFormatID == WiaImgFmt_JPEG2KX) {
|
|
DBG_TRC(("WIA File Format WiaImgFmt_JPEG2KX does not MAP to TWAIN a file format"));
|
|
} else {
|
|
|
|
}
|
|
|
|
////////////////////////////////
|
|
// NO TWAIN -> WIA CONVERSION //
|
|
////////////////////////////////
|
|
//
|
|
// TWFF_XBM
|
|
// TWFF_SPIFF
|
|
}
|
|
}
|
|
} while (hr == S_OK);
|
|
}
|
|
|
|
if (pFileTypeArray) {
|
|
|
|
//
|
|
// default index is equal to current index, because we are stating that the WIA driver
|
|
// is a fresh start-up state.
|
|
//
|
|
|
|
DefaultIndex = CurrentIndex;
|
|
|
|
twRc = pCap->Set(DefaultIndex,CurrentIndex,ActualCount,(BYTE*)pFileTypeArray,TRUE); // list
|
|
delete [] pFileTypeArray;
|
|
pFileTypeArray = NULL;
|
|
//twRc = TWRC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
pIEnumWIA_FORMAT_INFO->Release();
|
|
pIEnumWIA_FORMAT_INFO = NULL;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetImageFileFormats(), pIWiaDataTransfer->idtEnumWIA_FORMAT_INFO() failed to enumerate supported file formats"));
|
|
}
|
|
pIWiaDataTransfer->Release();
|
|
pIWiaDataTransfer = NULL;
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetImageFileFormats(), QueryInterface(IID_IWiaDataTransfer) failed"));
|
|
}
|
|
}
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetCompressionTypes()
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::GetCompressionTypes());
|
|
TW_UINT16 twRc = TWRC_FAILURE;
|
|
|
|
CCap *pCap = FindCap(ICAP_COMPRESSION);
|
|
if (pCap) {
|
|
|
|
#ifdef SUPPORT_COMPRESSION_TYPES
|
|
|
|
HRESULT hr = S_OK;
|
|
CWiahelper WIA;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::GetCompressionTypes(), failed to set IWiaItem for property reading"));
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT32 ActualCount = 0;
|
|
TW_UINT32 CurrentIndex = 0;
|
|
TW_UINT32 DefaultIndex = 0;
|
|
TW_UINT16 *pCompressionTypeArray = NULL;
|
|
|
|
//
|
|
// read current value, for default and current index settings
|
|
//
|
|
|
|
LONG lCurrentCompressionTypeValue = WIA_COMPRESSION_NONE;
|
|
hr = WIA.ReadPropertyLong(WIA_IPA_COMPRESSION,&lCurrentCompressionTypeValue);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
PROPVARIANT pv;
|
|
memset(&pv,0,sizeof(pv));
|
|
LONG lAccessFlags = 0;
|
|
hr = WIA.ReadPropertyAttributes(WIA_IPA_COMPRESSION,&lAccessFlags,&pv);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// collect valid values for compression type
|
|
//
|
|
|
|
if (lAccessFlags & WIA_PROP_LIST) {
|
|
pCompressionTypeArray = new TW_UINT16[WIA_PROP_LIST_COUNT(&pv)];
|
|
if (pCompressionTypeArray) {
|
|
memset(pCompressionTypeArray,0,(sizeof(TW_UINT16)*WIA_PROP_LIST_COUNT(&pv)));
|
|
for (ULONG i = 0; i < WIA_PROP_LIST_COUNT(&pv);i++) {
|
|
|
|
switch (pv.caul.pElems[i+2]) {
|
|
case WIA_COMPRESSION_NONE:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_NONE;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_NONE) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_NONE -> TWCP_NONE"));
|
|
break;
|
|
case WIA_COMPRESSION_G3:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_GROUP31D;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_G3) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_G3 -> TWCP_GROUP31D"));
|
|
break;
|
|
case WIA_COMPRESSION_G4:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_GROUP4;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_G4) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_G4 -> TWCP_GROUP4"));
|
|
break;
|
|
case WIA_COMPRESSION_JPEG:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_JPEG;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_JPEG) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_JPEG -> TWCP_JPEG"));
|
|
break;
|
|
case WIA_COMPRESSION_BI_RLE4:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_RLE4;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_BI_RLE4) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_BI_RLE4 -> TWCP_RLE4"));
|
|
break;
|
|
case WIA_COMPRESSION_BI_RLE8:
|
|
pCompressionTypeArray[ActualCount] = (TW_UINT16)TWCP_RLE8;
|
|
if (lCurrentCompressionTypeValue == WIA_COMPRESSION_BI_RLE8) {
|
|
CurrentIndex = ActualCount;
|
|
}
|
|
ActualCount++;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_BI_RLE8 -> TWCP_RLE8"));
|
|
break;
|
|
////////////////////////////////
|
|
// NO TWAIN -> WIA CONVERSION //
|
|
////////////////////////////////
|
|
//
|
|
// TWCP_PACKBITS
|
|
// TWCP_GROUP31D
|
|
// TWCP_GROUP31DEOL
|
|
// TWCP_GROUP32D
|
|
//
|
|
//
|
|
// TWCP_LZW
|
|
// TWCP_JBIG
|
|
|
|
default:
|
|
DBG_TRC(("WIA Compression Type (%d) does not MAP to TWAIN a compression type",pv.caul.pElems[i+2]));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCompressionTypes(), failed to allocate Compression Type Array Memory"));
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// current value becomes the only valid value
|
|
//
|
|
|
|
CurrentIndex = 0;
|
|
ActualCount = 1;
|
|
|
|
pCompressionTypeArray = new TW_UINT16[1];
|
|
if (pCompressionTypeArray) {
|
|
switch (lCurrentCompressionTypeValue) {
|
|
case WIA_COMPRESSION_NONE:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_NONE;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_NONE -> TWCP_NONE"));
|
|
break;
|
|
case WIA_COMPRESSION_G3:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_GROUP31D;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_G3 -> TWCP_GROUP31D"));
|
|
break;
|
|
case WIA_COMPRESSION_G4:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_GROUP4;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_G4 -> TWCP_GROUP4"));
|
|
break;
|
|
case WIA_COMPRESSION_JPEG:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_JPEG;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_JPEG -> TWCP_JPEG"));
|
|
break;
|
|
case WIA_COMPRESSION_BI_RLE4:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_RLE4;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_BI_RLE4 -> TWCP_RLE4"));
|
|
break;
|
|
case WIA_COMPRESSION_BI_RLE8:
|
|
pCompressionTypeArray[0] = (TW_UINT16)TWCP_RLE8;
|
|
DBG_TRC(("WIA driver supports WIA_COMPRESSION_BI_RLE8 -> TWCP_RLE8"));
|
|
break;
|
|
////////////////////////////////
|
|
// NO TWAIN -> WIA CONVERSION //
|
|
////////////////////////////////
|
|
//
|
|
// TWCP_PACKBITS
|
|
// TWCP_GROUP31D
|
|
// TWCP_GROUP31DEOL
|
|
// TWCP_GROUP32D
|
|
//
|
|
//
|
|
// TWCP_LZW
|
|
// TWCP_JBIG
|
|
|
|
default:
|
|
DBG_TRC(("WIA Compression Type (%d) does not MAP to TWAIN a compression type",lCurrentCompressionTypeValue));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pCompressionTypeArray) {
|
|
|
|
//
|
|
// default index is equal to current index, because we are stating that the WIA driver
|
|
// is a fresh start-up state.
|
|
//
|
|
|
|
DefaultIndex = CurrentIndex;
|
|
|
|
twRc = pCap->Set(DefaultIndex,CurrentIndex,ActualCount,(BYTE*)pCompressionTypeArray,TRUE); // list
|
|
delete [] pCompressionTypeArray;
|
|
pCompressionTypeArray = NULL;
|
|
|
|
//twRc = TWRC_SUCCESS;
|
|
}
|
|
|
|
PropVariantClear(&pv);
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCompressionTypes(), failed to read WIA_IPA_COMPRESSION attributes"));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::GetCompressionTypes(), failed to read WIA_IPA_COMPRESSION current value"));
|
|
}
|
|
#else // SUPPORT_COMPRESSION_TYPES
|
|
|
|
//
|
|
// support only TWCP_NONE (no Compression)
|
|
//
|
|
|
|
TW_UINT16 CapDataArray[1];
|
|
CapDataArray[0] = TWCP_NONE;
|
|
twRc = pCap->Set(0,0,1,(BYTE*)CapDataArray);
|
|
|
|
#endif // SUPPORT_COMPRESSION_TYPES
|
|
}
|
|
return twRc;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::SetCommonSettings(CCap *pCap)
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::SetCommonSettings());
|
|
HRESULT hr = S_OK;
|
|
LONG lValue = 0;
|
|
CWiahelper WIA;
|
|
IWiaItem *pIRootItem = NULL;
|
|
hr = WIA.SetIWiaItem(m_pCurrentIWiaItem);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), failed to set IWiaItem for property reading"));
|
|
}
|
|
|
|
//
|
|
// determine if it is a Capability that the device really needs to know
|
|
// about.
|
|
//
|
|
|
|
switch (pCap->GetCapId()) {
|
|
case ICAP_PIXELTYPE:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(ICAP_PIXELTYPE)"));
|
|
switch (pCap->GetCurrent()) {
|
|
case TWPT_BW:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_THRESHOLD"));
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DATATYPE,WIA_DATA_THRESHOLD);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_THRESHOLD failed"));
|
|
}
|
|
break;
|
|
case TWPT_GRAY:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_GRAYSCALE"));
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DATATYPE,WIA_DATA_GRAYSCALE);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_GRAYSCALE failed"));
|
|
}
|
|
break;
|
|
case TWPT_RGB:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_COLOR"));
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DATATYPE,WIA_DATA_COLOR);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_DATATYPE to WIA_DATA_COLOR failed"));
|
|
}
|
|
break;
|
|
case TWPT_PALETTE:
|
|
case TWPT_CMY:
|
|
case TWPT_CMYK:
|
|
case TWPT_YUV:
|
|
case TWPT_YUVK:
|
|
case TWPT_CIEXYZ:
|
|
default:
|
|
DBG_WRN(("CWiaDataSrc::SetCommonSettings(), An unsupported ICAP_PIXELTYPE (%d) was sent to this data source",(LONG)pCap->GetCurrent()));
|
|
break;
|
|
}
|
|
break;
|
|
case ICAP_BITDEPTH:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(ICAP_BITDEPTH)"));
|
|
lValue = (LONG)pCap->GetCurrent();
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_BITDEPTH to %d",lValue));
|
|
hr = WIA.WritePropertyLong(WIA_IPA_DEPTH,lValue);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_BITDEPTH to %d failed",lValue));
|
|
}
|
|
break;
|
|
case ICAP_IMAGEFILEFORMAT:
|
|
{
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(ICAP_IMAGEFILEFORMAT)"));
|
|
lValue = (LONG)pCap->GetCurrent();
|
|
LONG lTymed = TYMED_FILE;
|
|
if (lValue == TWFF_TIFFMULTI) {
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_TYMED to TYMED_MULTIPAGE_FILE"));
|
|
lTymed = TYMED_MULTIPAGE_FILE;
|
|
} else {
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_TYMED to TYMED_FILE"));
|
|
}
|
|
|
|
hr = WIA.WritePropertyLong(WIA_IPA_TYMED,lTymed);
|
|
|
|
GUID guidFormat = GUID_NULL;
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
switch (lValue) {
|
|
case TWFF_TIFFMULTI:
|
|
case TWFF_TIFF:
|
|
guidFormat = WiaImgFmt_TIFF;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_TIFF"));
|
|
break;
|
|
case TWFF_PICT:
|
|
guidFormat = WiaImgFmt_PICT;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_PICT"));
|
|
break;
|
|
case TWFF_BMP:
|
|
guidFormat = WiaImgFmt_BMP;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_BMP"));
|
|
break;
|
|
case TWFF_JFIF:
|
|
guidFormat = WiaImgFmt_JPEG;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_JPEG"));
|
|
break;
|
|
case TWFF_FPX:
|
|
guidFormat = WiaImgFmt_FLASHPIX;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_FLASHPIX"));
|
|
break;
|
|
case TWFF_PNG:
|
|
guidFormat = WiaImgFmt_PNG;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_PNG"));
|
|
break;
|
|
case TWFF_EXIF:
|
|
guidFormat = WiaImgFmt_EXIF;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to WiaImgFmt_EXIF"));
|
|
break;
|
|
case TWFF_SPIFF:
|
|
case TWFF_XBM:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
hr = WIA.WritePropertyGUID(WIA_IPA_FORMAT,guidFormat);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_FORMAT to %d failed",lValue));
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_TYMED to %d failed",lTymed));
|
|
}
|
|
}
|
|
break;
|
|
case ICAP_COMPRESSION:
|
|
{
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(ICAP_COMPRESSION)"));
|
|
lValue = (LONG)pCap->GetCurrent();
|
|
LONG lCompression = WIA_COMPRESSION_NONE;
|
|
switch (lValue) {
|
|
case TWCP_NONE:
|
|
lCompression = WIA_COMPRESSION_NONE;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_NONE"));
|
|
break;
|
|
case TWCP_GROUP4:
|
|
lCompression = WIA_COMPRESSION_G4;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_G4"));
|
|
break;
|
|
case TWCP_JPEG:
|
|
lCompression = WIA_COMPRESSION_JPEG;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_JPEG"));
|
|
break;
|
|
case TWCP_RLE4:
|
|
lCompression = WIA_COMPRESSION_BI_RLE4;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_BI_RLE4"));
|
|
break;
|
|
case TWCP_RLE8:
|
|
lCompression = WIA_COMPRESSION_BI_RLE8;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_BI_RLE8"));
|
|
break;
|
|
case TWCP_GROUP31D:
|
|
case TWCP_GROUP31DEOL:
|
|
case TWCP_GROUP32D:
|
|
lCompression = WIA_COMPRESSION_G3;
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to WIA_COMPRESSION_G3"));
|
|
break;
|
|
case TWCP_LZW:
|
|
case TWCP_JBIG:
|
|
case TWCP_PACKBITS:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
hr = WIA.WritePropertyLong(WIA_IPA_COMPRESSION,lCompression);
|
|
if(FAILED(hr)){
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Setting WIA_IPA_COMPRESSION to %d failed",lCompression));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), data source is not setting CAPID = %x to WIA device (it is not needed)",pCap->GetCapId()));
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
DBG_TRC(("CWiaDataSrc::SetCommonSettings(), Settings were successfully sent to WIA device"));
|
|
} else {
|
|
DBG_ERR(("CWiaDataSrc::SetCommonSettings(), Settings were unsuccessfully sent to WIA device"));
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
TW_UINT16 CWiaDataSrc::GetMemoryTransferBits(BYTE* pImageData)
|
|
{
|
|
DBG_FN_DS(CWiaScannerDS::GetMemoryTransferBits());
|
|
if(!pImageData){
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
BYTE *pBits = pImageData;
|
|
|
|
if (m_MemoryTransferInfo.mtiguidFormat == WiaImgFmt_MEMORYBMP) {
|
|
BITMAPINFOHEADER* pbmh = (BITMAPINFOHEADER*)pImageData;
|
|
if (pbmh) {
|
|
pBits += sizeof(BITMAPINFOHEADER) + (pbmh->biClrUsed * sizeof(RGBQUAD));
|
|
}
|
|
}
|
|
|
|
m_MemoryTransferInfo.mtipBits = pBits;
|
|
|
|
return TWRC_SUCCESS;
|
|
}
|