1921 lines
59 KiB
C++
1921 lines
59 KiB
C++
|
/***************************************************************************
|
|||
|
* TWAIN Data Source Manager version 2.1
|
|||
|
* Manages image acquisition data sources used by a machine.
|
|||
|
* Copyright <EFBFBD> 2007 TWAIN Working Group:
|
|||
|
* Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company,
|
|||
|
* Fujitsu Computer Products of America, JFL Peripheral Solutions Inc.,
|
|||
|
* Ricoh Corporation, and Xerox Corporation.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* This library is free software; you can redistribute it and/or
|
|||
|
* modify it under the terms of the GNU Lesser General Public
|
|||
|
* License as published by the Free Software Foundation; either
|
|||
|
* version 2.1 of the License, or (at your option) any later version.
|
|||
|
*
|
|||
|
* This library is distributed in the hope that it will be useful,
|
|||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|||
|
* Lesser General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU Lesser General Public
|
|||
|
* License along with this library; if not, write to the Free Software
|
|||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
*
|
|||
|
* Contact the TWAIN Working Group by emailing the Technical Subcommittee at
|
|||
|
* twainwg@twain.org or mailing us at 13090 Hwy 9, Suite 3, Boulder Creek, CA 95006.
|
|||
|
*
|
|||
|
***************************************************************************/
|
|||
|
|
|||
|
/**
|
|||
|
* @file apps.cpp
|
|||
|
* Support the Application and Driver data for the Data Source Manager.
|
|||
|
* @author TWAIN Working Group
|
|||
|
* @date March 2007
|
|||
|
*/
|
|||
|
|
|||
|
#include "dsm.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Describes everything we need to know about the Data Source over
|
|||
|
* the course of the session...
|
|||
|
*/
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
TW_IDENTITY Identity; /**< Identity info for data source */
|
|||
|
TW_HANDLE pHandle; /**< returned by LOADLIBRARY(...) */
|
|||
|
DSENTRYPROC DS_Entry; /**< function pointer to the DS_Entry function -- set by dlsym(...) */
|
|||
|
char szPath[FILENAME_MAX]; /**< location of the DS */
|
|||
|
TW_CALLBACK2 twcallback2; /**< callback structure (we're using callback2 because it's 32-bit and 64-bit safe) */
|
|||
|
TW_BOOL bCallbackPending; /**< True if an application is old style and a callback was supposed to be made to it */
|
|||
|
TW_BOOL bDSProcessingMessage; /**< True if the application is still waiting for the DS to return from processing a message */
|
|||
|
TW_BOOL bAppProcessingCallback; /**< True if the application is still waiting for the DS to return from processing a message */
|
|||
|
} DS_INFO;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* We have to manage the horror we created for ourselves when we
|
|||
|
* defined TW_INT32/TW_UINT32 to be long instead of int. We can
|
|||
|
* use the union with any data source on 64-bit Linux and not
|
|||
|
* worry about memory problems. We can then examine the data and
|
|||
|
* decide if we can handle it. Old data sources must be ignored.
|
|||
|
* Anyone who needs to use an old data source has to use the
|
|||
|
* libtwaindsm.so.2.3.1 DSM.
|
|||
|
*/
|
|||
|
typedef struct {
|
|||
|
long Id;
|
|||
|
TW_VERSION Version;
|
|||
|
TW_UINT16 ProtocolMajor;
|
|||
|
TW_UINT16 ProtocolMinor;
|
|||
|
long SupportedGroups;
|
|||
|
TW_STR32 Manufacturer;
|
|||
|
TW_STR32 ProductFamily;
|
|||
|
TW_STR32 ProductName;
|
|||
|
} TW_IDENTITY_LINUX64;
|
|||
|
typedef union {
|
|||
|
TW_IDENTITY twidentity;
|
|||
|
TW_IDENTITY_LINUX64 twidentitylinux64;
|
|||
|
} TW_IDENTITY_LINUX64SAFE;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Structure to hold a list of Data Sources.
|
|||
|
*/
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
TW_UINT16 NumFiles; /**< Number of items in list */
|
|||
|
DS_INFO DSInfo[MAX_NUM_DS]; /**< array of Data Sources */
|
|||
|
} DS_LIST;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Structure to hold data about a connected application, we use
|
|||
|
* DS_LIST so we don't have to allocate memory that we don't
|
|||
|
* need, on the theory that few applications will load more than
|
|||
|
* one driver at a time...
|
|||
|
*/
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
TW_IDENTITY identity; /**< the application's identity. */
|
|||
|
TW_INT16 ConditionCode; /**< the apps condition code. */
|
|||
|
DSM_State CurrentState; /**< the current state of the DSM for this app. */
|
|||
|
DS_LIST *pDSList; /**< Each Application has a list of DS that it discovers each time the app opens the DSM. */
|
|||
|
HWND hwnd; /**< the window that will monitor for events on Windows */
|
|||
|
} APP_INFO;
|
|||
|
|
|||
|
/**
|
|||
|
* Class CAppList implements dynamic array of APP_INFO
|
|||
|
*/
|
|||
|
class CAppList
|
|||
|
{
|
|||
|
private:
|
|||
|
APP_INFO *m_pList; /**< pointer to dynamicaly allocated array */
|
|||
|
TWID_T m_count; /**< number of elements of the array */
|
|||
|
|
|||
|
public:
|
|||
|
|
|||
|
/**
|
|||
|
* Default constructor
|
|||
|
* allocates element 0. Real data starts from element 1, for backward compatibility
|
|||
|
*/
|
|||
|
CAppList()
|
|||
|
{
|
|||
|
m_count=0;
|
|||
|
m_pList=NULL;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Default destructor
|
|||
|
* frees allocated resources
|
|||
|
*/
|
|||
|
~CAppList()
|
|||
|
{
|
|||
|
if(m_pList)
|
|||
|
{
|
|||
|
free(m_pList);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Get number of allocated App slots (Last valid App ID +1)
|
|||
|
* @return number of allocated App slots (Last valid App ID +1)
|
|||
|
*/
|
|||
|
TWID_T size(){return m_count;}
|
|||
|
|
|||
|
/**
|
|||
|
* Get reference to element of the array.
|
|||
|
* Allocate memory if the element does not exist.
|
|||
|
* @param[in] AppId is Application ID/array element index
|
|||
|
* @return reference to the array element
|
|||
|
*/
|
|||
|
APP_INFO& operator[](TWID_T AppId)
|
|||
|
{
|
|||
|
if(AppId>=m_count)
|
|||
|
{
|
|||
|
APP_INFO * pNewList=(APP_INFO *)realloc(m_pList,sizeof(APP_INFO)*(AppId+1));
|
|||
|
if(pNewList==NULL)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"realloc of m_pList failed AppId = %d",AppId));
|
|||
|
return m_pList[0];
|
|||
|
}
|
|||
|
m_pList = pNewList;
|
|||
|
memset(&m_pList[m_count],0,sizeof(APP_INFO)*(AppId+1-m_count));
|
|||
|
m_count = AppId+1;
|
|||
|
}
|
|||
|
|
|||
|
return m_pList[AppId];
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Erase element from the array
|
|||
|
* Unallocate memory if element is last one, else clear CurrentState
|
|||
|
* @param[in] AppId is Application ID/array element index
|
|||
|
* @return true on success
|
|||
|
*/
|
|||
|
bool Erase(TWID_T AppId)
|
|||
|
{
|
|||
|
if((AppId==0) || (AppId>=m_count))
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"AppId = %d is invalid",AppId));
|
|||
|
return false;
|
|||
|
}
|
|||
|
if(AppId==(m_count-1))
|
|||
|
{
|
|||
|
m_count--;
|
|||
|
for(TWID_T i=m_count-1; i>0;i--)
|
|||
|
{
|
|||
|
if(m_pList[i].identity.Id)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
m_count--;
|
|||
|
}
|
|||
|
APP_INFO * pNewList=(APP_INFO *)realloc(m_pList,sizeof(APP_INFO)*m_count);
|
|||
|
if(pNewList==NULL)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"realloc of m_pList failed AppId = %d",AppId));
|
|||
|
return false;
|
|||
|
}
|
|||
|
m_pList = pNewList;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
memset(&m_pList[AppId],0,sizeof(APP_INFO));
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Erase all elements of the array
|
|||
|
* @return true on success
|
|||
|
*/
|
|||
|
bool Clear()
|
|||
|
{
|
|||
|
APP_INFO * pNewList=(APP_INFO *)realloc(m_pList,sizeof(APP_INFO));
|
|||
|
if(pNewList==NULL)
|
|||
|
{
|
|||
|
memset(&m_pList[1],0,sizeof(APP_INFO)*(m_count-1));
|
|||
|
kLOG((kLOGERR,"realloc of m_pList failed"));
|
|||
|
return false;
|
|||
|
}
|
|||
|
m_count=1;
|
|||
|
m_pList = pNewList;
|
|||
|
return true;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Impl Class to hold list of connected applications.
|
|||
|
* In 32bit enviroments each application will connect to a seperate
|
|||
|
* instance of DSM data but with this list it allows ONE application
|
|||
|
* to connect several time, as long as it uses a different name with
|
|||
|
* each connection. I'm still not sure why you'd want to do that,
|
|||
|
* but there it is. This class is intended to hide the gory details
|
|||
|
* of how we're storing the data, so an impl is used.
|
|||
|
*/
|
|||
|
class CTwnDsmAppsImpl
|
|||
|
{
|
|||
|
public:
|
|||
|
|
|||
|
/**
|
|||
|
* Our CTwnDsmAppsImpl constructor.
|
|||
|
*/
|
|||
|
CTwnDsmAppsImpl()
|
|||
|
{
|
|||
|
memset(&pod,0,sizeof(_pod));
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Scan for Data Sources.
|
|||
|
* Recursively navigate the TWAIN datasource dir looking for data sources.
|
|||
|
* Store all valid data sources in _pList upto a maximum of MAX_NUM_DS
|
|||
|
* data sources.
|
|||
|
* @param[in] _szAbsPath starting directory to begin search.
|
|||
|
* @param[out] _pAppId the application requesting scan.
|
|||
|
* @return either EXIT_SUCCESS or EXIT_FAILURE.
|
|||
|
*/
|
|||
|
int scanDSDir(char *_szAbsPath,
|
|||
|
TW_IDENTITY *_pAppId);
|
|||
|
|
|||
|
/**
|
|||
|
* Translates the cc passed in into a string and returns it
|
|||
|
* @param[in] cc the TWAIN Condition Code to translate
|
|||
|
* @return a string that represents the cc
|
|||
|
*/
|
|||
|
const char *StringFromCC(const TW_UINT16 cc);
|
|||
|
|
|||
|
/**
|
|||
|
* Loads a DS from disk and adds it to a global list of DS's.
|
|||
|
* @param[in] _pAppId Origin of message
|
|||
|
* @param[in] _pPath The path to the library to open
|
|||
|
* @param[in] _DsId the source array index
|
|||
|
* @param[in] _boolKeepOpen if set to true keeps DS open after successful load
|
|||
|
* @return a valid TWRC_xxxx return code
|
|||
|
*/
|
|||
|
TW_INT16 LoadDS(TW_IDENTITY *_pAppId,
|
|||
|
char *_pPath,
|
|||
|
TWID_T _DsId,
|
|||
|
bool _boolKeepOpen);
|
|||
|
|
|||
|
/**
|
|||
|
* Set the condition code.
|
|||
|
* @param[in] _pAppId Origin of message
|
|||
|
* @param[in] _ConditionCode new code to remember
|
|||
|
*/
|
|||
|
void AppSetConditionCode(TW_IDENTITY *_pAppId,
|
|||
|
TW_UINT16 _ConditionCode);
|
|||
|
|
|||
|
public:
|
|||
|
// If you add a class in future, declare it here and not in
|
|||
|
// the pod, or the memset in the constructor will ruin your
|
|||
|
// day...
|
|||
|
|
|||
|
/**
|
|||
|
* We use a pod (Pieces of Data) system because it help prevents us from
|
|||
|
* making dumb initialization mistakes.
|
|||
|
*/
|
|||
|
struct _pod
|
|||
|
{
|
|||
|
TW_UINT16 m_conditioncode; /**< we use this if we have no apps. */
|
|||
|
} pod; /**< Pieces of data for CTwnDsmAppsImpl*/
|
|||
|
|
|||
|
CAppList m_AppInfo; /**< list of applications. */
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* The constructor, where we create our implementation object...
|
|||
|
*/
|
|||
|
CTwnDsmApps::CTwnDsmApps()
|
|||
|
{
|
|||
|
m_ptwndsmappsimpl = new CTwnDsmAppsImpl;
|
|||
|
if (!m_ptwndsmappsimpl)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"new of CTwnDsmAppsImpl failed..."));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* The destructor, where we destroy our implementation object...
|
|||
|
*/
|
|||
|
CTwnDsmApps::~CTwnDsmApps()
|
|||
|
{
|
|||
|
if (m_ptwndsmappsimpl)
|
|||
|
{
|
|||
|
// We should not have to go through the list of Apps at this point and
|
|||
|
// close them. The application should close any open DSs and then Close
|
|||
|
// the DSM which should take care of this. But just in case, we will
|
|||
|
// clean up any DS left open.
|
|||
|
for (TWID_T i = 1; i < m_ptwndsmappsimpl->m_AppInfo.size(); i++)
|
|||
|
{
|
|||
|
if( m_ptwndsmappsimpl->m_AppInfo[i].identity.Id
|
|||
|
&& dsmState_Open != m_ptwndsmappsimpl->m_AppInfo[i].CurrentState )
|
|||
|
{
|
|||
|
kLOG((kLOGINFO,"The Application, \"%0.32s\", has left the DSM in an open state when it was unloaded!",
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[i].identity.ProductName));
|
|||
|
|
|||
|
RemoveApp(&m_ptwndsmappsimpl->m_AppInfo[i].identity);
|
|||
|
}
|
|||
|
}
|
|||
|
delete m_ptwndsmappsimpl;
|
|||
|
m_ptwndsmappsimpl = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Add an application.
|
|||
|
* Attempt to add an application to our list. Truth be told we only expect
|
|||
|
* an application to do this once, but for legacy's sake we support the
|
|||
|
* ability to do it more than once, but the Application has to provide a
|
|||
|
* unique ProductName in its Identity. If an Application really has to
|
|||
|
* support multiple drivers, then it's recommended that it do this through
|
|||
|
* seperate processes, since their is no guarantee that any two drivers will
|
|||
|
* operator correctly in the same process...
|
|||
|
*/
|
|||
|
TW_UINT16 CTwnDsmApps::AddApp(TW_IDENTITY *_pAppId,
|
|||
|
TW_MEMREF _MemRef)
|
|||
|
{
|
|||
|
TWID_T ii;
|
|||
|
char szDsm[FILENAME_MAX];
|
|||
|
|
|||
|
// Validate...
|
|||
|
if (_pAppId->ProductName[0] == 0)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"AppId.ProductName is empty"));
|
|||
|
AppSetConditionCode(0,TWCC_BADVALUE);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// Initialize...
|
|||
|
ii = 0;
|
|||
|
memset(szDsm,0,sizeof(szDsm));
|
|||
|
|
|||
|
// Log the entry...
|
|||
|
kLOG((kLOGINFO,"Application: \"%0.32s\"", _pAppId->Manufacturer));
|
|||
|
kLOG((kLOGINFO," \"%0.32s\"", _pAppId->ProductFamily));
|
|||
|
kLOG((kLOGINFO," \"%0.32s\" version: %u.%u", _pAppId->ProductName, _pAppId->Version.MajorNum, _pAppId->Version.MinorNum));
|
|||
|
kLOG((kLOGINFO," TWAIN %u.%u", _pAppId->ProtocolMajor, _pAppId->ProtocolMinor));
|
|||
|
|
|||
|
// An application is identified by the name and handle
|
|||
|
// Check to see if this app has already been opened, and
|
|||
|
// if so, treat it as a sequence error, because this app
|
|||
|
// is already open...
|
|||
|
for (ii = 1; ii < m_ptwndsmappsimpl->m_AppInfo.size(); ii++)
|
|||
|
{
|
|||
|
if ( !strncmp((char*)m_ptwndsmappsimpl->m_AppInfo[ii].identity.ProductName,(char*)_pAppId->ProductName,sizeof(TW_STR32))
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[ii].hwnd == (HWND)(_MemRef?*(HWND*)_MemRef:0) )
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"A successful MSG_OPENDSM was already done for %s...",_pAppId->ProductName));
|
|||
|
AppSetConditionCode(0,TWCC_SEQERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Go through the list and find an empty location
|
|||
|
// Already tested that there is enough room to fit
|
|||
|
for (ii=1; ii < m_ptwndsmappsimpl->m_AppInfo.size(); ii++)
|
|||
|
{
|
|||
|
if (!m_ptwndsmappsimpl->m_AppInfo[ii].identity.Id)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
// The application ID is equal to array index it resides in.
|
|||
|
// We just let the 0-index stay empty...
|
|||
|
_pAppId->Id = (TWIDDEST_T)ii;
|
|||
|
_pAppId->SupportedGroups |= DF_DSM2;
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[ii].identity = *_pAppId;
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[ii].hwnd = (HWND)(_MemRef?*(HWND*)_MemRef:0);
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[ii].pDSList = (DS_LIST*)calloc(sizeof(DS_LIST)+1,1);
|
|||
|
if (!m_ptwndsmappsimpl->m_AppInfo[ii].pDSList)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"calloc failed for %s...",_pAppId->ProductName));
|
|||
|
AppSetConditionCode(0,TWCC_LOWMEMORY);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// Work out the full path to our drivers (if needed)...
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
(void)::GetWindowsDirectory(szDsm,sizeof(szDsm));
|
|||
|
SSTRCAT(szDsm,sizeof(szDsm),"\\");
|
|||
|
SSTRCAT(szDsm,sizeof(szDsm),kTWAIN_DS_DIR);
|
|||
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|||
|
SSTRCPY(szDsm,sizeof(szDsm),kTWAIN_DS_DIR);
|
|||
|
#else
|
|||
|
#error Sorry, we do not recognize this system...
|
|||
|
#endif
|
|||
|
|
|||
|
// Move DSM to state 3 for this app...
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[ii].CurrentState = dsmState_Open;
|
|||
|
|
|||
|
// Recursively navigate the TWAIN datasource dir looking for data sources.
|
|||
|
// Ignor error continue with what we found even if it is nothing
|
|||
|
m_ptwndsmappsimpl->scanDSDir(szDsm,_pAppId);
|
|||
|
|
|||
|
// Maybe one of many DS failed but we still found some.
|
|||
|
AppSetConditionCode(_pAppId, TWCC_SUCCESS);
|
|||
|
|
|||
|
// at this point we can safely add our flag to the caller's
|
|||
|
// application id, but don't bother to do it unless they put
|
|||
|
// their flag in there first...
|
|||
|
if (_pAppId->SupportedGroups & DF_APP2)
|
|||
|
{
|
|||
|
_pAppId->SupportedGroups |= DF_DSM2;
|
|||
|
}
|
|||
|
|
|||
|
// All done...
|
|||
|
return TWRC_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Remove an application.
|
|||
|
* Attempt to remove an application to our list.
|
|||
|
*/
|
|||
|
TW_UINT16 CTwnDsmApps::RemoveApp(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
int nIndex;
|
|||
|
DS_INFO *pDSInfo;
|
|||
|
TW_PENDINGXFERS twpendingxfers;
|
|||
|
TW_USERINTERFACE twuserinterface;
|
|||
|
|
|||
|
// Validate...
|
|||
|
if ( ((TWID_T)_pAppId->Id ==0)
|
|||
|
|| ((TWID_T)_pAppId->Id > m_ptwndsmappsimpl->m_AppInfo.size()))
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"_id is out of range...%d",(int)(TWID_T)_pAppId->Id));
|
|||
|
AppSetConditionCode(0,TWCC_BADVALUE);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// To close the DSM we must be open. I don't really like this
|
|||
|
// piece of code. To my way of thinking a close should always
|
|||
|
// succeed, even if there are mice in the DVD drive and the
|
|||
|
// monitor is on fire. The notion of a failure message during
|
|||
|
// a close is annoying. But there might be a good reason for
|
|||
|
// this I'm not aware of...
|
|||
|
if (dsmState_Open != m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].CurrentState)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO,"%0.32s not open.",(char*)_pAppId->ProductName));
|
|||
|
AppSetConditionCode(0,TWCC_SEQERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// Get rid of our list of drivers...
|
|||
|
if (m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList)
|
|||
|
{
|
|||
|
// Check all the driver slots, if we find an open slot, shotgun it
|
|||
|
// with the sequence of close out commands and shut it down. This
|
|||
|
// really isn't something we should have to do, but it makes us
|
|||
|
// more robust...
|
|||
|
for (nIndex = 1;
|
|||
|
nIndex < m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles;
|
|||
|
nIndex++)
|
|||
|
{
|
|||
|
pDSInfo = &m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[nIndex];
|
|||
|
if (pDSInfo->DS_Entry)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"MSG_CLOSEDSM called with drivers still open."));
|
|||
|
kLOG((kLOGINFO,"The application should not be doing this."));
|
|||
|
kLOG((kLOGINFO,"The DSM is going to try to gracefully shutdown the drivers..."));
|
|||
|
|
|||
|
memset(&twpendingxfers,0,sizeof(twpendingxfers));
|
|||
|
memset(&twuserinterface,0,sizeof(twuserinterface));
|
|||
|
|
|||
|
pDSInfo->DS_Entry(_pAppId,DG_CONTROL,DAT_PENDINGXFERS,MSG_ENDXFER,(TW_MEMREF)&twpendingxfers);
|
|||
|
pDSInfo->DS_Entry(_pAppId,DG_CONTROL,DAT_PENDINGXFERS,MSG_RESET,(TW_MEMREF)&twpendingxfers);
|
|||
|
pDSInfo->DS_Entry(_pAppId,DG_CONTROL,DAT_USERINTERFACE,MSG_DISABLEDS,(TW_MEMREF)&twuserinterface);
|
|||
|
pDSInfo->DS_Entry(_pAppId,DG_CONTROL,DAT_IDENTITY,MSG_CLOSEDS,(TW_MEMREF)&pDSInfo->Identity);
|
|||
|
UnloadDS(_pAppId,nIndex);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Okay, we can blow away the memory now...
|
|||
|
free(m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList);
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList = NULL;
|
|||
|
}
|
|||
|
//Free AppInfo for this App
|
|||
|
m_ptwndsmappsimpl->m_AppInfo.Erase((TWID_T)_pAppId->Id);
|
|||
|
|
|||
|
|
|||
|
// All done...
|
|||
|
return TWRC_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Validate an application's identity.
|
|||
|
* Make sure we're dealing with good data...
|
|||
|
*/
|
|||
|
TW_BOOL CTwnDsmApps::AppValidateId(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
if (!_pAppId)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"_pAppId is null..."));
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if ((TWID_T)_pAppId->Id >= m_ptwndsmappsimpl->m_AppInfo.size())
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"invalid App ID...%d",(int)(TWID_T)_pAppId->Id));
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Validate an application's identity and its DS identity.
|
|||
|
* Make sure we're dealing with good data...
|
|||
|
*/
|
|||
|
TW_BOOL CTwnDsmApps::AppValidateIds(TW_IDENTITY *_pAppId, TW_IDENTITY *_pDSId)
|
|||
|
{
|
|||
|
if(!AppValidateId(_pAppId))
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (!_pDSId)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"_pDSId is null..."));
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if ((TWID_T)_pDSId->Id >= MAX_NUM_DS)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"invalid DS ID...%d",(int)(TWID_T)_pDSId->Id));
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if (!m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"List of DS for app is invalid"));
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if ((TWID_T)_pDSId->Id > m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"The DS ID for app is not valid"));
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Validate an application's identity.
|
|||
|
* Make sure we're dealing with good data...
|
|||
|
*/
|
|||
|
TW_IDENTITY *CTwnDsmApps::AppGetIdentity(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
if (AppValidateId(_pAppId))
|
|||
|
{
|
|||
|
return &m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].identity;
|
|||
|
}
|
|||
|
kLOG((kLOGERR,"bad _pAppId..."));
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Return the condition code and clear it internally.
|
|||
|
* Every open application maintains its own conditioncode, which
|
|||
|
* is going to be used in state 3. In state 4 and higher the
|
|||
|
* condition code is coming from the driver. In state 2 there is
|
|||
|
* no condition code, so we have to use a value that is global to
|
|||
|
* this instance of the DSM. The code is cleared internally,
|
|||
|
* per the specification...
|
|||
|
*/
|
|||
|
TW_UINT16 CTwnDsmApps::AppGetConditionCode(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
TW_UINT16 conditioncode;
|
|||
|
|
|||
|
// Return the application specific value...
|
|||
|
if (AppValidateId(_pAppId))
|
|||
|
{
|
|||
|
conditioncode = m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].ConditionCode;
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].ConditionCode = TWCC_SUCCESS;
|
|||
|
m_ptwndsmappsimpl->pod.m_conditioncode = TWCC_SUCCESS;
|
|||
|
return conditioncode;
|
|||
|
}
|
|||
|
// Uh-oh, we have no app, so return the global value...
|
|||
|
else
|
|||
|
{
|
|||
|
conditioncode = m_ptwndsmappsimpl->pod.m_conditioncode;
|
|||
|
m_ptwndsmappsimpl->pod.m_conditioncode = TWCC_SUCCESS;
|
|||
|
return conditioncode;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Set the condition code.
|
|||
|
* The same rules apply here as they do for AppGetConditionCode.
|
|||
|
* This is the interface function...
|
|||
|
*/
|
|||
|
void CTwnDsmApps::AppSetConditionCode(TW_IDENTITY *_pAppId,
|
|||
|
TW_UINT16 _ConditionCode)
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->AppSetConditionCode(_pAppId,_ConditionCode);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Set the condition code.
|
|||
|
* The same rules apply here as they do for AppGetConditionCode.
|
|||
|
* This is the implemenation function.
|
|||
|
*/
|
|||
|
void CTwnDsmAppsImpl::AppSetConditionCode(TW_IDENTITY *_pAppId,
|
|||
|
TW_UINT16 _ConditionCode)
|
|||
|
{
|
|||
|
// We have no application identity to work with...
|
|||
|
if ( (0 == _pAppId)
|
|||
|
|| (0 == (TWID_T)_pAppId->Id)
|
|||
|
|| (0 == m_AppInfo[(TWID_T)_pAppId->Id].identity.Id))
|
|||
|
{
|
|||
|
pod.m_conditioncode = _ConditionCode;
|
|||
|
}
|
|||
|
|
|||
|
// This is where we normally expect to be...
|
|||
|
else
|
|||
|
{
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].ConditionCode = _ConditionCode;
|
|||
|
}
|
|||
|
|
|||
|
// Make a note of this in the log...
|
|||
|
if (_ConditionCode != TWCC_SUCCESS)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO,"Condition Code: %s",StringFromCC(_ConditionCode)));
|
|||
|
}
|
|||
|
|
|||
|
// All done...
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DSM_State CTwnDsmApps::AppGetState()
|
|||
|
{
|
|||
|
// Initialize to PreSession and update it if we find an application that is further along.
|
|||
|
DSM_State CurrentState = dsmState_PreSession;
|
|||
|
|
|||
|
for (TWID_T AppID = 1; AppID<m_ptwndsmappsimpl->m_AppInfo.size(); AppID++)
|
|||
|
{
|
|||
|
if(m_ptwndsmappsimpl->m_AppInfo[AppID].CurrentState > CurrentState)
|
|||
|
{
|
|||
|
CurrentState = m_ptwndsmappsimpl->m_AppInfo[AppID].CurrentState;
|
|||
|
}
|
|||
|
}
|
|||
|
return CurrentState;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Get our current state.
|
|||
|
* There are really only two states that the DSM can occupy, state
|
|||
|
* 2 where it's loaded, but hasn't had a MSG_OPENDSM done for the
|
|||
|
* specified application identity. And state 3, where a MSG_OPENDSM
|
|||
|
* has been successfully performed...
|
|||
|
*/
|
|||
|
DSM_State CTwnDsmApps::AppGetState(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
// Return the application specific state...
|
|||
|
if (AppValidateId(_pAppId))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].CurrentState;
|
|||
|
}
|
|||
|
// Otherwise we must be in state 2. Good luck ever getting state 1
|
|||
|
// out of the DSM... :)
|
|||
|
else
|
|||
|
{
|
|||
|
return dsmState_Loaded;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Get number of allocated App slots (Last valid App ID +1)
|
|||
|
* @return number of allocated App slots (Last valid App ID +1)
|
|||
|
*/
|
|||
|
TWID_T CTwnDsmApps::AppGetNumApp()
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo.size();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get our hwnd.
|
|||
|
* Windows needs this to help center the user select window...
|
|||
|
*/
|
|||
|
void *CTwnDsmApps::AppHwnd(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
// Return the hwnd...
|
|||
|
if (AppValidateId(_pAppId))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].hwnd;
|
|||
|
}
|
|||
|
// Otherwise return a null...
|
|||
|
else
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get the number of drivers found.
|
|||
|
* When LoadDS() is called during AddApp() we browse for drivers and
|
|||
|
* keep the identity for each one we find. This just tells us how
|
|||
|
* many we found...
|
|||
|
*/
|
|||
|
TWID_T CTwnDsmApps::AppGetNumDs(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
// Return the number of drivers we found...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList)
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles;
|
|||
|
}
|
|||
|
// Not a lot of choice, the value has to be zero...
|
|||
|
else
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get the identity for the specified driver.
|
|||
|
* When LoadDS() is called during AddApp() we browse for drivers and
|
|||
|
* keep the identity for each one we find. This just tells us how
|
|||
|
* many we found...
|
|||
|
*/
|
|||
|
TW_IDENTITY *CTwnDsmApps::DsGetIdentity(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Return a pointer to the driver's identity...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return &m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].Identity;
|
|||
|
}
|
|||
|
// Something is toasted, so return NULL...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning NULL from DsGetIdentity..."));
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get the DS_Entry function for the specified driver.
|
|||
|
* Every driver has to have one of these...
|
|||
|
*/
|
|||
|
DSENTRYPROC CTwnDsmApps::DsGetEntryProc(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Return a pointer to the driver's identity...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].DS_Entry;
|
|||
|
}
|
|||
|
// Something is toasted, so return NULL...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning NULL from DsGetEntryProc..."));
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get the full path and filename for the specified driver.
|
|||
|
* We use this to uniquely identify each driver, since the ProductName
|
|||
|
* is not guaranteed to be unique, though it should be...
|
|||
|
*/
|
|||
|
char *CTwnDsmApps::DsGetPath(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Return a pointer to the driver's file path and name...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].szPath;
|
|||
|
}
|
|||
|
// Something is toasted, so return NULL...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning NULL from DsGetPath..."));
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Get a point to the TW_CALLBACK2 for the specified driver.
|
|||
|
* This is optional for drivers on Windows. On Linux it's the only
|
|||
|
* way to use DAT_NULL to let an Application know about messages
|
|||
|
* going from the Driver to the Application
|
|||
|
*/
|
|||
|
TW_CALLBACK2 *CTwnDsmApps::DsCallback2Get(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Return a pointer to the driver's TW_CALLBACK...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return &m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].twcallback2;
|
|||
|
}
|
|||
|
// Something is toasted, so return NULL...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning NULL from DsCallbackGet..."));
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Check if a callback is waiting.
|
|||
|
* This allows the DSM to help a driver get its message to an
|
|||
|
* application...
|
|||
|
*/
|
|||
|
TW_BOOL CTwnDsmApps::DsCallbackIsWaiting(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Check the waiting flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bCallbackPending;
|
|||
|
}
|
|||
|
// Something is toasted, so return FALSE...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning FALSE from DsCallbackIsWaiting..."));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Set the callback flag.
|
|||
|
* This is how we know when we have something for the Application...
|
|||
|
*/
|
|||
|
void CTwnDsmApps::DsCallbackSetWaiting(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId,
|
|||
|
TW_BOOL _Waiting)
|
|||
|
{
|
|||
|
// Set the waiting flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bCallbackPending = _Waiting;
|
|||
|
}
|
|||
|
// Something is toasted, so whine about it...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Unable to properly handle DsCallbackSetWaiting..."));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Check if the DS is still processing last message.
|
|||
|
* This allows the DSM to prevent DS from recieving a new message
|
|||
|
* when they have not finished the current
|
|||
|
*/
|
|||
|
TW_BOOL CTwnDsmApps::DsIsProcessingMessage(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Check the waiting flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bDSProcessingMessage;
|
|||
|
}
|
|||
|
// Something is toasted, so return FALSE...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning FALSE from DsIsProcessingMessage..."));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Set the ProcessingMessage flag.
|
|||
|
* This is how we know the DS is not done processing the previous message
|
|||
|
*/
|
|||
|
void CTwnDsmApps::DsSetProcessingMessage(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId,
|
|||
|
TW_BOOL _Processing)
|
|||
|
{
|
|||
|
// Set the processing flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bDSProcessingMessage = _Processing;
|
|||
|
}
|
|||
|
// Something is toasted, so whine about it...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Unable to properly handle DsSetProcessingMessage..."));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Check if the App is still processing last callback.
|
|||
|
* This allows the DSM to prevent the app from calling the DS with a
|
|||
|
* message before it returns from recieving the last callback.
|
|||
|
*/
|
|||
|
TW_BOOL CTwnDsmApps::DsIsAppProcessingCallback(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Check the waiting flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
return m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bAppProcessingCallback;
|
|||
|
}
|
|||
|
// Something is toasted, so return FALSE...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning FALSE from DsIsAppProcessingCallback..."));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Set the AppProcessingCallback flag.
|
|||
|
* This is how we know the App is not done processing the previous callback
|
|||
|
*/
|
|||
|
void CTwnDsmApps::DsSetAppProcessingCallback(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId,
|
|||
|
TW_BOOL _Processing)
|
|||
|
{
|
|||
|
// Set the processing flag...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].bAppProcessingCallback = _Processing;
|
|||
|
}
|
|||
|
// Something is toasted, so whine about it...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Unable to properly handle DsSetAppProcessingCallback..."));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Turn a TWCC_ condition code into a string...
|
|||
|
*/
|
|||
|
const char *CTwnDsmAppsImpl::StringFromCC(const TW_UINT16 cc)
|
|||
|
{
|
|||
|
switch(cc)
|
|||
|
{
|
|||
|
case TWCC_SUCCESS:
|
|||
|
return "TWRC_SUCCESS";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_BUMMER:
|
|||
|
return "Failure due to unknown causes";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_LOWMEMORY:
|
|||
|
return "Not enough memory to perform operation";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_NODS:
|
|||
|
return "No Data Source";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_MAXCONNECTIONS:
|
|||
|
return "DS is connected to max possible applications";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_OPERATIONERROR:
|
|||
|
return "DS or DSM reported error, application shouldn't display an error";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_BADCAP:
|
|||
|
return "Unknown capability";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_BADPROTOCOL:
|
|||
|
return "Unrecognized MSG DG DAT combination";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_BADVALUE:
|
|||
|
return "Data parameter out of range";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_SEQERROR:
|
|||
|
return "DG DAT MSG out of expected sequence";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_BADDEST:
|
|||
|
return "Unknown destination Application/Source in DSM_Entry";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_CAPUNSUPPORTED:
|
|||
|
return "Capability not supported by source";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_CAPBADOPERATION:
|
|||
|
return "Operation not supported by capability";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_CAPSEQERROR:
|
|||
|
return "Capability has dependancy on other capability";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_DENIED:
|
|||
|
return "File System operation is denied (file is protected)";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_FILEEXISTS:
|
|||
|
return "Operation failed because file already exists.";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_FILENOTFOUND:
|
|||
|
return "File not found";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_NOTEMPTY:
|
|||
|
return "Operation failed because directory is not empty";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_PAPERJAM:
|
|||
|
return "The feeder is jammed";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_PAPERDOUBLEFEED:
|
|||
|
return "The feeder detected multiple pages";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_FILEWRITEERROR:
|
|||
|
return "Error writing the file (meant for things like disk full conditions)";
|
|||
|
break;
|
|||
|
|
|||
|
case TWCC_CHECKDEVICEONLINE:
|
|||
|
return "The device went offline prior to or during this operation";
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
static TW_STR32 hex;
|
|||
|
SSNPRINTF((char*)hex, NCHARS(hex), 32, "TWCC 0x%04x", cc);
|
|||
|
|
|||
|
return (char*)hex;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Find all of the drivers.
|
|||
|
* We recursively descend into the driver directory, looking for
|
|||
|
* files with a .ds extension, opening them and getting their
|
|||
|
* TW_IDENTITY. Which is why it's critical that drivers do as
|
|||
|
* little as possible during this operation. It's easy to know
|
|||
|
* when it's happening, because the application's TW_IDENTITY is
|
|||
|
* empty, which is the hint that the DSM is browsing...
|
|||
|
*/
|
|||
|
int CTwnDsmAppsImpl::scanDSDir(char *_szAbsPath,
|
|||
|
TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
// Validate...
|
|||
|
if ( !_szAbsPath
|
|||
|
|| !_pAppId)
|
|||
|
{
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Take care of VC++...
|
|||
|
//
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
WIN32_FIND_DATA FileData; // Data structure describes the file found
|
|||
|
HANDLE hSearch; // Search handle returned by FindFirstFile
|
|||
|
char szABSFilename[FILENAME_MAX];
|
|||
|
BOOL bFinished = FALSE;
|
|||
|
char szPrevWorkDir[FILENAME_MAX];
|
|||
|
|
|||
|
// Start searching for .ds files in the root directory.
|
|||
|
SSTRCPY(szABSFilename, NCHARS(szABSFilename), _szAbsPath);
|
|||
|
SSTRCAT(szABSFilename, NCHARS(szABSFilename), "\\*.ds");
|
|||
|
hSearch = FindFirstFile(szABSFilename,&FileData);
|
|||
|
|
|||
|
// If we find something, squirrel it away and anything else we find...
|
|||
|
if (hSearch != INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
/* Save the current working directory: */
|
|||
|
char *szResult = _getcwd( szPrevWorkDir, sizeof(szPrevWorkDir) );
|
|||
|
if (szResult == (char*)NULL)
|
|||
|
{
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
int iResult = _chdir(_szAbsPath);
|
|||
|
if (iResult != 0)
|
|||
|
{
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
while (!bFinished)
|
|||
|
{
|
|||
|
if (SSNPRINTF(szABSFilename, NCHARS(szABSFilename), FILENAME_MAX, "%s\\%s", _szAbsPath, FileData.cFileName) > 0)
|
|||
|
{
|
|||
|
if (TWRC_SUCCESS == LoadDS(_pAppId,
|
|||
|
szABSFilename,
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles+1,
|
|||
|
false))
|
|||
|
{
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!FindNextFile(hSearch, &FileData))
|
|||
|
{
|
|||
|
bFinished = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!FindClose (hSearch))
|
|||
|
{
|
|||
|
(void)_chdir( szPrevWorkDir );
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Start searching sub directories.
|
|||
|
SSTRCPY(szABSFilename, NCHARS(szABSFilename), _szAbsPath);
|
|||
|
SSTRCAT(szABSFilename, NCHARS(szABSFilename), "\\*.*");
|
|||
|
hSearch = FindFirstFile(szABSFilename, &FileData);
|
|||
|
bFinished = FALSE;
|
|||
|
if (hSearch == INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
(void)_chdir( szPrevWorkDir );
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
while (!bFinished)
|
|||
|
{
|
|||
|
if ( (strcmp(".", FileData.cFileName) != 0)
|
|||
|
&& (strcmp("..", FileData.cFileName) != 0)
|
|||
|
&& (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
|
|||
|
{
|
|||
|
if (SSNPRINTF(szABSFilename, NCHARS(szABSFilename), FILENAME_MAX, "%s\\%s", _szAbsPath, FileData.cFileName) > 0)
|
|||
|
{
|
|||
|
scanDSDir(szABSFilename,_pAppId);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!FindNextFile(hSearch, &FileData))
|
|||
|
{
|
|||
|
bFinished = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
(void)_chdir( szPrevWorkDir );
|
|||
|
|
|||
|
if (!FindClose (hSearch))
|
|||
|
{
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
return EXIT_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Take care of g++...
|
|||
|
//
|
|||
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|||
|
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
|
|||
|
|
|||
|
char szABSFilename[FILENAME_MAX];
|
|||
|
DIR *pdir;
|
|||
|
|
|||
|
// Initialize...
|
|||
|
pdir = 0;
|
|||
|
memset(szABSFilename,0,sizeof(szABSFilename));
|
|||
|
|
|||
|
// Open the directory...
|
|||
|
if ((pdir=opendir(_szAbsPath)) == 0)
|
|||
|
{
|
|||
|
perror("opendir");
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
struct dirent *pfile;
|
|||
|
while(errno=0, ((pfile=readdir(pdir)) != 0))
|
|||
|
{
|
|||
|
if ( (strcmp(".", pfile->d_name) == 0)
|
|||
|
|| (strcmp("..", pfile->d_name) == 0) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (SNPRINTF(szABSFilename,FILENAME_MAX,"%s/%s",_szAbsPath,pfile->d_name) < 0)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
struct stat st;
|
|||
|
if (lstat(szABSFilename, &st) < 0)
|
|||
|
{
|
|||
|
perror("lstat");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (S_ISDIR(st.st_mode) && (0 != strstr(pfile->d_name, ".ds")))
|
|||
|
{
|
|||
|
if (TWRC_SUCCESS == LoadDS(_pAppId,
|
|||
|
szABSFilename,
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles+1,
|
|||
|
false))
|
|||
|
{
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (0 != errno)
|
|||
|
{
|
|||
|
perror("readdir");
|
|||
|
}
|
|||
|
|
|||
|
closedir(pdir);
|
|||
|
return EXIT_SUCCESS;
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
char szABSFilename[FILENAME_MAX];
|
|||
|
DIR *pdir;
|
|||
|
|
|||
|
// Initialize...
|
|||
|
pdir = 0;
|
|||
|
memset(szABSFilename,0,sizeof(szABSFilename));
|
|||
|
|
|||
|
// Open the directory...
|
|||
|
if ((pdir=opendir(_szAbsPath)) == 0)
|
|||
|
{
|
|||
|
perror("opendir");
|
|||
|
return EXIT_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
struct dirent *pfile;
|
|||
|
while(errno=0, ((pfile=readdir(pdir)) != 0))
|
|||
|
{
|
|||
|
if ( (strcmp(".", pfile->d_name) == 0)
|
|||
|
|| (strcmp("..", pfile->d_name) == 0) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (SNPRINTF(szABSFilename,FILENAME_MAX,"%s/%s",_szAbsPath,pfile->d_name) < 0)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
struct stat st;
|
|||
|
if (lstat(szABSFilename, &st) < 0)
|
|||
|
{
|
|||
|
perror("lstat");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (S_ISDIR(st.st_mode))
|
|||
|
{
|
|||
|
scanDSDir(szABSFilename,_pAppId);
|
|||
|
}
|
|||
|
else if (S_ISREG(st.st_mode) && (0 != strstr(pfile->d_name, ".ds")))
|
|||
|
{
|
|||
|
if (TWRC_SUCCESS == LoadDS(_pAppId,
|
|||
|
szABSFilename,
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles+1,
|
|||
|
false))
|
|||
|
{
|
|||
|
m_AppInfo[(TWID_T)_pAppId->Id].pDSList->NumFiles++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (0 != errno)
|
|||
|
{
|
|||
|
perror("readdir");
|
|||
|
}
|
|||
|
|
|||
|
closedir(pdir);
|
|||
|
return EXIT_SUCCESS;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// meh!
|
|||
|
//
|
|||
|
#else
|
|||
|
#error Sorry, we do not recognize this system...
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Load a specific driver.
|
|||
|
* This is the interface function that's called by CTwnDsm
|
|||
|
*/
|
|||
|
TW_INT16 CTwnDsmApps::LoadDS(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
// Load the specified driver...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS))
|
|||
|
{
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
// Make the DS directory the current directoy while we load the DS so that any DLLs that
|
|||
|
// are loaded with the DS can be found.
|
|||
|
char *szResult;
|
|||
|
char szPrevWorkDir[FILENAME_MAX];
|
|||
|
char szWorkDir[FILENAME_MAX];
|
|||
|
|
|||
|
SSTRCPY(szWorkDir, NCHARS(szWorkDir), m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].szPath);
|
|||
|
// strip filename from path
|
|||
|
size_t x = strlen(szWorkDir);
|
|||
|
while(x > 0)
|
|||
|
{
|
|||
|
if(PATH_SEPERATOR == szWorkDir[x-1])
|
|||
|
{
|
|||
|
szWorkDir[x-1] = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
--x;
|
|||
|
}
|
|||
|
/* Save the current working directory: */
|
|||
|
memset( szPrevWorkDir, 0, NCHARS(szPrevWorkDir) );
|
|||
|
szResult = _getcwd( szPrevWorkDir, NCHARS(szPrevWorkDir) );
|
|||
|
if (!szResult)
|
|||
|
{
|
|||
|
kLOG((kLOGERR, "_getcwd failed..."));
|
|||
|
}
|
|||
|
(void)_chdir( szWorkDir );
|
|||
|
#endif
|
|||
|
|
|||
|
TW_INT16 result = m_ptwndsmappsimpl->LoadDS(_pAppId,
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].szPath,
|
|||
|
_DsId,
|
|||
|
true);
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
if(0!=strlen(szPrevWorkDir))
|
|||
|
{
|
|||
|
(void)_chdir( szPrevWorkDir );
|
|||
|
}
|
|||
|
#endif
|
|||
|
return result;
|
|||
|
}
|
|||
|
// Something is toasted, so return LoadDS...
|
|||
|
else
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Returning TWRC_FAILURE from LoadDS..."));
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Load a driver.
|
|||
|
* This is the implementation function. We use this both to browse
|
|||
|
* for drivers during MSG_GETFIRST/MSG_GETNEXT and to load a specific
|
|||
|
* driver during MSG_OPENDS. Which is why we need the path and the
|
|||
|
* keep open flag...
|
|||
|
*/
|
|||
|
TW_INT16 CTwnDsmAppsImpl::LoadDS(TW_IDENTITY *_pAppId,
|
|||
|
char *_pPath,
|
|||
|
TWID_T _DsId,
|
|||
|
bool _boolKeepOpen)
|
|||
|
{
|
|||
|
TW_INT16 result = TWRC_SUCCESS;
|
|||
|
DS_INFO *pDSInfo;
|
|||
|
bool hook;
|
|||
|
TW_IDENTITY_LINUX64SAFE twidentitylinux64safe;
|
|||
|
char szUseAppid[8];
|
|||
|
|
|||
|
// Validate...
|
|||
|
if ( 0 == _pPath )
|
|||
|
{
|
|||
|
// bad path
|
|||
|
kLOG((kLOGERR,"bad path."));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
if ( _DsId >= MAX_NUM_DS )
|
|||
|
{
|
|||
|
// too many DS's already open
|
|||
|
kLOG((kLOGINFO,"Too many DS's already open."));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// For Linux we only support the native architecture, so if
|
|||
|
// a 32-bit process tries to run on an x86_64 system we expect
|
|||
|
// it to fail. However, we can't rule out that somebody
|
|||
|
// might try to make this work. So we'll check the file...
|
|||
|
#if (TWNDSM_OS == TWNDSM_OS_LINUX)
|
|||
|
(void)hook;
|
|||
|
bool blSuccess = false;
|
|||
|
char szData[2048] = { 0 };
|
|||
|
snprintf(szData, sizeof(szData), "file \"%s\"", _pPath);
|
|||
|
FILE *pf = popen(szData, "r");
|
|||
|
if (pf)
|
|||
|
{
|
|||
|
szData[0] = 0;
|
|||
|
size_t sizet = fread(szData, 1, sizeof(szData), pf);
|
|||
|
szData[sizet] = 0;
|
|||
|
#if (TWNDSM_OS_64BIT == 1)
|
|||
|
blSuccess = strstr(szData, "x86-64") || strstr(szData, "MIPS64") || strstr(szData, "aarch64");
|
|||
|
#else
|
|||
|
blSuccess = strstr(szData, "Intel 80386");
|
|||
|
#endif
|
|||
|
pclose(pf);
|
|||
|
pf = 0;
|
|||
|
}
|
|||
|
if (!blSuccess)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO, "driver doesn't support architecture: %s <%s>", _pPath, szData));
|
|||
|
AppSetConditionCode(_pAppId, TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Mac has universal binaries which may or may not have what we
|
|||
|
// need. For instance, we can't load an image that only has
|
|||
|
// i386 content if we're running in an x86_64 process. So we're
|
|||
|
// using file(1) to filter content...
|
|||
|
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
|
|||
|
bool blSuccess = false;
|
|||
|
char szData[2048] = { 0 };
|
|||
|
snprintf(szData, sizeof(szData), "file \"%s/Contents/MacOS/$(/usr/libexec/PlistBuddy -c 'Print CFBundleExecutable' '%s/Contents/Info.plist')\"", _pPath, _pPath);
|
|||
|
FILE *pf = popen(szData, "r");
|
|||
|
if (pf)
|
|||
|
{
|
|||
|
szData[0] = 0;
|
|||
|
size_t sizet = fread(szData, 1, sizeof(szData), pf);
|
|||
|
szData[sizet] = 0;
|
|||
|
#if (TWNDSM_OS_64BIT == 1)
|
|||
|
blSuccess = (strstr(szData, "x86_64") != 0);
|
|||
|
#else
|
|||
|
blSuccess = (strstr(szData, "i386") != 0);
|
|||
|
#endif
|
|||
|
pclose(pf);
|
|||
|
pf = 0;
|
|||
|
}
|
|||
|
if (!blSuccess)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO, "driver doesn't support architecture: %s <%s>", _pPath, szData));
|
|||
|
AppSetConditionCode(_pAppId, TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Initialize stuff...
|
|||
|
pDSInfo = &m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId];
|
|||
|
|
|||
|
// Only log DS details when processing a MSG_OPENDS message
|
|||
|
if(_boolKeepOpen)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO,"Datasource: \"%0.32s\"", pDSInfo->Identity.Manufacturer));
|
|||
|
kLOG((kLOGINFO," \"%0.32s\"", pDSInfo->Identity.ProductFamily));
|
|||
|
kLOG((kLOGINFO," \"%0.32s\" version: %u.%u", pDSInfo->Identity.ProductName, pDSInfo->Identity.Version.MajorNum, pDSInfo->Identity.Version.MinorNum));
|
|||
|
kLOG((kLOGINFO," TWAIN %u.%u", pDSInfo->Identity.ProtocolMajor, pDSInfo->Identity.ProtocolMinor));
|
|||
|
}
|
|||
|
|
|||
|
// Only hook this driver if we've been asked to keep the driver
|
|||
|
// open (meaning we're processing a MSG_OPENDS) and if we see
|
|||
|
// that the driver is 1.x...(by checking the absence of DF_DS2)
|
|||
|
hook = _boolKeepOpen && !(pDSInfo->Identity.SupportedGroups & DF_DS2);
|
|||
|
|
|||
|
// Try to load the driver... We load the driver again if we are keeping
|
|||
|
// it open. This LoadLibrary is always closed so we dont hook this time.
|
|||
|
pDSInfo->pHandle = (TW_HANDLE)LOADLIBRARY(_pPath,false,0);
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
if (0 == pDSInfo->pHandle)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Could not load library: %s",_pPath));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|||
|
if (0 == pDSInfo->pHandle)
|
|||
|
{
|
|||
|
// This is a bit skanky, and not the sort of thing I really want
|
|||
|
// a user to have to see, but more info is better than less, so
|
|||
|
// hopefully someone will be able to sort out what the cryptic
|
|||
|
// message means and we can FAQ it...
|
|||
|
fprintf(stderr,">>> error loading <%s>\r\n",_pPath);
|
|||
|
fprintf(stderr,">>> %s\r\n",dlerror());
|
|||
|
fprintf(stderr,">>> please contact your scanner or driver vendor for more\r\n");
|
|||
|
fprintf(stderr,">>> help, if that doesn't help then check out the FAQ at\r\n");
|
|||
|
fprintf(stderr,">>> http://www.twain.org\r\n");
|
|||
|
kLOG((kLOGERR,"Could not load library: %s",_pPath));
|
|||
|
kLOG((kLOGERR,dlerror()));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#else
|
|||
|
#error Sorry, we do not recognize this system...
|
|||
|
#endif
|
|||
|
|
|||
|
// Try to get the entry point...
|
|||
|
pDSInfo->DS_Entry = (DSENTRYPROC)DSM_LoadFunction(pDSInfo->pHandle,"DS_Entry");
|
|||
|
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
// The WIATwain.ds does not have an entry point
|
|||
|
if (0 != strstr(_pPath, "wiatwain.ds"))
|
|||
|
{
|
|||
|
kLOG((kLOGERR, "We're deliberately skipping this file: %s", _pPath));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pDSInfo->DS_Entry = (DSENTRYPROC)GetProcAddress((HMODULE)pDSInfo->pHandle, MAKEINTRESOURCE(1));
|
|||
|
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO, "Could not find Entry 1 in DS: %s", _pPath));
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
kLOG((kLOGERR, "Could not find DS_Entry function in DS: %s", _pPath));
|
|||
|
#endif
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle, false, 0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
AppSetConditionCode(_pAppId, TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Allllrighty then! So the original TWAIN_32.DLL passes in
|
|||
|
// a value of NULL for the origin. This is not documented
|
|||
|
// anywhere in the TWAIN Spec. It was decided to maintain this
|
|||
|
// behavior in TWAINDSM.DLL. All fine and well for Window and
|
|||
|
// Linux. But Mac had it's own DSM, and it didn't pass in a
|
|||
|
// NULL. So now we have a conundrum.
|
|||
|
//
|
|||
|
// I'm adding an event variable so that an application can
|
|||
|
// override stuff, but the default behavior is going to be:
|
|||
|
// Windows - NULL
|
|||
|
// Linux - _pAppId
|
|||
|
// Mac - _pAppId
|
|||
|
memset(&szUseAppid, 0, sizeof(szUseAppid));
|
|||
|
SGETENV(szUseAppid, NCHARS(szUseAppid), "TWAINDSM_USEAPPID");
|
|||
|
// No data received, set the default based on the platform...
|
|||
|
if (szUseAppid[0] != 0)
|
|||
|
{
|
|||
|
#if (TWNDSM_OS == TWNDSM_OS_WINDOWS)
|
|||
|
szUseAppid[0] = '0'; // Windows is NULL
|
|||
|
#elif (TWNDSM_OS == TWNDSM_OS_LINUX)
|
|||
|
szUseAppid[0] = '1'; // Linux is _pAppId
|
|||
|
#elif (TWNDSM_OS == TWNDSM_OS_MACOSX)
|
|||
|
szUseAppid[0] = '1'; // Linux is _pAppId
|
|||
|
#else
|
|||
|
Unsupported...
|
|||
|
#endif
|
|||
|
}
|
|||
|
// Otherwise, force the value to be '0' or '1'...
|
|||
|
else if (szUseAppid[0] != '0')
|
|||
|
{
|
|||
|
szUseAppid[0] = '1';
|
|||
|
}
|
|||
|
|
|||
|
// Report success and squirrel away the index...
|
|||
|
kLOG((kLOGINFO, "Loaded library: %s (TWAINDSM_USEAPPID:%c)", _pPath, szUseAppid[0]));
|
|||
|
pDSInfo->Identity.Id = (TWIDDEST_T)_DsId;
|
|||
|
|
|||
|
// Get the source to fill in the identity structure
|
|||
|
// This operation should never fail on any DS
|
|||
|
//
|
|||
|
// We need the NULL to be backwards compatible with the
|
|||
|
// older DSM. This is the only way a driver can tell if
|
|||
|
// it's being talked to directly by the DSM instead of
|
|||
|
// by the application (with the DSM as a passthru).
|
|||
|
//
|
|||
|
// Okay, this is where we make the actual call. I left
|
|||
|
// the original comments in place...
|
|||
|
memset(&twidentitylinux64safe, 0, sizeof(twidentitylinux64safe));
|
|||
|
twidentitylinux64safe.twidentity.Id = (TWIDDEST_T)_DsId;
|
|||
|
if (szUseAppid[0] == '1')
|
|||
|
{
|
|||
|
// this is what the spec calls for
|
|||
|
result = pDSInfo->DS_Entry(_pAppId, DG_CONTROL, DAT_IDENTITY, MSG_GET, (TW_MEMREF)&twidentitylinux64safe);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// this is out of spec, but we need it for Windows
|
|||
|
result = pDSInfo->DS_Entry(NULL, DG_CONTROL, DAT_IDENTITY, MSG_GET, (TW_MEMREF)&twidentitylinux64safe);
|
|||
|
}
|
|||
|
if (result != TWRC_SUCCESS)
|
|||
|
{
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle,false,0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
pDSInfo->DS_Entry = NULL;
|
|||
|
kLOG((kLOGINFO, "DG_CONTROL,DAT_IDENTITY,MSG_GET failed"));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// We're going to do a sanity check on the data if we
|
|||
|
// are running on Linux as a 64-bit process. This is
|
|||
|
// because we messed up the definition of TW_INT32 and
|
|||
|
// TW_UINT32, making them 64-bit values (based on long)
|
|||
|
// rather than 32-bit values (based on int). Starting
|
|||
|
// with TWAIN 2.4 this is fixed, but we have to be able
|
|||
|
// to handle old drivers. These will be in trouble
|
|||
|
// because their Id and SupportedGroups will be 64-bit,
|
|||
|
// shifting data in the structure.
|
|||
|
//
|
|||
|
// This is a heuristic, meaning that it's possible to
|
|||
|
// get it wrong. Add as many checks as possible. All
|
|||
|
// TWAIN drivers must support DG_CONTROL and DG_IMAGE,
|
|||
|
// and we're going to validate a whole mess of protocol
|
|||
|
// versions...
|
|||
|
#if (TWNDSM_OS == TWNDSM_OS_LINUX) && (TWNDSM_OS_64BIT == 1)
|
|||
|
if ( ((twidentitylinux64safe.twidentity.SupportedGroups & (DG_CONTROL | DG_IMAGE)) == (DG_CONTROL | DG_IMAGE))
|
|||
|
&& (((twidentitylinux64safe.twidentity.ProtocolMajor >= 3) && (twidentitylinux64safe.twidentity.ProtocolMinor <= 9))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 2) && (twidentitylinux64safe.twidentity.ProtocolMinor >= 4) && (twidentitylinux64safe.twidentity.ProtocolMinor <= 9))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 5))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 6))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 7))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 8))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 9))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 1) && (twidentitylinux64safe.twidentity.ProtocolMinor == 91))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 2) && (twidentitylinux64safe.twidentity.ProtocolMinor == 0))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 2) && (twidentitylinux64safe.twidentity.ProtocolMinor == 1))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 2) && (twidentitylinux64safe.twidentity.ProtocolMinor == 2))
|
|||
|
|| ((twidentitylinux64safe.twidentity.ProtocolMajor == 2) && (twidentitylinux64safe.twidentity.ProtocolMinor == 3))))
|
|||
|
{
|
|||
|
// We're good, keep going...
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle,false,0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
pDSInfo->DS_Entry = NULL;
|
|||
|
kLOG((kLOGINFO,"DG_CONTROL,DAT_IDENTITY,MSG_GET failed (rejected as old 64-bit TW_INT32/TW_UINT32)"));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Okay, we can keep this TW_IDENTITY, so copy it over,
|
|||
|
// but be careful to use the TW_IDENTITY size...
|
|||
|
memcpy(&pDSInfo->Identity, &twidentitylinux64safe.twidentity, sizeof(pDSInfo->Identity));
|
|||
|
|
|||
|
// Compare the supported groups. Note that the & is correct
|
|||
|
// because we are comparing bits...
|
|||
|
// we do not want to compare DG_CONTROL because is it supported by all
|
|||
|
if ( !( (_pAppId->SupportedGroups & DG_MASK & ~DG_CONTROL) // app supports
|
|||
|
& (pDSInfo->Identity.SupportedGroups & DG_MASK & ~DG_CONTROL) ) ) // source supports
|
|||
|
{
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle,false,0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
pDSInfo->DS_Entry = NULL;
|
|||
|
kLOG((kLOGINFO,"The SupportedGroups do not match."));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
// The DS should not modify the Id even though the spec states
|
|||
|
// that the id will not be assigned until DSM sends MSG_OPENDS to DS, and
|
|||
|
// by the way...don't do the copy of the src and dst are the same address...
|
|||
|
pDSInfo->Identity.Id = (TWIDDEST_T)_DsId;
|
|||
|
if (pDSInfo->szPath != _pPath)
|
|||
|
{
|
|||
|
SSTRNCPY(pDSInfo->szPath, NCHARS(pDSInfo->szPath),_pPath,FILENAME_MAX);
|
|||
|
}
|
|||
|
|
|||
|
// We clear the library to avoid cluttering up the virtual address space, and
|
|||
|
// to prevent scary weirdness that can result from multiple drivers being
|
|||
|
// loaded (if the application wants to load multiple drivers, that's its risk).
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle,false,0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
pDSInfo->DS_Entry = NULL;
|
|||
|
|
|||
|
// At this point you're probably scratching your head. Here's the deal.
|
|||
|
// When the DSM issues DG_CONTROL/DAT_IDENTITY/MSG_GET without an
|
|||
|
// AppIdentity structure it alerts the driver that it's being called by
|
|||
|
// the DSM and not by the application, most likely to bring up the user
|
|||
|
// selection dialog. A driver should use this information to create --
|
|||
|
// and more importantly -- to destroy its internal data structures,
|
|||
|
// because it will get no other chance to clean itself up.
|
|||
|
//
|
|||
|
// It's worth interjecting at this point that Microsoft warns against
|
|||
|
// any but the most minimal activity in DllMain, so relying on doing
|
|||
|
// the create/destroy in there is very risky. The same goes for the
|
|||
|
// __attribute(constructor)/__attribute(destructor) with GNU.
|
|||
|
//
|
|||
|
// The problem is that the DSM issues DG_CONTROL/DAT_IDENTITY/MSG_GET
|
|||
|
// just prior to DG_CONTROL/DAT_IDENTITY/MSG_OPEN. If a driver is keyed
|
|||
|
// to the AppIdentity being NULL, it'll incorrectly clean itself up.
|
|||
|
//
|
|||
|
// This means we need to unload and reload the library, to give the
|
|||
|
// driver a consistent look.
|
|||
|
if (_boolKeepOpen == true)
|
|||
|
{
|
|||
|
pDSInfo->pHandle = (TW_HANDLE)LOADLIBRARY(_pPath,hook,_DsId);
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
if (0 == pDSInfo->pHandle)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"Could not load library: %s",_pPath));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|||
|
if (0 == pDSInfo->pHandle)
|
|||
|
{
|
|||
|
// This is a bit skanky, and not the sort of thing I really want
|
|||
|
// a user to have to see, but more info is better than less, so
|
|||
|
// hopefully someone will be able to sort out what the cryptic
|
|||
|
// message means and we can FAQ it...
|
|||
|
fprintf(stderr,">>> error loading <%s>\r\n",_pPath);
|
|||
|
fprintf(stderr,">>> %s\r\n",dlerror());
|
|||
|
fprintf(stderr,">>> please contact your scanner or driver vendor for more\r\n");
|
|||
|
fprintf(stderr,">>> help, if that doesn't help then check out the FAQ at\r\n");
|
|||
|
fprintf(stderr,">>> http://www.twain.org\r\n");
|
|||
|
kLOG((kLOGERR,"Could not load library: %s",_pPath));
|
|||
|
kLOG((kLOGERR,dlerror()));
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
#else
|
|||
|
#error Sorry, we do not recognize this system...
|
|||
|
#endif
|
|||
|
|
|||
|
// Try to get the entry point...
|
|||
|
pDSInfo->DS_Entry = (DSENTRYPROC)DSM_LoadFunction(pDSInfo->pHandle,"DS_Entry");
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
// The WIATwain.ds does not have an entry point
|
|||
|
if(0 != strstr(_pPath, "wiatwain.ds"))
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"We're deliberately skipping this file: %s",_pPath));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pDSInfo->DS_Entry = (DSENTRYPROC)GetProcAddress((HMODULE)pDSInfo->pHandle, MAKEINTRESOURCE(1));
|
|||
|
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
kLOG((kLOGINFO,"Could not find Entry 1 in DS: %s",_pPath));
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
kLOG((kLOGERR,"Could not find DS_Entry function in DS: %s",_pPath));
|
|||
|
#endif
|
|||
|
if (pDSInfo->DS_Entry == 0)
|
|||
|
{
|
|||
|
(void)UNLOADLIBRARY(pDSInfo->pHandle,false,0);
|
|||
|
pDSInfo->pHandle = NULL;
|
|||
|
AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
|
|||
|
return TWRC_FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// All done...
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Unload a specific driver.
|
|||
|
* I don't care if the called sends this function a bouquet of pink
|
|||
|
* bunnies, I think that close type functions shouldn't fail (unless,
|
|||
|
* of course they're doing something vital, in which case that
|
|||
|
* vital activity shouldn't be in the close function in the first
|
|||
|
* place -- so there)...
|
|||
|
*/
|
|||
|
void CTwnDsmApps::UnloadDS(TW_IDENTITY *_pAppId,
|
|||
|
TWID_T _DsId)
|
|||
|
{
|
|||
|
int retval = 0;
|
|||
|
|
|||
|
// Unload the specified driver...
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList
|
|||
|
&& (_DsId < MAX_NUM_DS)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].pHandle)
|
|||
|
{
|
|||
|
// Unload the library...
|
|||
|
retval = UNLOADLIBRARY(m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].pHandle,true,_DsId);
|
|||
|
|
|||
|
// Log if something bad happens...
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
if(0 == retval)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"failed to unload datasource"));
|
|||
|
}
|
|||
|
#else
|
|||
|
if(0 != retval)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"dlclose: %s",dlerror()));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].DS_Entry = 0;
|
|||
|
m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].pDSList->DSInfo[_DsId].pHandle = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Wakeup an application.
|
|||
|
* We need this in Windows when we send DAT_NULL to an application, otherwise
|
|||
|
* it'll sit there like a lump on a bog until an event comes along to wake it
|
|||
|
* up so it can process the message.
|
|||
|
*/
|
|||
|
void CTwnDsmApps::AppWakeup(TW_IDENTITY *_pAppId)
|
|||
|
{
|
|||
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|||
|
BOOL boolResult;
|
|||
|
if ( AppValidateId(_pAppId)
|
|||
|
&& m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].hwnd)
|
|||
|
{
|
|||
|
// Force the parent to process a message. WM_NULL is
|
|||
|
// safe, because it's a no-op...
|
|||
|
boolResult = ::PostMessage(m_ptwndsmappsimpl->m_AppInfo[(TWID_T)_pAppId->Id].hwnd,WM_NULL,(WPARAM)0,(LPARAM)0);
|
|||
|
if (!boolResult)
|
|||
|
{
|
|||
|
kLOG((kLOGERR,"PostMessage failed..."));
|
|||
|
}
|
|||
|
}
|
|||
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|||
|
kLOG((kLOGERR,"We shouldn't be here in AppWakeup..."));
|
|||
|
// We don't support this path on this platform, use
|
|||
|
// callbacks instead...
|
|||
|
// Make the compiler happy...
|
|||
|
void *unused = _pAppId;
|
|||
|
unused = 0;
|
|||
|
(void)unused;
|
|||
|
#else
|
|||
|
#error Sorry, we do not recognize this system...
|
|||
|
#endif
|
|||
|
}
|