/***************************************************************************
 * TWAIN Data Source Manager version 2.1
 * Manages image acquisition data sources used by a machine. 
 * Copyright © 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 dsm.cpp
* Data Source Manager.
* This software manages the interactions between the application 
* and the Source. 
* @author TWAIN Working Group
* @date March 2007
*/

/*! \mainpage Data Source Manager
 *
 * The Source Manager provides the communication path between the 
 * Application and the Source, supports the user’s selection of a  
 * Source, and loads the Source for access by the Application.   
 * Communications from Application to Source Manager or the Source
 * to Source Manager (via DAT_NULL) arrive in exclusively through
 * the DSM_Entry() entry point.
 *
 *
 *
 *
 *
 *
 *
 *
 * Copyright © 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.
 */

#include "dsm.h"



/*
* These are all the globals we should ever have in this project...
*/
HINSTANCE   g_hinstance     = 0; /**< Windows Instance handle for the DSM DLL... */
CTwnDsm    *g_ptwndsm       = 0; /**< The main DSM object */
CTwnDsmLog *g_ptwndsmlog    = 0; /**< The logging object, only access through macros */



/**
* Localization:  we have the selection box on Windows that we have
* to deal with, so this table gives us all our strings in UTF-8 format.
* TWAIN defines a lot of languages, and it makes sense to reference
* all of them.  Refer to the DSM_translations.txt file if you want to
* see the mapping between the hex UTF-8 and the viewable characters.
* The UTF-8 values are in hex so that the encoding of this file doesn't
* change the resulting output.
*
* Many languages still need translations...
*/
/*
* we need this because xcode insists on complaining about content
* inside of visual studio's code block
*/
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wall"
#endif
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
typedef struct
{
  TW_INT16  Language;     /**< Language */
  BYTE      CharSet;      /**< Character Set*/
  LANGID    LangId;       /**< Language Id*/
  char      *Title;       /**< the Title string*/
  char      *Sources;     /**< the Sources string */
  char      *Select;      /**< the Select string */
  char      *Cancel;      /**< the Cancel string */
} TwLocalize;

/**
* Localized strings for the select dialog
*/
static TwLocalize s_twlocalize[] =
{
      {TWLG_AFRIKAANS,          ANSI_CHARSET,       MAKELANGID(LANG_AFRIKAANS,SUBLANG_NEUTRAL),                 "Select Source","Sources:","Select","\x4b\x61\x6e\x73\x65\x6c\x6c\x65\x65\x72"},
      {TWLG_ALBANIA,            EASTEUROPE_CHARSET, MAKELANGID(LANG_ALBANIAN,SUBLANG_NEUTRAL),                  "","","",""},
      {TWLG_ARABIC,             ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_NEUTRAL),                    "","","",""},
      {TWLG_ARABIC_ALGERIA,     ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_ALGERIA),             "","","",""},
      {TWLG_ARABIC_BAHRAIN,     ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_BAHRAIN),             "","","",""},
      {TWLG_ARABIC_EGYPT,       ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_EGYPT),               "","","",""},
      {TWLG_ARABIC_IRAQ,        ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_IRAQ),                "","","",""},
      {TWLG_ARABIC_JORDAN,      ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_JORDAN),              "","","",""},
      {TWLG_ARABIC_KUWAIT,      ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_KUWAIT),              "","","",""},
      {TWLG_ARABIC_LEBANON,     ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_LEBANON),             "","","",""},
      {TWLG_ARABIC_LIBYA,       ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_LIBYA),               "","","",""},
      {TWLG_ARABIC_MOROCCO,     ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_MOROCCO),             "","","",""},
      {TWLG_ARABIC_OMAN,        ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_OMAN),                "","","",""},
      {TWLG_ARABIC_QATAR,       ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_QATAR),               "","","",""},
      {TWLG_ARABIC_SAUDIARABIA, ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_SAUDI_ARABIA),        "","","",""},
      {TWLG_ARABIC_SYRIA,       ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_SYRIA),               "","","",""},
      {TWLG_ARABIC_TUNISIA,     ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_TUNISIA),             "","","",""},
      {TWLG_ARABIC_UAE,         ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_UAE),                 "","","",""},
      {TWLG_ARABIC_YEMEN,       ARABIC_CHARSET,     MAKELANGID(LANG_ARABIC,SUBLANG_ARABIC_YEMEN),               "","","",""},
      {TWLG_ASSAMESE,           ANSI_CHARSET,       MAKELANGID(LANG_ASSAMESE,SUBLANG_NEUTRAL),                  "","","",""},
      {TWLG_BASQUE,             ANSI_CHARSET,       MAKELANGID(LANG_BASQUE,SUBLANG_NEUTRAL),                    "Select Source","Sources:","Select","\x55\x74\x7a\x69"},
      {TWLG_BENGALI,            ANSI_CHARSET,       MAKELANGID(LANG_BENGALI,SUBLANG_NEUTRAL),                   "","","",""},
      {TWLG_BIHARI,             0,                  0,                                                          "","","",""},
      {TWLG_BODO,               0,                  0,                                                          "","","",""},
      {TWLG_BULGARIAN,          RUSSIAN_CHARSET,    MAKELANGID(LANG_BULGARIAN,SUBLANG_NEUTRAL),                 "","","",""},
      {TWLG_BYELORUSSIAN,       RUSSIAN_CHARSET,    MAKELANGID(LANG_BELARUSIAN,SUBLANG_NEUTRAL),                "","","",""},
      {TWLG_CATALAN,            ANSI_CHARSET,       MAKELANGID(LANG_CATALAN,SUBLANG_NEUTRAL),                   "Select Source","Sources:","Select","\x43\x61\x6e\x63\x65\x6c\xc2\xb7\x6c\x61"},
      {TWLG_CHINESE,            GB2312_CHARSET,     MAKELANGID(LANG_CHINESE,SUBLANG_NEUTRAL),                   "\xe9\x80\x89\xe6\x8b\xa9\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90","\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90\x3a","\xe9\x80\x89\xe6\x8b\xa9","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_HONGKONG,   CHINESEBIG5_CHARSET,MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_HONGKONG),          "\xe9\x81\xb8\xe6\x93\x87\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90","\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90\x3a","\xe7\xa2\xba\xe5\xae\x9a","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_PRC,        GB2312_CHARSET,     MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),        "\xe9\x80\x89\xe6\x8b\xa9\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90","\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90\x3a","\xe9\x80\x89\xe6\x8b\xa9","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_SIMPLIFIED, GB2312_CHARSET,     MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),        "\xe9\x80\x89\xe6\x8b\xa9\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90","\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90\x3a","\xe9\x80\x89\xe6\x8b\xa9","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_SINGAPORE,  GB2312_CHARSET,     MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),        "\xe9\x80\x89\xe6\x8b\xa9\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90","\xe6\x95\xb0\xe6\x8d\xae\xe6\xba\x90\x3a","\xe9\x80\x89\xe6\x8b\xa9","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_TAIWAN,     CHINESEBIG5_CHARSET,MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_TRADITIONAL),       "\xe9\x81\xb8\xe6\x93\x87\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90","\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90\x3a","\xe7\xa2\xba\xe5\xae\x9a","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CHINESE_TRADITIONAL,CHINESEBIG5_CHARSET,MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_TRADITIONAL),       "\xe9\x81\xb8\xe6\x93\x87\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90","\xe5\xbd\xb1\xe5\x83\x8f\xe4\xbe\x86\xe6\xba\x90\x3a","\xe7\xa2\xba\xe5\xae\x9a","\xe5\x8f\x96\xe6\xb6\x88"},
      {TWLG_CROATIA,            EASTEUROPE_CHARSET, MAKELANGID(LANG_CROATIAN,SUBLANG_NEUTRAL),                  "Select Source","Sources:","Select","\x4f\x64\x75\x73\x74\x61\x6e\x69"},
      {TWLG_CZECH,              EASTEUROPE_CHARSET, MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT),                     "Select Source","Sources:","Select","\x53\x74\x6f\x72\x6e\x6f"},
      {TWLG_DANISH,             ANSI_CHARSET,       MAKELANGID(LANG_DANISH,SUBLANG_NEUTRAL),                    "\x56\xC3\xA6\x6C\x67\x20\x45\x6E\x68\x65\x64","\x45\x6E\x68\x65\x64","\x56\xE6\x6C\x67","\x41\x6E\x6E\x75\x6C\x6C\x65\x72"},
      {TWLG_DOGRI,              0,                  0,                                                          "","","",""},
      {TWLG_DUTCH,              ANSI_CHARSET,       MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH),                       "\x53\x65\x6c\x65\x63\x74\x65\x65\x72\x20\x62\x72\x6f\x6e","\x42\x72\x6f\x6e\x6e\x65\x6e\x3a","\x53\x65\x6c\x65\x63\x74\x65\x72\x65\x6e","\x41\x6e\x6e\x75\x6c\x65\x72\x65\x6e"},
      {TWLG_DUTCH_BELGIAN,      ANSI_CHARSET,       MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN),               "\x53\x65\x6c\x65\x63\x74\x65\x65\x72\x20\x62\x72\x6f\x6e","\x42\x72\x6f\x6e\x6e\x65\x6e\x3a","\x53\x65\x6c\x65\x63\x74\x65\x72\x65\x6e","\x41\x6e\x6e\x75\x6c\x65\x72\x65\x6e"},
      {TWLG_ENGLISH,            ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_NEUTRAL),                   "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_AUSTRALIAN, ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_AUS),               "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_CANADIAN,   ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_CAN),               "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_IRELAND,    ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_EIRE),              "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_NEWZEALAND, ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_NZ),                "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_SOUTHAFRICA,ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_SOUTH_AFRICA),      "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_UK,         ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_UK),                "Select Source","Sources:","Select","Cancel"},
      {TWLG_ENGLISH_USA,        ANSI_CHARSET,       MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),                "Select Source","Sources:","Select","Cancel"},
      {TWLG_ESTONIAN,           BALTIC_CHARSET,     MAKELANGID(LANG_ESTONIAN,SUBLANG_NEUTRAL),                  "Select Source","Sources:","Select","\x4b\x75\x73\x74\x75\x74\x61"},
      {TWLG_FAEROESE,           EASTEUROPE_CHARSET, MAKELANGID(LANG_FAEROESE,SUBLANG_NEUTRAL),                  "","","",""},
      {TWLG_FARSI,              ARABIC_CHARSET,     MAKELANGID(LANG_FARSI,SUBLANG_NEUTRAL),                     "","","",""},
      {TWLG_FINNISH,            ANSI_CHARSET,       MAKELANGID(LANG_FINNISH,SUBLANG_NEUTRAL),                   "Select Source","Sources:","Select","\x50\x65\x72\x75\x75\x74\x61"},
      {TWLG_FRENCH,             ANSI_CHARSET,       MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH),                     "\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72\x20\x73\x6f\x75\x72\x63\x65","\x53\x6f\x75\x72\x63\x65\x73\x3a","\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72","\x41\x6e\x6e\x75\x6c\x65\x72"},
      {TWLG_FRENCH_BELGIAN,     ANSI_CHARSET,       MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH_BELGIAN),             "\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72\x20\x73\x6f\x75\x72\x63\x65","\x53\x6f\x75\x72\x63\x65\x73\x3a","\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72","\x41\x6e\x6e\x75\x6c\x65\x72"},
      {TWLG_FRENCH_CANADIAN,    ANSI_CHARSET,       MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH_CANADIAN),            "\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72\x20\x73\x6f\x75\x72\x63\x65","\x53\x6f\x75\x72\x63\x65\x73\x3a","\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72","\x41\x6e\x6e\x75\x6c\x65\x72"},
      {TWLG_FRENCH_LUXEMBOURG,  ANSI_CHARSET,       MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH_LUXEMBOURG),          "\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72\x20\x73\x6f\x75\x72\x63\x65","\x53\x6f\x75\x72\x63\x65\x73\x3a","\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72","\x41\x6e\x6e\x75\x6c\x65\x72"},
      {TWLG_FRENCH_SWISS,       ANSI_CHARSET,       MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH_SWISS),               "\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72\x20\x73\x6f\x75\x72\x63\x65","\x53\x6f\x75\x72\x63\x65\x73\x3a","\x53\xc3\xa9\x6c\x65\x63\x74\x69\x6f\x6e\x6e\x65\x72","\x41\x6e\x6e\x75\x6c\x65\x72"},
      {TWLG_GERMAN,             ANSI_CHARSET,       MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN),                     "\x51\x75\x65\x6c\x6c\x65\x20\x77\xc3\xa4\x68\x6c\x65\x6e","\x51\x75\x65\x6c\x6c\x65\x6e\x3a","\x41\x75\x73\x77\xc3\xa4\x68\x6c\x65\x6e","\x41\x62\x62\x72\x65\x63\x68\x65\x6e"},
      {TWLG_GERMAN_AUSTRIAN,    ANSI_CHARSET,       MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN_AUSTRIAN),            "\x51\x75\x65\x6c\x6c\x65\x20\x77\xc3\xa4\x68\x6c\x65\x6e","\x51\x75\x65\x6c\x6c\x65\x6e\x3a","\x41\x75\x73\x77\xc3\xa4\x68\x6c\x65\x6e","\x41\x62\x62\x72\x65\x63\x68\x65\x6e"},
      {TWLG_GERMAN_LIECHTENSTEIN,ANSI_CHARSET,      MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN_LIECHTENSTEIN),       "\x51\x75\x65\x6c\x6c\x65\x20\x77\xc3\xa4\x68\x6c\x65\x6e","\x51\x75\x65\x6c\x6c\x65\x6e\x3a","\x41\x75\x73\x77\xc3\xa4\x68\x6c\x65\x6e","\x41\x62\x62\x72\x65\x63\x68\x65\x6e"},
      {TWLG_GERMAN_LUXEMBOURG,  ANSI_CHARSET,       MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN_LUXEMBOURG),          "\x51\x75\x65\x6c\x6c\x65\x20\x77\xc3\xa4\x68\x6c\x65\x6e","\x51\x75\x65\x6c\x6c\x65\x6e\x3a","\x41\x75\x73\x77\xc3\xa4\x68\x6c\x65\x6e","\x41\x62\x62\x72\x65\x63\x68\x65\x6e"},
      {TWLG_GERMAN_SWISS,       ANSI_CHARSET,       MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN_SWISS),               "\x51\x75\x65\x6c\x6c\x65\x20\x77\xc3\xa4\x68\x6c\x65\x6e","\x51\x75\x65\x6c\x6c\x65\x6e\x3a","\x41\x75\x73\x77\xc3\xa4\x68\x6c\x65\x6e","\x41\x62\x62\x72\x65\x63\x68\x65\x6e"},
      {TWLG_GREEK,              GREEK_CHARSET,      MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT),                     "","","",""},
      {TWLG_GUJARATI,           GREEK_CHARSET,      MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT),                  "","","",""},
      {TWLG_HARYANVI,           0,                  0,                                                          "","","",""},
      {TWLG_HEBREW,             HEBREW_CHARSET,     MAKELANGID(LANG_HEBREW,SUBLANG_NEUTRAL),                    "","","",""},
      {TWLG_HINDI,              HEBREW_CHARSET,     MAKELANGID(LANG_HINDI,SUBLANG_NEUTRAL),                     "","","",""},
      {TWLG_HUNGARIAN,          EASTEUROPE_CHARSET, MAKELANGID(LANG_HUNGARIAN,SUBLANG_NEUTRAL),                 "Select Source","Sources:","Select","\x4d\xc3\xa9\x67\x73\x65"},
      {TWLG_ICELANDIC,          ANSI_CHARSET,       MAKELANGID(LANG_ICELANDIC,SUBLANG_NEUTRAL),                 "","","",""},
      {TWLG_INDONESIAN,         ANSI_CHARSET,       MAKELANGID(LANG_INDONESIAN,SUBLANG_NEUTRAL),                "\x50\x69\x6c\x69\x74\x68\x20\x53\x75\x6d\x62\x65\x72","\x53\x75\x6d\x62\x65\x72\x3a","\x50\x69\x6c\x69\x74\x68","\x42\x61\x74\x61\x6c"},
      {TWLG_ITALIAN,            ANSI_CHARSET,       MAKELANGID(LANG_ITALIAN,SUBLANG_ITALIAN),                   "\x53\x65\x6c\x65\x7a\x69\x6f\x6e\x61\x20\x6f\x72\x69\x67\x69\x6e\x65","\x4f\x72\x69\x67\x69\x6e\x69\x3a","\x53\x65\x6c\x65\x7a\x69\x6f\x6e\x61","\x41\x6e\x6e\x75\x6c\x6c\x61"},
      {TWLG_ITALIAN_SWISS,      ANSI_CHARSET,       MAKELANGID(LANG_ITALIAN,SUBLANG_ITALIAN_SWISS),             "\x53\x65\x6c\x65\x7a\x69\x6f\x6e\x61\x20\x6f\x72\x69\x67\x69\x6e\x65","\x4f\x72\x69\x67\x69\x6e\x69\x3a","\x53\x65\x6c\x65\x7a\x69\x6f\x6e\x61","\x41\x6e\x6e\x75\x6c\x6c\x61"},
      {TWLG_JAPANESE,           SHIFTJIS_CHARSET,   MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT),                  "\xe5\x8e\x9f\xe7\xa8\xbf\xe3\x81\xae\xe9\x81\xb8\xe6\x8a\x9e","\xe5\x8e\x9f\xe7\xa8\xbf\x3a","\xe9\x81\xb8\xe6\x8a\x9e","\xe3\x82\xad\xe3\x83\xa3\xe3\x83\xb3\xe3\x82\xbb\xe3\x83\xab"},
      {TWLG_KANNADA,            ANSI_CHARSET,       MAKELANGID(LANG_KANNADA,SUBLANG_NEUTRAL),                   "","","",""},
      {TWLG_KASHMIRI,           ANSI_CHARSET,       MAKELANGID(LANG_KASHMIRI,SUBLANG_NEUTRAL),                  "","","",""},
      {TWLG_KOREAN,             HANGUL_CHARSET,     MAKELANGID(LANG_KOREAN,SUBLANG_KOREAN),                     "\xec\x9e\xa5\xec\xb9\x98\x20\xec\x84\xa0\xed\x83\x9d","\xec\x9e\xa5\xec\xb9\x98","\xec\x84\xa0\xed\x83\x9d","\xec\xb7\xa8\xec\x86\x8c"},
      {TWLG_KOREAN_JOHAB,       JOHAB_CHARSET,      MAKELANGID(LANG_KOREAN,SUBLANG_KOREAN),                     "\xec\x9e\xa5\xec\xb9\x98\x20\xec\x84\xa0\xed\x83\x9d","\xec\x9e\xa5\xec\xb9\x98","\xec\x84\xa0\xed\x83\x9d","\xec\xb7\xa8\xec\x86\x8c"},
      {TWLG_LATVIAN,            BALTIC_CHARSET,     MAKELANGID(LANG_LATVIAN,SUBLANG_NEUTRAL),                   "","","",""},
      {TWLG_LITHUANIAN,         BALTIC_CHARSET,     MAKELANGID(LANG_LITHUANIAN,SUBLANG_NEUTRAL),                "","","",""},
      {TWLG_MALAYALAM,          BALTIC_CHARSET,     MAKELANGID(LANG_MALAYALAM,SUBLANG_NEUTRAL),                 "","","",""},
      {TWLG_MARATHI,            ANSI_CHARSET,       MAKELANGID(LANG_MARATHI,SUBLANG_NEUTRAL),                   "","","",""},
      {TWLG_MARWARI,            0,                  0,                                                          "","","",""},
      {TWLG_MEGHALAYAN,         0,                  0,                                                          "","","",""},
      {TWLG_MIZO,               0,                  0,                                                          "","","",""},
      {TWLG_NAGA,               0,                  0,                                                          "","","",""},
      {TWLG_NORWEGIAN,          ANSI_CHARSET,       MAKELANGID(LANG_NORWEGIAN,SUBLANG_NEUTRAL),                 "","","",""},
      {TWLG_NORWEGIAN_BOKMAL,   ANSI_CHARSET,       MAKELANGID(LANG_NORWEGIAN,SUBLANG_NORWEGIAN_BOKMAL),        "","","",""},
      {TWLG_NORWEGIAN_NYNORSK,  ANSI_CHARSET,       MAKELANGID(LANG_NORWEGIAN,SUBLANG_NORWEGIAN_NYNORSK),       "","","",""},
      {TWLG_ORISSI,             0,                  0,                                                          "","","",""},
      {TWLG_POLISH,             EASTEUROPE_CHARSET, MAKELANGID(LANG_POLISH,SUBLANG_NEUTRAL),                    "Select Source","Sources:","Select","\x41\x6e\x75\x6c\x75\x6a"},
      {TWLG_PORTUGUESE,         EASTEUROPE_CHARSET, MAKELANGID(LANG_PORTUGUESE,SUBLANG_PORTUGUESE),             "\x53\x65\x6c\x65\x63\x69\x6f\x6e\x61\x72\x20\x4f\x72\x69\x67\x65\x6d","\x4f\x72\x69\x67\x65\x6e\x73\x3a","\x53\x65\x6c\x65\x63\x69\x6f\x6e\x61\x72","\x43\x61\x6e\x63\x65\x6c\x61\x72"},
      {TWLG_PORTUGUESE_BRAZIL,  ANSI_CHARSET,       MAKELANGID(LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN),   "\x53\x65\x6c\x65\x63\x69\x6f\x6e\x61\x72\x20\x4f\x72\x69\x67\x65\x6d","\x4f\x72\x69\x67\x65\x6e\x73\x3a","\x53\x65\x6c\x65\x63\x69\x6f\x6e\x61\x72","\x43\x61\x6e\x63\x65\x6c\x61\x72"},
      {TWLG_PUNJABI,            ANSI_CHARSET,       MAKELANGID(LANG_PUNJABI,SUBLANG_NEUTRAL),                   "","","",""},
      {TWLG_PUSHTU,             0,                  0,                                                          "","","",""},
      {TWLG_ROMANIAN,           EASTEUROPE_CHARSET, MAKELANGID(LANG_ROMANIAN,SUBLANG_NEUTRAL),                  "","","",""},
      {TWLG_RUSSIAN,            RUSSIAN_CHARSET,    MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT),                   "\xd0\x92\xd1\x8b\xd0\xb1\xd1\x80\xd0\xb0\xd1\x82\xd1\x8c\x20\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe\xd1\x87\xd0\xbd\xd0\xb8\xd0\xba","\xd0\x98\xd1\x81\xd1\x82\xd0\xbe\xd1\x87\xd0\xbd\xd0\xb8\xd0\xba\xd0\xb8\x3a","\xd0\x92\xd1\x8b\xd0\xb1\xd1\x80\xd0\xb0\xd1\x82\xd1\x8c","\xd0\x9e\xd1\x82\xd0\xbc\xd0\xb5\xd0\xbd\xd0\xb8\xd1\x82\xd1\x8c"},
      {TWLG_SERBIAN_CYRILLIC,   ANSI_CHARSET,       MAKELANGID(LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC),          "","","",""},
      {TWLG_SERBIAN_LATIN,      EASTEUROPE_CHARSET, MAKELANGID(LANG_SERBIAN,SUBLANG_SERBIAN_LATIN),             "","","",""},
      {TWLG_SIKKIMI,            0,                  0,                                                          "","","",""},
      {TWLG_SLOVAK,             EASTEUROPE_CHARSET, MAKELANGID(LANG_SLOVAK,SUBLANG_NEUTRAL),                    "","","",""},
      {TWLG_SLOVENIAN,          EASTEUROPE_CHARSET, MAKELANGID(LANG_SLOVENIAN,SUBLANG_NEUTRAL),                 "Select Source","Sources:","Select","\x50\x72\x65\x6b\x69\x6e\x69"},
      {TWLG_SPANISH,            ANSI_CHARSET,       MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH),                   "\x53\x65\x6c\x65\x63\x63\x69\xc3\xb3\x6e\x20\x64\x65\x20\x66\x75\x65\x6e\x74\x65","\x46\x75\x65\x6e\x74\x65\x73\x3a","\x53\x65\x6c\x65\x63\x63\x69\x6f\x6e\x61\x72","\x43\x61\x6e\x63\x65\x6c\x61\x72"},
      {TWLG_SPANISH_MEXICAN,    ANSI_CHARSET,       MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH_MEXICAN),           "\x53\x65\x6c\x65\x63\x63\x69\xc3\xb3\x6e\x20\x64\x65\x20\x66\x75\x65\x6e\x74\x65","\x46\x75\x65\x6e\x74\x65\x73\x3a","\x53\x65\x6c\x65\x63\x63\x69\x6f\x6e\x61\x72","\x43\x61\x6e\x63\x65\x6c\x61\x72"},
      {TWLG_SPANISH_MODERN,     ANSI_CHARSET,       MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH_MODERN),            "\x53\x65\x6c\x65\x63\x63\x69\xc3\xb3\x6e\x20\x64\x65\x20\x66\x75\x65\x6e\x74\x65","\x46\x75\x65\x6e\x74\x65\x73\x3a","\x53\x65\x6c\x65\x63\x63\x69\x6f\x6e\x61\x72","\x43\x61\x6e\x63\x65\x6c\x61\x72"},
      {TWLG_SWEDISH,            ANSI_CHARSET,       MAKELANGID(LANG_SWEDISH,SUBLANG_SWEDISH),                   "Select Source","Sources:","Select","\x41\x76\x62\x72\x79\x74"},
      {TWLG_SWEDISH_FINLAND,    ANSI_CHARSET,       MAKELANGID(LANG_SWEDISH,SUBLANG_SWEDISH_FINLAND),           "Select Source","Sources:","Select","\x41\x76\x62\x72\x79\x74"},
      {TWLG_TAMIL,              ANSI_CHARSET,       MAKELANGID(LANG_TAMIL,SUBLANG_NEUTRAL),                     "","","",""},
      {TWLG_TELUGU,             ANSI_CHARSET,       MAKELANGID(LANG_TELUGU,SUBLANG_NEUTRAL),                    "","","",""},
      {TWLG_THAI,               THAI_CHARSET,       MAKELANGID(LANG_THAI,SUBLANG_NEUTRAL),                      "","","",""},
      {TWLG_TRIPURI,            0,                  0,                                                          "","","",""},
      {TWLG_TURKISH,            TURKISH_CHARSET,    MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT),                   "\x4b\x61\x79\x6e\x61\x6b\x20\x73\x65\xc3\xa7\x69\x6e\x69\x7a","\x4b\x61\x79\x6e\x61\x6b","\x53\x65\xc3\xa7\x69\x6e\x69\x7a","\xc4\xb0\x70\x74\x61\x6c"},
      {TWLG_UKRANIAN,           RUSSIAN_CHARSET,    MAKELANGID(LANG_UKRAINIAN,SUBLANG_NEUTRAL),                 "","","",""},
      {TWLG_URDU,               ANSI_CHARSET,       MAKELANGID(LANG_URDU,SUBLANG_NEUTRAL),                      "","","",""},
      {TWLG_VIETNAMESE,         VIETNAMESE_CHARSET, MAKELANGID(LANG_VIETNAMESE,SUBLANG_NEUTRAL),                "","","",""},
      {-1, 0, 0, 0, 0, 0, 0} // must be last...
};
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    // We don't have anything for here...
#else
    #error Sorry, we do not recognize this system...
#endif
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
#pragma clang diagnostic pop
#endif

#if TWNDSM_OS_64BIT
  #define TWNDSM_DS_REG_LOC "Software\\Microsoft\\Windows NT\\CurrentVersion\\TWAIN64"
#else
  #define TWNDSM_DS_REG_LOC "Software\\Microsoft\\Windows NT\\CurrentVersion\\TWAIN"
#endif


/**
* @defgroup MemFunctions declarations for our memory management functions...
* @{
*/

#if 0
/**
* Memory Allocate
* @param[in] _size the size of the memory to allocate
* @return the handle to the memory allocated
*/
static TW_HANDLE PASCAL DSM_MemAllocate(TW_UINT32 _size);

/**
* Memory Free
* @param[in] _handle the handle to the memory to free
*/
static void PASCAL DSM_MemFree(TW_HANDLE _handle);

/**
* Memory Lock
* @param[in] _handle the handle to the memory to lock
* @return locked pointer to the memory in the handle
*/
static TW_MEMREF PASCAL DSM_MemLock(TW_HANDLE _handle);

/**
* Memory Unlock
* @param[in] _handle the handle to the memory to unlock
*/
static void PASCAL DSM_MemUnlock(TW_HANDLE _handle);
// @}
#endif

/**
* Data Source Manager Entry Point.
*
* The only entry point into the Data Source Manager.  This is the only
* function not a part of CTwnDsm.  It's responsible for managing the
* class.  We keep things organized this way to make sure that we don't
* allocate any resources until MSG_OPENDSM, and that we release all
* resources as a part of MSG_CLOSEDSM.  If this is done correctly then
* we won't mind if the library isn't freed (which has been seen to
* happen sometimes with COM)...
*
* Defined in twain.h
*
* @param[in] _pOrigin Identifies the source module of the message. This could
*            identify an Application, a Source, or the Source Manager.
*
* @param[in] _pDest Identifies the destination module for the message.
*            This could identify an application or a data source.
*            If this is NULL, the message goes to the Source Manager.
*
* @param[in] _DG The Data Group. 
*            Example: DG_IMAGE.
*
* @param[in] _DAT The Data Attribute Type.
*            Example: DAT_IMAGEMEMXFER.
*    
* @param[in] _MSG The message.  Messages are interpreted by the destination module
*            with respect to the Data Group and the Data Attribute Type.  
*            Example: MSG_GET.
*
* @param[in,out] _pData A pointer to the data structure or variable identified 
*            by the Data Attribute Type.
*            Example: (TW_MEMREF)&ImageMemXfer
*                   where ImageMemXfer is a TW_IMAGEMEMXFER structure.
*                    
* @return a valid TWRC_xxxx return code.
*            Example: TWRC_SUCCESS.
*/
DSMENTRY DSM_Entry(TW_IDENTITY  *_pOrigin,
                   TW_IDENTITY  *_pDest,
                   TW_UINT32     _DG,
                   TW_UINT16     _DAT,
                   TW_UINT16     _MSG,
                   TW_MEMREF     _pData)
{
  TW_UINT16 rcDSM;

  // Validate...
  if (0 == _pOrigin)
  {
    return TWRC_FAILURE;
  }

  // If we're processing DG_CONTROL/DAT_PARENT/MSG_OPENDSM then see
  // if we need to create our CTwnDsm object.  We don't want to
  // allocate any resources prior to new CTwnDsm!!!
  if (   (_MSG == MSG_OPENDSM)
      && (_DAT == DAT_PARENT)
      && (_DG  == DG_CONTROL)
      && (0 == g_ptwndsm))
  {
      g_ptwndsm = new CTwnDsm;
      if (0 == g_ptwndsm)
      {
          kPANIC("Failed to new CTwnDsm!!!");
          return TWRC_FAILURE;
      }
  }

  // If we have no CTwnDsm object, then we're in trouble, but
  // try to handle DAT_STATUS in case it gets called before
  // MSG_OPENDSM or after MSG_CLOSEDSM...
  if (0 == g_ptwndsm)
  {
      if (   ( (_MSG == MSG_GET) || (_MSG == MSG_CHECKSTATUS) )
          && (_DAT == DAT_STATUS)
          && (_DG  == DG_CONTROL)
          && (0 != _pData)
          )
      {
        ((TW_STATUS*)_pData)->ConditionCode = TWCC_BUMMER;
        return (TWRC_SUCCESS);
      }
      else
      {
        //kLOG((kLOGERR,"DAT_STATUS called before MSG_OPENDSM or after MSG_CLOSEDSM..."));
        return (TWRC_FAILURE);
      }
  }

  // Transfer control over to our dsm object, otherwise we'll
  // be doing g_ptwndsm all over the place...
  rcDSM = g_ptwndsm->DSM_Entry(_pOrigin,_pDest,_DG,_DAT,_MSG,_pData);

  // If we successfully processed DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM,
  // and don't have any other applications with it open,
  // then destroy our object.  We don't want to have any resources
  // lingering around after we destroy our CTwnDsm object!!!
  if (   (TWRC_SUCCESS == rcDSM)
      && (_MSG == MSG_CLOSEDSM)
      && (_DAT == DAT_PARENT)
      && (_DG  == DG_CONTROL)
      && g_ptwndsm->DSMGetState() != dsmState_Open )
  {
      delete g_ptwndsm;
      g_ptwndsm = 0;
  }

  // All done...
  return rcDSM;
}



/*
* Our constructor...
* Clean out the pod and set stuff.  Get logging set up so we
* can have a clue what's going on...
*/
CTwnDsm::CTwnDsm()
{
  // Zero out the pod...
  memset(&pod,0,sizeof(pod));

  // Get our logging object...
  g_ptwndsmlog = new CTwnDsmLog;
  if (!g_ptwndsmlog)
  {
      kPANIC("Failed to new CTwnDsmLog!!!");
  }

  // If logging is on, then this is a good chance to dump information
  // about ourselves...
  kLOG((kLOGINFO,"************************************************"));
  kLOG((kLOGINFO,"%s",TWNDSM_ORGANIZATION));
  kLOG((kLOGINFO,"%s",TWNDSM_DESCRIPTION));
  kLOG((kLOGINFO,"version: %s",TWNDSM_VERSION_STR));

  // Get our application object...
  pod.m_ptwndsmapps = new CTwnDsmApps();
  if (!pod.m_ptwndsmapps)
  {
      kPANIC("Failed to new CTwnDsmApps!!!");
  }
}



/*
* Our destructor...
* Free any resources we might have...
*/
CTwnDsm::~CTwnDsm()
{
  if (pod.m_ptwndsmapps)
  {
    delete pod.m_ptwndsmapps;
  }
  if (g_ptwndsmlog)
  {
    delete g_ptwndsmlog;
  }
  memset(&pod,0,sizeof(pod));
}



/*
* This is where we finish up the DSM_Entry duties inside of the
* context of the class...
*/
TW_UINT16 CTwnDsm::DSM_Entry(TW_IDENTITY  *_pOrigin,
                             TW_IDENTITY  *_pDest,
                             TW_UINT32     _DG,
                             TW_UINT16     _DAT,
                             TW_UINT16     _MSG,
                             TW_MEMREF     _pData)
{
  TW_UINT16     rcDSM   = TWRC_SUCCESS;
  bool          bPrinted;
  TW_CALLBACK2 *ptwcallback2;
  TW_IDENTITY  *pAppId  = _pOrigin;
  TW_IDENTITY  *pDSId   = _pDest;

  // Do a test to see if pOrigin is a DS instead of App, if so then switch pAppId and pDSId
  // MSG_INVOKE_CALLBACK was only used on the Mac and is now deprecated (ver 2.1)
  // it is here for backwords capabiltiy
  if ( (_DAT == DAT_NULL /*&& _DG == DG_CONTROL */)
    || (_DAT == DAT_CALLBACK && _MSG == MSG_INVOKE_CALLBACK /*&& _DG == DG_CONTROL */) )
  {
    pAppId  = _pDest;
    pDSId   = _pOrigin;
  }

  // Print the triplets to stdout for information purposes
  bPrinted = printTripletsInfo(_pOrigin,_pDest,_DG,_DAT,_MSG,_pData);

  // Sniff for the application forwarding an event to the
  // DS. It may be possible that the app has a message waiting for
  // it because it didn't register a callback.
  if (   (DAT_EVENT == _DAT)
      && (MSG_PROCESSEVENT == _MSG))
  {
    // Check that the AppID and DSID are valid...
    if (!pod.m_ptwndsmapps->AppValidateIds(pAppId,pDSId))
    {
      kLOG((kLOGINFO,"Bad TW_IDENTITY"));
      pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
      rcDSM = TWRC_FAILURE;
    }
    else if (pod.m_ptwndsmapps->DsCallbackIsWaiting(pAppId,(TWID_T)pDSId->Id))
    {
      ptwcallback2 = pod.m_ptwndsmapps->DsCallback2Get(pAppId,(TWID_T)pDSId->Id);
      ((TW_EVENT*)(_pData))->TWMessage = ptwcallback2->Message;
      if( g_ptwndsmlog )
      {
        char szMsg[64];
        StringFromMsg(szMsg,NCHARS(szMsg),ptwcallback2->Message);
        kLOG((kLOGINFO,"%.32s retrieving DAT_EVENT / %s\n", pAppId->ProductName, szMsg));
      }
      ptwcallback2->Message = 0;
      pod.m_ptwndsmapps->DsCallbackSetWaiting(pAppId,(TWID_T)pDSId->Id,FALSE);
      rcDSM = TWRC_DSEVENT;
    }
    // No callback, so fall on through...
  }

  // Is this msg for us?
  if( TWRC_SUCCESS == rcDSM )
  {
  switch (_DAT)
    {
      case DAT_IDENTITY:
        // If the pDSId is 0 then the message is intended for us.  We're
        // going to force the matter if _MSG is MSG_CLOSEDS, otherwise
        // we send the MSG_CLOSEDS to the driver, but never process it
        // ourselves, which seems like a terrible idea...
        if ((pDSId == 0) || (_MSG == MSG_CLOSEDS))
        {
          rcDSM = DSM_Identity(pAppId,_MSG,(TW_IDENTITY*)_pData);
          break;
        }
        // else we fall thru to send the message onto the DS

      default:
        // check if the application is open or not.  If it isn't, we have a bad sequence
        if (dsmState_Open == pod.m_ptwndsmapps->AppGetState(pAppId))
        {
            // Check that the AppID and DSID are valid...
            if (!pod.m_ptwndsmapps->AppValidateIds(pAppId,pDSId))
            {
              kLOG((kLOGINFO,"Bad TW_IDENTITY"));
              pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
              rcDSM = TWRC_FAILURE;
            }

            // Issue the command...
            else if (0 != pod.m_ptwndsmapps->DsGetEntryProc(pAppId,(TWID_T)pDSId->Id))
            {
              // Don't send a new message if the DS is still processing a previous message
              // or if the application has not returned back from recieving callback.
              // Place a Try | Catch around the function so we can maintain correct state 
              // in the case of an exception
			  //
			  // We are only enforcing this new behavior for TWAIN 2.2 applications and
			  // and higher.  Older apps can still use the 'wrong' behavior.  We need this
			  // to preserve backwards compability, and to give ourselves a chance to
			  // inform developers of the new requirement...
			  //
              if(((((pAppId->ProtocolMajor*10) + (pAppId->ProtocolMinor)) <= 201) || 
                     !pod.m_ptwndsmapps->DsIsProcessingMessage(pAppId,(TWID_T)pDSId->Id)     ) && 
                 ((((pAppId->ProtocolMajor*10) + (pAppId->ProtocolMinor)) <= 202) ||
                     !pod.m_ptwndsmapps->DsIsAppProcessingCallback(pAppId,(TWID_T)pDSId->Id) )    ) 
              {
                pod.m_ptwndsmapps->DsSetProcessingMessage(pAppId,(TWID_T)pDSId->Id,TRUE);
                try
                {
                  // Create a local copy of the AppIdentity
                  TW_IDENTITY AppId = *pod.m_ptwndsmapps->AppGetIdentity(pAppId);

                  rcDSM = (pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)pDSId->Id))(
                                          &AppId,
                                          _DG,
                                          _DAT,
                                          _MSG,
                                          _pData);
                }
                catch(...)
                {
                  rcDSM = TWRC_FAILURE;
                  pod.m_ptwndsmapps->AppSetConditionCode(pAppId,TWCC_BUMMER);
                  kLOG((kLOGERR,"Exception caught while DS was processing message.  Returning Failure."));
                }
                pod.m_ptwndsmapps->DsSetProcessingMessage(pAppId,(TWID_T)pDSId->Id,FALSE);
              }
              else
              {
                if( _DAT == DAT_EVENT && _MSG == MSG_PROCESSEVENT)
                {
                  kLOG((kLOGINFO,"Nested DAT_EVENT / MSG_PROCESSEVENT Ignored"));
                  rcDSM = TWRC_NOTDSEVENT;
                  ((TW_EVENT*)(_pData))->TWMessage = MSG_NULL;
                }
                else
                {
                  kLOG((kLOGERR,"Nested calls back to the DS.  Returning Failure."));
                  pod.m_ptwndsmapps->AppSetConditionCode(pAppId,TWCC_SEQERROR);
                  rcDSM = TWRC_FAILURE;
                }
              }
            }

            // For some reason we have no pointer to the dsentry function...
            else
            {
              kLOG((kLOGERR,"Unable to find driver, check your AppId and DsId values..."));
              pod.m_ptwndsmapps->AppSetConditionCode(pAppId,TWCC_OPERATIONERROR);
              kLOG((kLOGERR,"DS_Entry is null...%ld",(TWID_T)pAppId->Id));
              rcDSM = TWRC_FAILURE;
            }
        }
        else
        {
            kLOG((kLOGINFO,"DS is not open"));
            pod.m_ptwndsmapps->AppSetConditionCode(pAppId,TWCC_SEQERROR);
            rcDSM = TWRC_FAILURE;
        }
        break;

      case DAT_PARENT:
        rcDSM = DSM_Parent(pAppId,_MSG,_pData);
        break;

      case DAT_TWUNKIDENTITY:
        rcDSM = DSM_TwunkIdentity(pAppId,_MSG,(TW_TWUNKIDENTITY*)_pData);
        break;

      case DAT_ENTRYPOINT:
        rcDSM = DSM_Entrypoint(pAppId,_MSG,(TW_ENTRYPOINT*)_pData);
        break;

      case DAT_STATUS:
        if( _MSG == MSG_CHECKSTATUS )
        {
          _MSG = MSG_GET;
          kLOG((kLOGINFO, "MSG_CHECKSTATUS is Depreciated using MSG_GET"));
        }

        // If we get a DSId then it is intended to be passed along to the driver.
        // If the DSId is null then the request is handled by the DSM
        // If we're talking to a driver (state 4 or higher), then we
        // will pass the DAT_STATUS request down to it...
        if (  0 != pDSId
          &&  (dsmState_Open == pod.m_ptwndsmapps->AppGetState(pAppId))
          &&  pod.m_ptwndsmapps->AppValidateIds(pAppId,pDSId)
          &&  (0 != pod.m_ptwndsmapps->DsGetEntryProc(pAppId,(TWID_T)pDSId->Id)))
        {
          // Create a local copy of the AppIdentity
          TW_IDENTITY AppId = *pod.m_ptwndsmapps->AppGetIdentity(pAppId);

          rcDSM = (pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)pDSId->Id))(
                                  &AppId,
                                  _DG,
                                  _DAT,
                                  _MSG,
                                  _pData);
        }
        // Otherwise, handle it ourself...
        else
        {
          rcDSM = DSM_Status(pAppId,_MSG,(TW_STATUS*)_pData);
        }
        break;

      case DAT_CALLBACK:
        // DAT_CALLBACK can be either from an Application registering its Callback, 
        // or from a DS Invoking a request to send a message to the Application
        rcDSM = DSM_Callback(_pOrigin,_pDest,_MSG,(TW_CALLBACK*)_pData);
        break;

      case DAT_CALLBACK2:
        // DAT_CALLBACK2 can be either from an Application registering its Callback, 
        // or from a DS Invoking a request to send a message to the Application
        rcDSM = DSM_Callback2(_pOrigin,_pDest,_MSG,(TW_CALLBACK2*)_pData);
        break;

      case DAT_NULL:
        // Note how the origin and destination are switched for this
        // call (and only this call).  Because, of course, this
        // message is being send from the driver to the application...
        rcDSM = DSM_Null(pAppId,pDSId,_MSG);
        break;
    }
  }

  // Log how it went...
  if (bPrinted)
  {
    printResults(_DG,_DAT,_MSG,_pData,rcDSM);
  }

  return rcDSM;
}

/*
* Return the state of the DSM by checking the state of all applications
*/
DSM_State CTwnDsm::DSMGetState()
{
  DSM_State CurrentState = pod.m_ptwndsmapps->AppGetState();
  return CurrentState;
}

/*
* Handle DAT_STATUS.  Just a few things of note, we handle some
* DAT_STATUS stuff in DSM_Entry.  And per the spec we have to
* clear the condition code when we are done.  I've also put in
* MSG_CHECKSTATUS, because I can't imagine why we have the silly
* thing if it isn't for this function...
*/
TW_INT16 CTwnDsm::DSM_Status(TW_IDENTITY  *_pAppId,
                             TW_UINT16     _MSG,
                             TW_STATUS    *_pStatus)
{
  TW_INT16 result = TWRC_SUCCESS;

  switch (_MSG)
  {
    case MSG_GET:
    case MSG_CHECKSTATUS:
       _pStatus->ConditionCode = pod.m_ptwndsmapps->AppGetConditionCode(_pAppId);
       _pStatus->Reserved = 0;
      break;

    default:
      result = TWRC_FAILURE;
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
      break;
  }

  return result;
}



/*
* Handle DAT_PARENT.  This is where the DSM is expected to
* do most of its contribution, which is finding drivers for
* the application...
*/
TW_INT16 CTwnDsm::DSM_Parent(TW_IDENTITY  *_pAppId,
                             TW_UINT16     _MSG,
                             TW_MEMREF     _MemRef)
{
  TW_UINT16 result;

  // Validate...
  if (0 == _pAppId)
  {
      kLOG((kLOGERR,"_pAppId is null"));
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
      return TWRC_FAILURE;
  }

  // Init stuff...
  result = TWRC_SUCCESS;

  // Process the message...
  switch (_MSG)
  {
    case MSG_OPENDSM:
      // Try to add the proposed item...
      result = pod.m_ptwndsmapps->AddApp(_pAppId,_MemRef);
      break;

    case MSG_CLOSEDSM:
      // Try to remove the proposed item...
      result = pod.m_ptwndsmapps->RemoveApp(_pAppId);
      break;

    default:
      result = TWRC_FAILURE;
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
      break;
  }

  return result;
}



/*
* Handle DAT_IDENTITY.  This is where the DSM is expected to
* do most of its contribution, which is finding drivers for
* the application...
*/
TW_INT16 CTwnDsm::DSM_Identity(TW_IDENTITY  *_pAppId,
                               TW_UINT16     _MSG,
                               TW_IDENTITY  *_pDsId)
{
  TW_INT16  result;

  // Validate...
  if (0 == _pAppId || (TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp())
  {
      kLOG((kLOGERR,"_pAppId is null"));
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
      return TWRC_FAILURE;
  }

  // Init stuff...
  result = TWRC_SUCCESS;

  // Pick the message...
  if (dsmState_Open == pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    switch (_MSG)
    {
      case MSG_OPENDS:
        result = OpenDS(_pAppId,_pDsId);
        break;

      case MSG_CLOSEDS:
        result = CloseDS(_pAppId,_pDsId);
        break;

      case MSG_USERSELECT:
        result = DSM_SelectDS(_pAppId,_pDsId);
        break;

      case MSG_GETFIRST:
        result = DSM_GetFirst(_pAppId,_pDsId);
        break;

      case MSG_GETNEXT:
        result = DSM_GetNext(_pAppId,_pDsId);
        break;

      case MSG_GETDEFAULT:
        result = GetMatchingDefault(_pAppId,_pDsId);
        break;

      case MSG_SET:
        result = DSM_SetDefaultDS(_pAppId,_pDsId);
        break;

      default:
        result = TWRC_FAILURE;
        pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
        break;
    }
  }
  else
  {
    result = TWRC_FAILURE;
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
  }

  return result;
}

/*
* Handle DAT_TWUNKIDENTITY.  This is here for backwards compatibility. 
* DAT_TWUNKIDENTITY is undocumented.  It was used by the Twunking
* layer.  Some old applications use it to get the path to the DS.
* We need to continue to support it.
*/
TW_INT16 CTwnDsm::DSM_TwunkIdentity(TW_IDENTITY  *_pAppId,
                                    TW_UINT16     _MSG,
                                TW_TWUNKIDENTITY *_pTwunkId)
{
  TW_INT16  result = TWRC_SUCCESS;

  // Validate...
  if (0 == _pAppId || (TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp())
  {
    kLOG((kLOGERR,"_pAppId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (dsmState_Open != pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return TWRC_FAILURE;
  }
  else if (MSG_GET != _MSG)
  {
    kLOG((kLOGERR,"protocol error"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
    return TWRC_FAILURE;
  }
  else if (0 == _pTwunkId)
  {
    kLOG((kLOGERR,"_pTwunkId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  // Check that the DSID is valid...
  else if (!pod.m_ptwndsmapps->AppValidateIds(_pAppId,&_pTwunkId->identity))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  SSTRCPY((char*)_pTwunkId->dsPath, sizeof(_pTwunkId->dsPath),(char*)pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pTwunkId->identity.Id));

  return result;
}



/*
* Handle DAT_ENTRYPOINT.  This handles an application asking
* for entry point information.  Drivers have this information
* pushed to them in a different part of the code (just before
* DG_CONTORL/DAT_IDENTITY/MSG_OPENDS is received)
*/
TW_INT16 CTwnDsm::DSM_Entrypoint(TW_IDENTITY    *_pAppId,
                                 TW_UINT16      _MSG,
                                 TW_ENTRYPOINT  *_pEntrypoint)
{
  // Validate...
  if (0 == _pAppId)
  {
    kLOG((kLOGERR,"_pAppId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (MSG_GET != _MSG)
  {
    kLOG((kLOGERR,"protocol error"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
    return TWRC_FAILURE;
  }
  else if (0 == _pEntrypoint)
  {
    kLOG((kLOGERR,"_pEntrypoint is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (0 == _pEntrypoint->Size)
  {
    kLOG((kLOGERR,"_pEntrypoint is zero, it needs to be set to the size of TW_ENTRYPOINT..."));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (!(_pAppId->SupportedGroups & DF_APP2))
  {
    kLOG((kLOGERR,"_pAppId->SupportedGroups must include the DF_APP2 flag to make this call..."));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
    return TWRC_FAILURE;
  }

  // This is the TWAIN 2.0 minimum size.  If we add more values
  // in future we should create a new structure (ex: TW_ENTRYPOINT2)
  // and then add an if-statement for it...
  if (_pEntrypoint->Size < sizeof(TW_ENTRYPOINT))
  {
    kLOG((kLOGERR,"_pEntrypoint->Size minimum is %ld, we got %ld...",sizeof(TW_ENTRYPOINT),_pEntrypoint->Size));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  // TWAIN 2.0
  // Stock the structure with interesting stuff...
  else if (_pEntrypoint->Size == sizeof(TW_ENTRYPOINT))
  {
    _pEntrypoint->DSM_Entry        = ::DSM_Entry;
    _pEntrypoint->DSM_MemAllocate  = DSM_MemAllocate;
    _pEntrypoint->DSM_MemFree      = DSM_MemFree;
    _pEntrypoint->DSM_MemLock      = DSM_MemLock;
    _pEntrypoint->DSM_MemUnlock    = DSM_MemUnlock;
  }

  // Uh-oh...
  else
  {
    kLOG((kLOGERR,"_pEntrypoint->Size cannot be larger than %ld, we got %ld...",sizeof(TW_ENTRYPOINT),_pEntrypoint->Size));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  // All done...
  return TWRC_SUCCESS;
}



/*
* We've received a callback.  MSG_REGISTER_CALLBACK are from
* the Application and MSG_INVOKE_CALLBACK (Mac OSx only) 
* are from the DS.
* If Callbacks have not been registered by the App then when the 
* DS Invokes a callback, make a note of it so the next time the 
* application hits us with a Windows message for us to process, 
* we can send it the callback message...
*/
TW_INT16 CTwnDsm::DSM_Callback(TW_IDENTITY *_pOrigin,
                               TW_IDENTITY *_pDest,
                               TW_UINT16    _MSG,
                               TW_CALLBACK *_pData)
{
  TW_INT16      result;
  TW_CALLBACK  *ptwcallback;
  TW_CALLBACK2 *ptwcallback2;

  // Init stuff...
  result = TWRC_SUCCESS;

  // Take action on the message...
  switch (_MSG)
  {
    case MSG_REGISTER_CALLBACK:
      {
        // Origin is an App
        // Check that the ids are valid...
        if (!pod.m_ptwndsmapps->AppValidateIds(_pOrigin,_pDest))
        {
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
          return TWRC_FAILURE;
        }
        if(0 == _pData)
        {
          kLOG((kLOGERR,"Invalid data"));
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADVALUE);
          return TWRC_FAILURE;
        }

        ptwcallback2 = pod.m_ptwndsmapps->DsCallback2Get(_pOrigin,(TWID_T)_pDest->Id);
        ptwcallback2->CallBackProc = ((TW_CALLBACK*)_pData)->CallBackProc;
        ptwcallback2->RefCon = (TWID_T)((TW_CALLBACK*)_pData)->RefCon;
        ptwcallback2->Message = ((TW_CALLBACK*)_pData)->Message;
        pod.m_ptwndsmapps->DsCallbackSetWaiting(_pOrigin,(TWID_T)_pDest->Id,FALSE);
      }
      break;

    case MSG_INVOKE_CALLBACK:
      {
        // For backwards capability only.  MSG_INVOKE_CALLBACK is deprecated - use DAT_NULL
        // Origin is a DS
        // Check that the ids are valid...
        kLOG((kLOGINFO,"MSG_INVOKE_CALLBACK is deprecated - use DAT_NULL"));
        if (!pod.m_ptwndsmapps->AppValidateIds(_pDest,_pOrigin))
        {
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
          return TWRC_FAILURE;
        }
        if(0 == _pData)
        {
          kLOG((kLOGERR,"Invalid data"));
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADVALUE);
          return TWRC_FAILURE;
        }

        ptwcallback = (TW_CALLBACK*)_pData;
        result = DSM_Null(_pDest,_pOrigin,ptwcallback->Message);
      }
      break;

    default:
      result = TWRC_FAILURE;
      pod.m_ptwndsmapps->AppSetConditionCode(_pOrigin,TWCC_BADPROTOCOL);
      break;
  }

  return result;
}



/*
* We've received a callback.  MSG_REGISTER_CALLBACK are from
* the Application and MSG_INVOKE_CALLBACK (Mac OSx only) 
* are from the DS.
* If Callbacks have not been registered by the App then when the 
* DS Invokes a callback, make a note of it so the next time the 
* application hits us with a Windows message for us to process, 
* we can send it the callback message...
*/
TW_INT16 CTwnDsm::DSM_Callback2(TW_IDENTITY *_pOrigin,
                               TW_IDENTITY *_pDest,
                               TW_UINT16    _MSG,
                               TW_CALLBACK2 *_pData)
{
  TW_INT16      result;
  TW_CALLBACK2 *ptwcallback2;

  // Init stuff...
  result = TWRC_SUCCESS;

  // Take action on the message...
  switch (_MSG)
  {
    case MSG_REGISTER_CALLBACK:
      {
        // Origin is an App
        // Check that the ids are valid...
        if (!pod.m_ptwndsmapps->AppValidateIds(_pOrigin,_pDest))
        {
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
          return TWRC_FAILURE;
        }
        if(0 == _pData)
        {
          kLOG((kLOGERR,"Invalid data"));
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADVALUE);
          return TWRC_FAILURE;
        }

        ptwcallback2 = pod.m_ptwndsmapps->DsCallback2Get(_pOrigin,(TWID_T)_pDest->Id);
        memcpy(ptwcallback2,_pData,sizeof(*ptwcallback2));
        pod.m_ptwndsmapps->DsCallbackSetWaiting(_pOrigin,(TWID_T)_pDest->Id,FALSE);
      }
      break;

    case MSG_INVOKE_CALLBACK:
      {
        // For backwards capability only.  MSG_INVOKE_CALLBACK is deprecated - use DAT_NULL
        // Origin is a DS
        // Check that the ids are valid...
        kLOG((kLOGINFO,"MSG_INVOKE_CALLBACK is deprecated - use DAT_NULL"));
        if (!pod.m_ptwndsmapps->AppValidateIds(_pDest,_pOrigin))
        {
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADPROTOCOL);
          return TWRC_FAILURE;
        }
        if(0 == _pData)
        {
          kLOG((kLOGERR,"Invalid data"));
          pod.m_ptwndsmapps->AppSetConditionCode(0,TWCC_BADVALUE);
          return TWRC_FAILURE;
        }

        ptwcallback2 = (TW_CALLBACK2*)_pData;
        result = DSM_Null(_pDest,_pOrigin,ptwcallback2->Message);
      }
      break;

    default:
      result = TWRC_FAILURE;
      pod.m_ptwndsmapps->AppSetConditionCode(_pOrigin,TWCC_BADPROTOCOL);
      break;
  }

  return result;
}



/*
* Open the specified driver.  We're using the application identity
* and the driver identity we picked up during MSG_OPENDSM.  The
* application is just telling us which driver to load.  As part of
* a successful open we'll remember this driver's full path and file
* name as the new default driver.  On Windows this information goes
* into the registry.  On Linux we put it under the user's home
* directory...
*/
TW_INT16 CTwnDsm::OpenDS(TW_IDENTITY *_pAppId,
                         TW_IDENTITY *_pDsId)
{
  TW_INT16      result;
  TW_ENTRYPOINT twentrypoint;
 
  // Validate...
  if (0 == _pAppId)
  {
      kLOG((kLOGERR,"_pAppId is null"));
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
      return TWRC_FAILURE;
  }
  else if (   ((TWID_T)_pAppId->Id < 1)
           || ((TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp()))
  {
      kLOG((kLOGERR,"id is out of range...%d",(int)(TWID_T)_pAppId->Id));
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_MAXCONNECTIONS);
      return TWRC_FAILURE;
  }

  // Init stuff...
  result = TWRC_SUCCESS;

  // check that we are in the proper state
  if (dsmState_Open != pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    kLOG((kLOGERR,"DSM must be open before opening DS"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return(TWRC_FAILURE);
  }

  // check for valid data
  if(0 == _pDsId)
  {
    kLOG((kLOGERR,"_pDsId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }

  // Do we need to find a source to open
  if (0 == (TWID_T)_pDsId->Id)
  {
    // Does the app know the name of the source it wants to open
    if (0 != _pDsId->ProductName[0])
    {
      // The application is passing me a TW_IDENTITY structure that contains
      // the name of the source to select.
      result = GetDSFromProductName(_pAppId,_pDsId);

      // was the id found or specified by the app?
      if (TWRC_SUCCESS != result)
      {
        pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_NODS);
        return result;
      }
    }

    // Does the application want me to choose the default?
    // Or no Source located by name
    if (0 == _pDsId->ProductName[0]) 
    {
      // -if the name of the source is NULL, and the id is 0, the application is
      //  telling me to select the default source.
      result = GetMatchingDefault(_pAppId,_pDsId);

      // was the id found or specified by the app?
      if (TWRC_SUCCESS != result)
      {
        return result;
      }
    }
  }

  // Load the driver...
  result = pod.m_ptwndsmapps->LoadDS(_pAppId,(TWID_T)_pDsId->Id);
  if (result != TWRC_SUCCESS)
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_NODS);
    return TWRC_FAILURE;
  }

  // open the ds
  if (0 != pod.m_ptwndsmapps->DsGetEntryProc(_pAppId,(TWID_T)_pDsId->Id))
  {
    // If the DS reports support for DF_DS2, then send it our DAT_ENTRYPOINT
    // information.  Failure to handle this is treated like a failure to open...
    result = TWRC_SUCCESS;

    // Create a local copy of the AppIdentity
    TW_IDENTITY AppId = *pod.m_ptwndsmapps->AppGetIdentity(_pAppId);

    if (_pDsId->SupportedGroups & DF_DS2)
    {
      memset(&twentrypoint,0,sizeof(twentrypoint));
      twentrypoint.Size = sizeof(TW_ENTRYPOINT);
      twentrypoint.DSM_Entry        = ::DSM_Entry;
      twentrypoint.DSM_MemAllocate  = DSM_MemAllocate;
      twentrypoint.DSM_MemFree      = DSM_MemFree;
      twentrypoint.DSM_MemLock      = DSM_MemLock;
      twentrypoint.DSM_MemUnlock    = DSM_MemUnlock;
      result = pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)_pDsId->Id)(
                                &AppId,
                                DG_CONTROL,
                                DAT_ENTRYPOINT,
                                MSG_SET,
                                (TW_MEMREF)&twentrypoint);
    }

    // We have a problem...
    if (TWRC_SUCCESS != result)
    {
      kLOG((kLOGERR,"DAT_ENTRYPOINT failed..."));
      pod.m_ptwndsmapps->AppSetConditionCode(&AppId,TWCC_OPERATIONERROR);
    }

    // Okay, we're good.  Either we're a 1.x driver, or we were able to
    // push down our entrypoint info, so open the ds...
    else
    {
      result = pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)_pDsId->Id)(
                                &AppId,
                                DG_CONTROL,
                                DAT_IDENTITY,
                                MSG_OPENDS,
                                (TW_MEMREF)_pDsId);

      // Oh well...
      if (TWRC_SUCCESS != result)
      {
        kLOG((kLOGINFO,"MSG_OPENDS failed..."));
        TW_UINT16  rcDSMStatus;
		TW_STATUS  twstatus = { 0, { 0 } };
        // If the call to MSG_OPENDS fails, then we need to get the DAT_STATUS and squirrel
        // it away, because we're going to close this data source soon...
        rcDSMStatus = (pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)_pDsId->Id))(
					              &AppId,
					              DG_CONTROL,
					              DAT_STATUS,
					              MSG_GET,
					              (TW_MEMREF)&twstatus);
        if (rcDSMStatus == TWRC_SUCCESS)
        {
          pod.m_ptwndsmapps->AppSetConditionCode(&AppId,twstatus.ConditionCode);
        }
        else
        {
          pod.m_ptwndsmapps->AppSetConditionCode(&AppId,TWCC_NODS);
        }
      }
    }
  }

  // Remember that we opened this DS...
  if (TWRC_SUCCESS == result)
  {
    // Starting with TWAIN 2.1 the application will use DAT_IDENTITY / MSG_SET
    // to set the default DS
    if( ( _pAppId->ProtocolMajor == 2
       && _pAppId->ProtocolMinor == 0 )
     || _pAppId->ProtocolMajor < 2 ) 
    {
      #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
        // skip...
      #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
		int iResult;
        FILE *pfile;
        char *szHome;
        char szFile[FILENAME_MAX];
        szHome = getenv("HOME");
        if (szHome)
        {
          SSTRCPY(szFile,sizeof(szFile),szHome);
          SSTRCAT(szFile,sizeof(szFile),"/.twndsmrc");
          mkdir(szFile,0660);
          SSTRCAT(szFile,sizeof(szFile),"/defaultds");
          FOPEN(pfile,szFile,"w");
          if (pfile)
          {
            iResult = fwrite
			(
				pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pDsId->Id),
                1,
                strlen(pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pDsId->Id)),
                pfile
			);
			if (iResult < (int)strlen(pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pDsId->Id)))
			{
				kLOG((kLOGERR,"fwrite defaultds failed..."));
			}
            fclose(pfile);
          }
        }
      #else
        #error Sorry, we do not recognize this system...
      #endif
    }
  }

  // If we had an error, make sure we unload the ds...
  else
  {
    pod.m_ptwndsmapps->UnloadDS(_pAppId,(TWID_T)_pDsId->Id);
  }

  // All done...
  return result;
}



/*
* Close the specified driver...
*/
TW_INT16 CTwnDsm::CloseDS(TW_IDENTITY *_pAppId,
                          TW_IDENTITY *_pDsId)
{
  TW_INT16  result;

  // Validate...
  if (0 == _pAppId)
  {
    kLOG((kLOGERR,"_pAppId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (   ((TWID_T)_pAppId->Id < 1)
           || ((TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp()))
  {
    kLOG((kLOGERR,"id out of range...%d",(int)(TWID_T)_pAppId->Id));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  // Init stuff...
  result = TWRC_SUCCESS;

  // check that we are in the proper state
  if (dsmState_Open != pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return TWRC_FAILURE;
  }

  // Check for valid DS
  if(0 == _pDsId)
  {
    kLOG((kLOGERR,"_pDsId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }

  // close the ds
  if (0 != pod.m_ptwndsmapps->DsGetEntryProc(_pAppId,(TWID_T)_pDsId->Id))
  {
    // Create a local copy of the AppIdentity
    TW_IDENTITY AppId = *pod.m_ptwndsmapps->AppGetIdentity(_pAppId);

    result = (pod.m_ptwndsmapps->DsGetEntryProc(&AppId,(TWID_T)_pDsId->Id))(
                             &AppId,
                             DG_CONTROL,
                             DAT_IDENTITY,
                             MSG_CLOSEDS,
                             (TW_MEMREF)_pDsId);

    if (TWRC_SUCCESS != result)
    {
      pod.m_ptwndsmapps->AppSetConditionCode(&AppId,TWCC_OPERATIONERROR);
      return result;
    }

    // Cleanup...
    pod.m_ptwndsmapps->UnloadDS(&AppId,(TWID_T)_pDsId->Id);
  }

  // All done...
  return result;
}



#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
/**
* DllMain is only needed for Windows, and it's only needed to collect
* our instance handle, which is also our module handle.  Don't ever
* put anything else in here, not even logging messages.  It just isn't
* safe...
* @param[in] _hmodule handle to the application that loaded the DSM
* @param[in] _dwReasonCalled why this function is being called
* @return TRUE
*/
BOOL WINAPI DllMain(HINSTANCE _hmodule,
                    DWORD     _dwReasonCalled,
                    LPVOID)
{
  switch (_dwReasonCalled)
  {
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
      break;
    case DLL_PROCESS_ATTACH:
      g_hinstance = _hmodule;
      break;
    case DLL_PROCESS_DETACH:
      if( g_ptwndsm )
      {
        if( g_ptwndsm->DSMGetState() == dsmState_Open )
        {
          // This should never happen!
          // The Application should always close any open DS, then Close the DSM.
          kLOG((kLOGERR,"The DSM was left in an open state when it was unloaded!"));
        }
        delete g_ptwndsm;
        g_ptwndsm = 0;
      }
      break;
  }
  return(TRUE);
}
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    // Nothing for us to do...
#else
    #error Sorry, we do not recognize this system...
#endif



#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
/**
* We support a selection dialog on Windows.  I wish we didn't, it's
* more trouble than it's worth, but it's part of that legacy thing.
* This function is properly constructed for use with DialogBox...
* @param[in] _hWnd Window handle of the dialog
* @param[in] _Message message
* @param[in] _wParam wparam
* @param[in] _lParam lparam
* @return FALSE if we processed the message
*/
BOOL CALLBACK SelectDlgProc(HWND   _hWnd,
                            UINT   _Message,
                            WPARAM _wParam,
                            LPARAM _lParam)
{
  if (g_ptwndsm)
  {
    return g_ptwndsm->SelectDlgProc(_hWnd,_Message,_wParam,_lParam);
  }
  else
  {
    return TRUE;
  }
}
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  // We don't have one of these...
#else
  #error Sorry, we do not recognize this system...
#endif



#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
/**
* We support a selection dialog on Windows.  This function is
* part of our CTwnDsm class, so we don't have to have a lot
* of pointers, and we can keep things private, unlike what we
* would have to do if we put this code into the function we
* actually pass to DialogBox...
* @param[in] hwnd Window handle of the dialog
* @param[in] lParam lparam
* @return TRUE
*/
BOOL CALLBACK EnumChildProc
(
  HWND hwnd,
  LPARAM lParam
)
{
  ::SendMessage(hwnd,WM_SETFONT,(WPARAM)lParam,(LPARAM)TRUE);
  return TRUE;
}



BOOL CTwnDsm::SelectDlgProc(HWND hWnd,
                            UINT Message,
                            WPARAM wParam,
                            LPARAM /*lParam - unused*/)
{
  TW_IDENTITY  *pAppId;
  int           nIndex;
  int           nSelect;
  TW_UINT32     x;
  char         *szProductName;
  HWND          hListBox;
  RECT          rectParent;
  RECT          rectSelect;
  HWND          hParent;
  LANGID        LangId;
  wchar_t       uzUnicode[128];
  HFONT         hfont;
  LOGFONT       lf;
  POINT         point;
  int           nWidth;
  int           nHeight;

  // Init stuff...
  nSelect = 0;
  pAppId = pod.m_pSelectDlgAppId;

  // Process the message...
  switch (Message)
  {
    case WM_INITDIALOG:
      
      // If the caller wants us to figure out the language, do this...
      if (pAppId->Version.Language == (TW_UINT16)TWLG_USERLOCALE)
      {
      // Try to find the user's default language...
      LangId = ::GetUserDefaultLangID();
      for (nIndex = 0;
           s_twlocalize[nIndex].Language >= 0;
           nIndex++)
      {
        if (    (s_twlocalize[nIndex].LangId == LangId)
            &&  s_twlocalize[nIndex].LangId
            &&  s_twlocalize[nIndex].Title
            &&  s_twlocalize[nIndex].Title[0]
            &&  s_twlocalize[nIndex].Sources
            &&  s_twlocalize[nIndex].Sources[0]
            &&  s_twlocalize[nIndex].Select
            &&  s_twlocalize[nIndex].Select[0]
            &&  s_twlocalize[nIndex].Cancel
            &&  s_twlocalize[nIndex].Cancel[0])
        {
            break;
        }
      }

      // If that doesn't work, try for the primary language...
      if (s_twlocalize[nIndex].Language < 0)
      {
        LangId &= 0xFF;
        for (nIndex = 0;
             s_twlocalize[nIndex].Language >= 0;
             nIndex++)
        {
          if (  ((s_twlocalize[nIndex].LangId & 0xFF) == LangId)
            &&  s_twlocalize[nIndex].LangId
            &&  s_twlocalize[nIndex].Title
            &&  s_twlocalize[nIndex].Title[0]
            &&  s_twlocalize[nIndex].Sources
            &&  s_twlocalize[nIndex].Sources[0]
            &&  s_twlocalize[nIndex].Select
            &&  s_twlocalize[nIndex].Select[0]
            &&  s_twlocalize[nIndex].Cancel
            &&  s_twlocalize[nIndex].Cancel[0])
          {
            break;
          }
        }
      }
    }

    // Otherwise, use whatever the caller gave us...
    else
    {
      for (nIndex = 0;
           s_twlocalize[nIndex].Language >= 0;
           nIndex++)
      {
        if (    (s_twlocalize[nIndex].Language == pAppId->Version.Language)
            &&  s_twlocalize[nIndex].LangId
            &&  s_twlocalize[nIndex].Title
            &&  s_twlocalize[nIndex].Title[0]
            &&  s_twlocalize[nIndex].Sources
            &&  s_twlocalize[nIndex].Sources[0]
            &&  s_twlocalize[nIndex].Select
            &&  s_twlocalize[nIndex].Select[0]
            &&  s_twlocalize[nIndex].Cancel
            &&  s_twlocalize[nIndex].Cancel[0])
        {
          break;
        }
      }
    }

    // If we didn't find our language, go for English...
    if (s_twlocalize[nIndex].Language < 0)
    {
      for (nIndex = 0;
           s_twlocalize[nIndex].Language >= 0;
           nIndex++)
      {
        if (    (s_twlocalize[nIndex].Language == TWLG_ENGLISH)
            &&  s_twlocalize[nIndex].LangId
            &&  s_twlocalize[nIndex].Title
            &&  s_twlocalize[nIndex].Title[0]
            &&  s_twlocalize[nIndex].Sources
            &&  s_twlocalize[nIndex].Sources[0]
            &&  s_twlocalize[nIndex].Select
            &&  s_twlocalize[nIndex].Select[0]
            &&  s_twlocalize[nIndex].Cancel
            &&  s_twlocalize[nIndex].Cancel[0])
        {
          break;
        }
      }
    }

    // If we found something, then use it...
    if (s_twlocalize[nIndex].Language >= 0)
    {
      // Set our font...
      memset(&lf,0,sizeof(lf));
      lf.lfHeight = 16;
      lf.lfCharSet = s_twlocalize[nIndex].CharSet;
      hfont = CreateFontIndirect(&lf);
      EnumChildWindows(hWnd,EnumChildProc,(LPARAM)hfont);

      MultiByteToWideChar(CP_UTF8,0,s_twlocalize[nIndex].Title,-1,uzUnicode,sizeof(uzUnicode)/sizeof(*uzUnicode));
      SetWindowTextW(hWnd,uzUnicode);

      MultiByteToWideChar(CP_UTF8,0,s_twlocalize[nIndex].Sources,-1,uzUnicode,sizeof(uzUnicode)/sizeof(*uzUnicode));
      SetWindowTextW(::GetDlgItem(hWnd,IDC_STATIC),uzUnicode);

      MultiByteToWideChar(CP_UTF8,0,s_twlocalize[nIndex].Select,-1,uzUnicode,sizeof(uzUnicode)/sizeof(*uzUnicode));
      SetWindowTextW(::GetDlgItem(hWnd,IDOK),uzUnicode);

      MultiByteToWideChar(CP_UTF8,0,s_twlocalize[nIndex].Cancel,-1,uzUnicode,sizeof(uzUnicode)/sizeof(*uzUnicode));
      SetWindowTextW(::GetDlgItem(hWnd,IDCANCEL),uzUnicode);
    }

      hListBox = ::GetDlgItem(hWnd,ID_LST_SOURCES);
      if (hListBox) 
      {
        SendMessage(hListBox,LB_RESETCONTENT,(WPARAM)NULL,(LPARAM)NULL);

        for (x = 1; x < MAX_NUM_DS; ++x)
        {
          // We expect the list to be contiguous...
          szProductName = pod.m_ptwndsmapps->DsGetIdentity(pAppId,x)->ProductName;
          if (!szProductName[0])
          {
              break;
          }
          // Display the name...
          nIndex = (int)SendMessage(hListBox,LB_ADDSTRING,(WPARAM)NULL,(LPARAM)szProductName);
          if (LB_ERR == nIndex)
          {
            break;
          }
          // Associate the id with the name...
          nIndex = (int)SendMessage(hListBox,
                                    LB_SETITEMDATA,
                                    (WPARAM)nIndex,
                                    (LPARAM)pod.m_ptwndsmapps->DsGetIdentity(pAppId,x)->Id);
          if (LB_ERR == nIndex)
          {
            break;
          }
          // Remember this item if it's the default...
          if (!strcmp(pod.m_ptwndsmapps->DsGetPath(pAppId,x),pod.m_DefaultDSPath))
          {
            nSelect = x;
          }
        }
        // If we have no drivers, then disable the OK button...
        if (pod.m_ptwndsmapps->AppGetNumDs(pAppId) < 1)
        {
          HWND hOK= ::GetDlgItem(hWnd,IDOK);
          EnableWindow(hOK, FALSE);
        }
        // Otherwise select the defaulted item...
        else
        {
          nIndex = (int)SendMessage(hListBox,
                                    LB_FINDSTRINGEXACT,
                                    (WPARAM)-1,
                                    (LPARAM)pod.m_ptwndsmapps->DsGetIdentity(pAppId,nSelect)->ProductName);
          if (LB_ERR == nIndex)
          {
            nIndex = 0;
          }
          SendMessage(hListBox,LB_SETCURSEL,(WPARAM)nIndex,(LPARAM)NULL);
        }
      }

    // Center our dialog on the window reported to us in MSG_OPENDS...
    hParent = (HWND)pod.m_ptwndsmapps->AppHwnd(pAppId);
    if (hParent)
    {
      GetClientRect(hParent,&rectParent);
      GetWindowRect(hWnd,&rectSelect);
          nWidth  = (rectSelect.right - rectSelect.left);
          nHeight = (rectSelect.bottom - rectSelect.top);
      point.x = (rectParent.right - rectParent.left) / 2;
      point.y = (rectParent.bottom - rectParent.top) / 2;
      ClientToScreen(hParent,&point);
      point.x -= nWidth / 2;
      point.y -= nHeight / 2;
      // keep the dialog visible on the screen
      if(point.x < 0)
      {
        point.x = 0;
      }
      if(point.y < 0)
      {
        point.y = 0;
      }
      MoveWindow(hWnd,point.x,point.y,nWidth,nHeight,FALSE);
    }

      return TRUE;

    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case ID_LST_SOURCES: 
          if (HIWORD(wParam) != LBN_DBLCLK) 
            break; 
            // drop through...

        case IDOK:
          {
            hListBox = ::GetDlgItem(hWnd, ID_LST_SOURCES); 
            nIndex   = 0;
            if ( hListBox ) 
            {
              nIndex = (int)SendMessage(hListBox,LB_GETCURSEL,(WPARAM)0,(LPARAM)0);
              if (LB_ERR == nIndex)
              {
                // if there is no selection should not have OK available
                // to press in the first place.
                return TRUE;
              }
              nIndex = (int)SendMessage(hListBox,LB_GETITEMDATA,(WPARAM)nIndex,(LPARAM)0);
              if (LB_ERR != nIndex)
              {
                pod.m_pSelectDlgDsId = pod.m_ptwndsmapps->DsGetIdentity(pAppId,nIndex);
              }
            }
            EndDialog(hWnd, IDOK);
            return TRUE;
          }
        case IDCANCEL:
          EndDialog(hWnd, IDCANCEL);
          return TRUE;
      }
      break;
  }
  return FALSE;
}
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  // We don't have anything to do on Linux...
#else
  #error Sorry, we do not recognize this system...
#endif



/*
* Invoke the user selection dialog box.  We only support this for
* Windows, for Linux it's a bad protocol, since there is no way
* to query the user (nicely) across all consoles and graphical
* interfaces for all distributions...
*/
TW_INT16 CTwnDsm::DSM_SelectDS(TW_IDENTITY *_pAppId,
                               TW_IDENTITY *_pDsId)
{
  // Validate...
  if (0 == _pAppId)
  {
    kLOG((kLOGERR,"_pAppId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  if (   ((TWID_T)_pAppId->Id < 1)
      || ((TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp()))
  {
    kLOG((kLOGERR,"_pAppId.Id is out of range"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (0 == _pDsId)
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }
  else if (dsmState_Open != pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return TWRC_FAILURE;
  }

  /** @todo scanDSDir needs to be done with each MSG_USERSELECT  
    currently we are only scanDSDir when an App opens the DSM **/

  // Make sure the id is 0 before we go into this...
  _pDsId->Id = 0;

  // Windows...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)

      HKEY      hKey;
      long      status;
      char     *szPath;
      TW_INT16  result;

      // Set the context...
      result = TWRC_SUCCESS;
      pod.m_pSelectDlgAppId = _pAppId;
        
      // If passed in a DS name we want to select it
      if (_pDsId->ProductName[0] != 0)
      {
        result = GetDSFromProductName(_pAppId,_pDsId);
        // If no match continue anyway.
      }

      // If not passed a DS or the name was not currently found
      // then selete the default
      _pDsId->Id = 0;
      result = GetMatchingDefault(_pAppId,_pDsId);
      pod.m_pSelectDlgDsId = _pDsId;

      // a.walling - Get the HWND of the parent window, if any, and use it as the dialog's parent
      HWND hParent = (HWND)pod.m_ptwndsmapps->AppHwnd(_pAppId);

      // create the dialog window
      int ret = (int)::DialogBoxW(g_hinstance,
                                  (LPCWSTR)IDD_DLG_SOURCE,
                                  (HWND)hParent,
                                  (DLGPROC)::SelectDlgProc);

      // User picked something...
      if (ret == IDOK)
      {
        // Validate the result...
        if (!pod.m_pSelectDlgDsId)
        {
          kLOG((kLOGERR,"We came out of the Select Dialog with a null..."));
          pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
          return TWRC_FAILURE;
        }

        // Copy the data over...
        *_pDsId = *pod.m_pSelectDlgDsId;

        // save default source to Registry  
        // sanity check...
        if (   (pod.m_pSelectDlgDsId->Id < 1)
            || (pod.m_pSelectDlgDsId->Id >= MAX_NUM_DS))
        {
          // Failed to save default DS to registry
          kLOG((kLOGERR,"Id is out of range 0 - 49..."));
          // Nothing preventing us from using the default right now
          pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BUMMER);
        }

        else
        {
          // Get the path we're using...
          status = ERROR_SUCCESS;
          szPath = pod.m_ptwndsmapps->DsGetPath(pod.m_pSelectDlgAppId,pod.m_pSelectDlgDsId->Id);

          // Open the key, creating it if it doesn't exist.
          if (RegCreateKeyEx(HKEY_CURRENT_USER, 
                             TWNDSM_DS_REG_LOC,
                             NULL,
                             NULL,
                             NULL,
                             KEY_READ | KEY_WRITE, NULL,
                             &hKey,
                             NULL) == ERROR_SUCCESS)
          {
            status = RegSetValueEx(hKey,"Default Source",0,REG_SZ,(LPBYTE)szPath,(DWORD)strlen((char*)szPath)+1);
            if (status != ERROR_SUCCESS)
            {
              // Failed to save default DS to registry
              kLOG((kLOGERR,"Failed to save default DS to registry"));
              // Nothing preventing us from using the default right now
              pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BUMMER);
            }
          }
          // Close the key.
          RegCloseKey(hKey);
        }
      }

      // We're cancelling...
      else if (ret == IDCANCEL)
      {
        result = TWRC_CANCEL;
      }

      // Something back happened...
      else if (ret == -1)
      {
        ::MessageBox(NULL,"Dialog failed!","Error",MB_OK|MB_ICONINFORMATION);
        pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BUMMER);
        result = TWRC_FAILURE;
      }

      return result;

  // We don't support the user selection box on linux...
  #elif  (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)

    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
    return TWRC_FAILURE;

  // Ruh-roh, Reorge...
  #else
    #error Sorry, we do not recognize this system...
  #endif
}


/*
* Set the datasource as the default
*/
TW_INT16 CTwnDsm::DSM_SetDefaultDS(TW_IDENTITY *_pAppId,
                                   TW_IDENTITY *_pDsId)
{
  TW_INT16 result = TWRC_SUCCESS;

  // Validate app ...
  if (0 == _pAppId)
  {
    kLOG((kLOGERR,"_pAppId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if ( (TWID_T)_pAppId->Id < 1
         || (TWID_T)_pAppId->Id >= pod.m_ptwndsmapps->AppGetNumApp() )
  {
    kLOG((kLOGERR,"_pAppId.Id is out of range...%d",(int)(TWID_T)_pAppId->Id));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }
  else if (dsmState_Open != pod.m_ptwndsmapps->AppGetState(_pAppId))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return TWRC_FAILURE;
  }

  // Validate DS
  if(0 == _pDsId)
  {
    kLOG((kLOGERR,"_pDsId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }
  else if ((TWID_T)_pDsId->Id < 1
        || (TWID_T)_pDsId->Id >= MAX_NUM_DS)
  {
    kLOG((kLOGERR,"Id is out of range 0 - 49..."));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  char     *szPath = pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pDsId->Id); // Get the path we're using...

  if(!szPath)
  {
    kLOG((kLOGERR,"DS is not valid"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADVALUE);
    return TWRC_FAILURE;
  }

  // Windows... save default source to Registry  
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    HKEY      hKey;
    long      status = ERROR_SUCCESS;

    // Open the key, creating it if it doesn't exist.
    if (RegCreateKeyEx(HKEY_CURRENT_USER, 
                       TWNDSM_DS_REG_LOC,
                       NULL,
                       NULL,
                       NULL,
                       KEY_READ | KEY_WRITE, NULL,
                       &hKey,
                       NULL) == ERROR_SUCCESS)
    {
      status = RegSetValueEx(hKey,"Default Source",0,REG_SZ,(LPBYTE)szPath,(DWORD)strlen((char*)szPath)+1);
      if (status != ERROR_SUCCESS)
      {
        // Failed to save default DS to registry
        kLOG((kLOGERR,"Failed to save default DS to registry"));
        // Nothing preventing us from using the default right now
        pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BUMMER);
        result = TWRC_FAILURE;
      }

      // Close the key.
      RegCloseKey(hKey);
    }

  // Linux looks in the user's directory...
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    int iResult;
    FILE *pfile;
    char *szHome;
    char szFile[FILENAME_MAX];
    szHome = getenv("HOME");
    if (szHome)
    {
      SSTRCPY(szFile,sizeof(szFile),szHome);
      SSTRCAT(szFile,sizeof(szFile),"/.twndsmrc/defaultds");
      FOPEN(pfile,szFile,"w");
      if (pfile)
      {
        iResult = fwrite(szPath, 1, strlen(szPath), pfile);
		if (iResult < (int)strlen(pod.m_ptwndsmapps->DsGetPath(_pAppId,(TWID_T)_pDsId->Id)))
		{
			kLOG((kLOGERR,"fwrite defaultds failed..."));
		}
        fclose(pfile);
      }
    }

  // eek...
  #else
    #error Sorry, we do not recognize this system...
    result = TWRC_FAILURE
  #endif

  return result;
}


/*
* Invoke the user selection dialog box.  We only support this for
* Windows, for Linux it's a bad protocol, since there is no way
* to query the user (nicely) across all consoles and graphical
* interfaces for all distributions...
*/
TW_INT16 CTwnDsm::GetDSFromProductName(TW_IDENTITY *_pAppId,
                                       TW_IDENTITY *_pDsId)
{
  TWID_T ii;

  // Validate...
  if (   !pod.m_ptwndsmapps->AppValidateId(_pAppId)
      || (0 == _pDsId))
  {
    kLOG((kLOGERR,"bad _pAppId or _pDsId..."));
    return TWRC_FAILURE;
  }
  else if (0 == _pDsId->ProductName[0])
  {
    return TWRC_FAILURE;
  }

  // Search for a match on the ProductName...
  for (ii = 1; ii < MAX_NUM_DS; ++ii)
  {
    // Note that TW_STR32 type is NUL-filled, not NUL-terminated...
    if (0 == strncmp((char*)_pDsId->ProductName,
                     (char*)pod.m_ptwndsmapps->DsGetIdentity(_pAppId,ii)->ProductName,
                     sizeof(TW_STR32)))
    {
      // match found, set the index
      *_pDsId = *pod.m_ptwndsmapps->DsGetIdentity(_pAppId,ii);
      return TWRC_SUCCESS;
    }
  }

  // Uh-oh...
  return TWRC_FAILURE;
}



/*
* Get the identity for the first driver we found, or TWRC_ENDOFLIST
* if we don't have any...
*/
TW_INT16 CTwnDsm::DSM_GetFirst(TW_IDENTITY *_pAppId,
                               TW_IDENTITY *_pDsId)
{
  // Validate...
  if (   !pod.m_ptwndsmapps->AppValidateId(_pAppId)
      || (0 == _pDsId))
  {
    kLOG((kLOGERR,"bad _pAppId or _pDsId..."));
    return TWRC_FAILURE;
  }

  /** @todo scanDSDir needs to be done with each MSG_GETFIRST  
      currently we are only scanDSDir when an App opens the DSM **/

  // There are no supported drivers...
  if (pod.m_ptwndsmapps->AppGetNumDs(_pAppId) < 1)
  {
    // Make sure we fail of GetNext is called...
    pod.m_nextDsId = pod.m_ptwndsmapps->AppGetNumDs(_pAppId) + 1;
    return TWRC_ENDOFLIST;
  }

  // Check for valid DS
  if(0 == _pDsId)
  {
    kLOG((kLOGERR,"_pDsId is null"));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }


  // Return info on the first driver we found...
  pod.m_nextDsId = 1;
  *_pDsId = *pod.m_ptwndsmapps->DsGetIdentity(_pAppId,pod.m_nextDsId);

  // All done...
  return TWRC_SUCCESS;
}



/*
* Get the identity for the next driver we found, or TWRC_ENDOFLIST
* if we've run out...
*/
TW_INT16 CTwnDsm::DSM_GetNext(TW_IDENTITY *_pAppId,
                              TW_IDENTITY *_pDsId)
{
  // Validate...
  if (   !pod.m_ptwndsmapps->AppValidateId(_pAppId)
      || (0 == _pDsId))
  {
    kLOG((kLOGERR,"bad _pAppId or _pDsId..."));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }

  // Applications must call MSG_GETFIRST before making this call...
  if (pod.m_nextDsId == 0)
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_SEQERROR);
    return TWRC_FAILURE;
  }

  // Prep for a call to GetNext...
  pod.m_nextDsId += 1;

  // We're out of items...
  if (pod.m_nextDsId > pod.m_ptwndsmapps->AppGetNumDs(_pAppId))
  {
    pod.m_nextDsId = 0;
    return TWRC_ENDOFLIST;
  }

  // Return info on the this driver...
  *_pDsId = *pod.m_ptwndsmapps->DsGetIdentity(_pAppId,pod.m_nextDsId);

  // All done...
  return TWRC_SUCCESS;
}



/*
* Get the identity of the default source...
*/
TW_INT16 CTwnDsm::GetMatchingDefault(TW_IDENTITY *_pAppId,
                                     TW_IDENTITY *_pDsId)
{
  bool      bMatchFnd = false;
  bool      bDefaultFound = false;
  TW_UINT32 ii;

  // Validate...
  if (   !pod.m_ptwndsmapps->AppValidateId(_pAppId)
      || (0 == _pDsId))
  {
    kLOG((kLOGERR,"bad _pAppId or _pDsId..."));
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADDEST);
    return TWRC_FAILURE;
  }

  // is there something to match to?
  if (pod.m_ptwndsmapps->AppGetNumDs(_pAppId) < 1)
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_NODS);
    return TWRC_FAILURE;
  }

  // Something very bad may be happening, so don't let the
  // application get away with this...
  if (0 != (TWID_T)_pDsId->Id)
  {
    kLOG((kLOGINFO,"Please make sure your TW_IDENTITY.Id for your driver (the destination) is zeroed out before making this call..."));
    //pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_OPERATIONERROR);
    //return TWRC_FAILURE;
  }

  // In Windows the default Data Source is stored in the registry
  // as the path to that DS.  We will need to compare this to the other DS as a match.
  // read default source from Registry
  memset(pod.m_DefaultDSPath,0,sizeof(pod.m_DefaultDSPath));

  // Windows uses the registry...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    HKEY hKey;
    if (RegOpenKeyEx(HKEY_CURRENT_USER,
                     TWNDSM_DS_REG_LOC,
                     0,
                     KEY_READ,
                     &hKey) == ERROR_SUCCESS )
    {
      // Look for the subkey "Default Source".
      DWORD DWtype = REG_SZ;
      DWORD DWsize = sizeof(pod.m_DefaultDSPath);
      bDefaultFound = ( RegQueryValueEx(hKey,"Default Source",NULL,&DWtype,(LPBYTE)pod.m_DefaultDSPath,&DWsize) == ERROR_SUCCESS);

      // Close the registry key handle.
      RegCloseKey(hKey);
    }

  // Linux looks in the user's directory...
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    int iResult;
    FILE *pfile;
    char *szHome;
    char szFile[FILENAME_MAX];
    memset(pod.m_DefaultDSPath,0,sizeof(pod.m_DefaultDSPath));
    szHome = getenv("HOME");
    if (szHome)
    {
      SSTRCPY(szFile,sizeof(szFile),szHome);
      SSTRCAT(szFile,sizeof(szFile),"/.twndsmrc/defaultds");
      FOPEN(pfile,szFile,"r");
      if (pfile)
      {
        iResult = fread(pod.m_DefaultDSPath,1,sizeof(pod.m_DefaultDSPath)-1,pfile);
		if (iResult <= 0)
		{
			kLOG((kLOGINFO,"The defaultds file is empty, this is okay..."));
			pod.m_DefaultDSPath[0] = 0;
		}
        bDefaultFound = true;
        fclose(pfile);
      }
    }

  // eek...
  #else
    #error Sorry, we do not recognize this system...
  #endif


  // If current default source is not a match find a new default source
  // that will match this app
  for (ii = 1; ii < MAX_NUM_DS; ++ii)
  {
    // Mark the first match to use as default, if we don't
    // find a match, this will be the one we go with...
    if (!bMatchFnd)
    {
      *_pDsId = *pod.m_ptwndsmapps->DsGetIdentity(_pAppId,ii);
      bMatchFnd = true;

      //If no default was saved no need to go checking for a match
      if(!bDefaultFound)
      {
        break;
      }
    }

    // If the system default is a match we will use it and stop looking.
    if (0 == STRNICMP(pod.m_DefaultDSPath,
                      pod.m_ptwndsmapps->DsGetPath(_pAppId,ii),
                      sizeof(pod.m_DefaultDSPath)))
    {
      *_pDsId = *pod.m_ptwndsmapps->DsGetIdentity(_pAppId,ii);
      bMatchFnd = true;
      break;
    }
  }

  if (!bMatchFnd)
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_NODS);
    return TWRC_FAILURE;
  }

  return TWRC_SUCCESS;
}


/*
* Log the triplets that the application sends to us...
*/
bool CTwnDsm::printTripletsInfo(const TW_IDENTITY *_pOrigin,
                                const TW_IDENTITY *_pDest,
                                const TW_UINT32 _DG,
                                const TW_UINT16 _DAT,
                                const TW_UINT16 _MSG,
                                const TW_MEMREF _pData)
{
  char szDg[64];
  char szDat[64];
  char szMsg[64];
  char szData[128];
  TW_CAPABILITY *_pCap;

  // Don't spend time processing the triplet if we are not logging.
  if( !g_ptwndsmlog )
  {
    return false;
  }

  // too many of these messages to log...
  if (    (DG_CONTROL == _DG)
      &&  (DAT_EVENT == _DAT))
  {
    return false;
  }

  // Convert them...
  StringFromDg(szDg,NCHARS(szDg),_DG);
  StringFromDat(szDat,NCHARS(szDat),_DAT);
  StringFromMsg(szMsg,NCHARS(szMsg),_MSG);

  memset(szData, 0, sizeof(szData));

  // If we have data do some extra work to see what it might be
  if(NULL != _pData)
  {
    // If we're a capability, try to tell them what cap it is...
    if (   (DG_CONTROL == _DG)
        && (DAT_CAPABILITY == _DAT) )
    {
      _pCap = (TW_CAPABILITY*)_pData;
      StringFromCap(szData,NCHARS(szData),_pCap->Cap);

      // If sending a container, try to tell them what...
      if( MSG_SET == _MSG )
      {
        char szType[32];
        StringFromConType(szType,NCHARS(szType),_pCap->ConType);
        SSTRCAT(szData,NCHARS(szData),szType);
      }
    }
  }

  // Print out the orgin and Destination
  kLOG((kLOGINFO,"%.32s -> %.32s",_pOrigin? (char*)_pOrigin->ProductName:"DSM", _pDest? (char*)_pDest->ProductName:"DSM"));
  
  // Print them
  if(strlen(szData))
  {
    kLOG((kLOGINFO,"%s/%s/%s/%s",szDg,szDat,szMsg,szData));
  }
  else
  {
    kLOG((kLOGINFO,"%s/%s/%s",szDg,szDat,szMsg));
  }
  g_ptwndsmlog->Indent(1);

  // All done...
  return true;
}

/*
* Log the results after processing the Triplet
*/
void CTwnDsm::printResults(const TW_UINT32 _DG,
                           const TW_UINT16 _DAT,
                           const TW_UINT16 _MSG,
                           const TW_MEMREF _pData,
                           const TW_UINT16 _RC)
{
  char szRc[64];

  StringFromRC(szRc,NCHARS(szRc),_RC);

  // If we have data do some extra work to see what it might be
  if( NULL != _pData && TWRC_FAILURE != _RC)
  {
    if( DG_CONTROL == _DG
     && DAT_CAPABILITY == _DAT 
     && ( MSG_GET == _MSG || MSG_GETCURRENT == _MSG || MSG_GETDEFAULT == _MSG || MSG_RESET == _MSG ) )
    {
        TW_CAPABILITY *_pCap = (TW_CAPABILITY*)_pData;
        char szType[32];
        StringFromConType(szType, NCHARS(szType), _pCap->ConType);
        SSTRCAT(szRc, NCHARS(szRc), szType);
    }
    else if( DG_CONTROL == _DG
     && DAT_PENDINGXFERS == _DAT )
    {
        TW_PENDINGXFERS *pXfer = (TW_PENDINGXFERS*)_pData;
        char szValue[32];
        SSNPRINTF(szValue, NCHARS(szValue), NCHARS(szValue), " Count = %d", pXfer->Count==0xFFFF?-1:pXfer->Count);
        SSTRCAT(szRc, NCHARS(szRc), szValue);
    }
    else if( DG_CONTROL == _DG
     && DAT_STATUS == _DAT )
    {
        TW_STATUS *pStatus = (TW_STATUS*)_pData;
        char szStatus[32];
        StringFromConditionCode(szStatus, NCHARS(szStatus), pStatus->ConditionCode);
        SSTRCAT(szRc, NCHARS(szRc), szStatus);
    }
  }

  // ... and add a blank line
  SSTRCAT(szRc,NCHARS(szRc),"\n");

  g_ptwndsmlog->Indent(-1);
  kLOG((kLOGINFO,szRc));
}

/*
* DAT_NULL is used by a driver to send certain messages back to the
* application, like MSG_XFERREADY...
*/
TW_INT16 CTwnDsm::DSM_Null(TW_IDENTITY *_pAppId,
                           TW_IDENTITY *_pDsId,
                           TW_UINT16    _MSG)
{
  TW_CALLBACK2 *ptwcallback2 = 0;
  TW_INT16      result    = TWRC_SUCCESS;
  TW_MEMREF     MemRef; 
  bool          bPrinted  = false;

  // Validate...
  if ( !pod.m_ptwndsmapps->AppValidateIds(_pAppId,_pDsId) )
  {
    kLOG((kLOGERR,"bad _pAppId or _pDsId..."));
    return TWRC_FAILURE;
  }

  // Invoke the application's callback to send this message along.
  if (   (MSG_DEVICEEVENT != _MSG)
      && (MSG_CLOSEDSOK   != _MSG)
      && (MSG_CLOSEDSREQ  != _MSG)
      && (MSG_XFERREADY   != _MSG))
  {
    pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BADPROTOCOL);
    return TWRC_FAILURE;
  }

  // Get the current callback...
  ptwcallback2 = pod.m_ptwndsmapps->DsCallback2Get(_pAppId,(TWID_T)_pDsId->Id);

  // We have something to call...
  if (   (0 != ptwcallback2)
    && (ptwcallback2->CallBackProc))
  {
    // RefCon is returned back to the calling application in pData
    // Unfortunately RefCon is defined as TW_INT32
    // Application writers that want to store a pointer in RefCon
    // on 64bit will need to store an index to local storage.
    MemRef = (TW_MEMREF)ptwcallback2->RefCon;

    // Set flag to prevent Application from sending a new message 
    // before returning back from recieving this callback.
    pod.m_ptwndsmapps->DsSetAppProcessingCallback(_pAppId,(TWID_T)_pDsId->Id,TRUE);

    // We should have a try/catch around this...
    // Send a message from DS to the Application.
    // Rare case where the origin is the DS and dest is the App
    try
    {
      // Create a local copy of the AppIdentity
      TW_IDENTITY AppId = *pod.m_ptwndsmapps->AppGetIdentity(_pAppId);

      // Print the triplets to stdout for information purposes
      bPrinted = printTripletsInfo(NULL,&AppId,DG_CONTROL,DAT_NULL,_MSG,MemRef);

	  // Send a pointer to the data...
      result = ((DSMENTRYPROC)(ptwcallback2->CallBackProc))(
          pod.m_ptwndsmapps->DsGetIdentity(&AppId,(TWID_T)_pDsId->Id),
          &AppId,
          DG_CONTROL,
          DAT_NULL,
          _MSG,
          MemRef);
    }
    catch(...)
    {
      pod.m_ptwndsmapps->AppSetConditionCode(_pAppId,TWCC_BUMMER);
      kLOG((kLOGERR,"Exception caught while App was processing message.  Returning Failure."));
      result = TWRC_FAILURE;
    }

    pod.m_ptwndsmapps->DsSetAppProcessingCallback(_pAppId,(TWID_T)_pDsId->Id,FALSE);
  }

  // Application has not registered a callback. As a result, the msg will
  // be sent to the app the next time it forwards an event.
  // Each App's DS has a callback structure.   This way multiple DS's can make
  // a callback to a single app, and we are not going to lose MSG's
  else
  {
	  if (ptwcallback2)
	  {
		  if (ptwcallback2->Message != 0)
		  {
				char szMsg[64];
				StringFromMsg(szMsg, NCHARS(szMsg), ptwcallback2->Message);
				kLOG((kLOGERR, "%.32s NEVER retrieved DAT_EVENT / %s\n", _pAppId->ProductName, szMsg));
		  }
		  ptwcallback2->Message = _MSG;
	  }
	  pod.m_ptwndsmapps->DsCallbackSetWaiting(_pAppId,(TWID_T)_pDsId->Id,TRUE);
      pod.m_ptwndsmapps->AppWakeup(_pAppId);
  }

  // Log how it went...
  if (bPrinted)
  {
    printResults(DG_CONTROL,DAT_NULL,_MSG,MemRef,result);
  }

  return result;
}



/*
* Convert a DG_ data group numerical value to a string...
*/
void CTwnDsm::StringFromDg(char      *_szDg,
                     const int        _nChars,
                     const TW_UINT32  _DG)
{
  switch(_DG)
  {
    default:
      SSNPRINTF(_szDg,_nChars,_nChars,"DG_0x%04lx",_DG);
      break;

    case DG_CONTROL:
      SSTRCPY(_szDg,_nChars,"DG_CONTROL");
      break;

    case DG_IMAGE:
      SSTRCPY(_szDg,_nChars,"DG_IMAGE");
      break;

    case DG_AUDIO:
      SSTRCPY(_szDg,_nChars,"DG_AUDIO");
      break;
  }
}



/*
* Convert a DAT_ data argument type numerical value to a string...
*/
void CTwnDsm::StringFromDat(char     *_szDat,
                      const int       _nChars,
                      const TW_UINT16 _DAT)
{
  switch(_DAT)
  {
    default:
      SSNPRINTF(_szDat,_nChars,_nChars,"DAT_0x%04x",_DAT);

    case DAT_NULL:
      SSTRCPY(_szDat,_nChars,"DAT_NULL");
      break;

    case DAT_CUSTOMBASE:
      SSTRCPY(_szDat,_nChars,"DAT_CUSTOMBASE");
      break;

    case DAT_CAPABILITY:
      SSTRCPY(_szDat,_nChars,"DAT_CAPABILITY");
      break;

    case DAT_EVENT:
      SSTRCPY(_szDat,_nChars,"DAT_EVENT");
      break;

    case DAT_IDENTITY:
      SSTRCPY(_szDat,_nChars,"DAT_IDENTITY");
      break;

    case DAT_PARENT:
      SSTRCPY(_szDat,_nChars,"DAT_PARENT");
      break;

    case DAT_PENDINGXFERS:
      SSTRCPY(_szDat,_nChars,"DAT_PENDINGXFERS");
      break;

    case DAT_SETUPMEMXFER:
      SSTRCPY(_szDat,_nChars,"DAT_SETUPMEMXFER");
      break;

    case DAT_SETUPFILEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_SETUPFILEXFER");
      break;

    case DAT_STATUS:
      SSTRCPY(_szDat,_nChars,"DAT_STATUS");
      break;

    case DAT_USERINTERFACE:
      SSTRCPY(_szDat,_nChars,"DAT_USERINTERFACE");
      break;

    case DAT_XFERGROUP:
      SSTRCPY(_szDat,_nChars,"DAT_XFERGROUP");
      break;

    case DAT_TWUNKIDENTITY:
      SSTRCPY(_szDat,_nChars,"DAT_TWUNKIDENTITY");
      break;

    case DAT_CUSTOMDSDATA:
      SSTRCPY(_szDat,_nChars,"DAT_CUSTOMDSDATA");
      break;

    case DAT_DEVICEEVENT:
      SSTRCPY(_szDat,_nChars,"DAT_DEVICEEVENT");
      break;

    case DAT_FILESYSTEM:
      SSTRCPY(_szDat,_nChars,"DAT_FILESYSTEM");
      break;

    case DAT_PASSTHRU:
      SSTRCPY(_szDat,_nChars,"DAT_PASSTHRU");
      break;

    case DAT_CALLBACK:
      SSTRCPY(_szDat,_nChars,"DAT_CALLBACK");
      break;

    case DAT_STATUSUTF8:
      SSTRCPY(_szDat,_nChars,"DAT_STATUSUTF8");
      break;

    case DAT_IMAGEINFO:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGEINFO");
      break;

    case DAT_IMAGELAYOUT:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGELAYOUT");
      break;

    case DAT_IMAGEMEMXFER:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGEMEMXFER");
      break;

    case DAT_IMAGENATIVEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGENATIVEXFER");
      break;

    case DAT_IMAGEFILEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGEFILEXFER");
      break;

    case DAT_CIECOLOR:
      SSTRCPY(_szDat,_nChars,"DAT_CIECOLOR");
      break;

    case DAT_GRAYRESPONSE:
      SSTRCPY(_szDat,_nChars,"DAT_GRAYRESPONSE");
      break;

    case DAT_RGBRESPONSE:
      SSTRCPY(_szDat,_nChars,"DAT_RGBRESPONSE");
      break;

    case DAT_JPEGCOMPRESSION:
      SSTRCPY(_szDat,_nChars,"DAT_JPEGCOMPRESSION");
      break;

    case DAT_PALETTE8:
      SSTRCPY(_szDat,_nChars,"DAT_PALETTE8");
      break;

    case DAT_EXTIMAGEINFO:
      SSTRCPY(_szDat,_nChars,"DAT_EXTIMAGEINFO");
      break;

    case DAT_AUDIOFILEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_AUDIOFILEXFER");
      break;

    case DAT_AUDIOINFO:
      SSTRCPY(_szDat,_nChars,"DAT_AUDIOINFO");
      break;

    case DAT_AUDIONATIVEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_AUDIONATIVEXFER");
      break;

    case DAT_ICCPROFILE:
      SSTRCPY(_szDat,_nChars,"DAT_ICCPROFILE");
      break;

    case DAT_IMAGEMEMFILEXFER:
      SSTRCPY(_szDat,_nChars,"DAT_IMAGEMEMFILEXFER");
      break;

    case DAT_ENTRYPOINT:
      SSTRCPY(_szDat,_nChars,"DAT_ENTRYPOINT");
      break;
  }
}



/*
* Convert a MSG_ message numerical value to a string...
*/
void CTwnDsm::StringFromMsg(char     *_szMsg,
                      const int       _nChars,
                      const TW_UINT16 _MSG)
{
  switch (_MSG)
  {
    default:  
      SSNPRINTF(_szMsg,_nChars,_nChars,"MSG_0x%04x",_MSG);
      break;

    case MSG_NULL:
      SSTRCPY(_szMsg,_nChars,"MSG_NULL");
      break;

    case MSG_CUSTOMBASE:
      SSTRCPY(_szMsg,_nChars,"MSG_CUSTOMBASE");
      break;

    case MSG_GET:
      SSTRCPY(_szMsg,_nChars,"MSG_GET");
      break;

    case MSG_GETCURRENT:
      SSTRCPY(_szMsg,_nChars,"MSG_GETCURRENT");
      break;

    case MSG_GETDEFAULT:
      SSTRCPY(_szMsg,_nChars,"MSG_GETDEFAULT");
      break;

    case MSG_GETFIRST:
      SSTRCPY(_szMsg,_nChars,"MSG_GETFIRST");
      break;

    case MSG_GETNEXT:
      SSTRCPY(_szMsg,_nChars,"MSG_GETNEXT");
      break;

    case MSG_SET:
      SSTRCPY(_szMsg,_nChars,"MSG_SET");
      break;

    case MSG_RESET:
      SSTRCPY(_szMsg,_nChars,"MSG_RESET");
      break;

    case MSG_QUERYSUPPORT:
      SSTRCPY(_szMsg,_nChars,"MSG_QUERYSUPPORT");
      break;

    case MSG_GETHELP:
      SSTRCPY(_szMsg,_nChars,"MSG_GETHELP");
      break;

    case MSG_GETLABEL:
      SSTRCPY(_szMsg,_nChars,"MSG_GETLABEL");
      break;

    case MSG_GETLABELENUM:
      SSTRCPY(_szMsg,_nChars,"MSG_GETLABELENUM");
      break;

    case MSG_XFERREADY:
      SSTRCPY(_szMsg,_nChars,"MSG_XFERREADY");
      break;

    case MSG_CLOSEDSREQ:
      SSTRCPY(_szMsg,_nChars,"MSG_CLOSEDSREQ");
      break;

    case MSG_CLOSEDSOK:
      SSTRCPY(_szMsg,_nChars,"MSG_CLOSEDSOK");
      break;

    case MSG_DEVICEEVENT:
      SSTRCPY(_szMsg,_nChars,"MSG_DEVICEEVENT");
      break;
    case MSG_CHECKSTATUS:
      SSTRCPY(_szMsg,_nChars,"MSG_CHECKSTATUS");
      break;
    case MSG_OPENDSM:
      SSTRCPY(_szMsg,_nChars,"MSG_OPENDSM");
      break;

    case MSG_CLOSEDSM:
      SSTRCPY(_szMsg,_nChars,"MSG_CLOSEDSM");
      break;

    case MSG_OPENDS:
      SSTRCPY(_szMsg,_nChars,"MSG_OPENDS");
      break;

    case MSG_CLOSEDS:
      SSTRCPY(_szMsg,_nChars,"MSG_CLOSEDS");
      break;

    case MSG_USERSELECT:
      SSTRCPY(_szMsg,_nChars,"MSG_USERSELECT");
      break;

    case MSG_DISABLEDS:
      SSTRCPY(_szMsg,_nChars,"MSG_DISABLEDS");
      break;

    case MSG_ENABLEDS:
      SSTRCPY(_szMsg,_nChars,"MSG_ENABLEDS");
      break;

    case MSG_ENABLEDSUIONLY:
      SSTRCPY(_szMsg,_nChars,"MSG_ENABLEDSUIONLY");
      break;

    case MSG_PROCESSEVENT:
      SSTRCPY(_szMsg,_nChars,"MSG_PROCESSEVENT");
      break;

    case MSG_ENDXFER:
      SSTRCPY(_szMsg,_nChars,"MSG_ENDXFER");
      break;

    case MSG_CHANGEDIRECTORY:
      SSTRCPY(_szMsg,_nChars,"MSG_CHANGEDIRECTORY");
      break;

    case MSG_CREATEDIRECTORY:
      SSTRCPY(_szMsg,_nChars,"MSG_CREATEDIRECTORY");
      break;

    case MSG_DELETE:
      SSTRCPY(_szMsg,_nChars,"MSG_DELETE");
      break;

    case MSG_FORMATMEDIA:
      SSTRCPY(_szMsg,_nChars,"MSG_FORMATMEDIA");
      break;

    case MSG_GETCLOSE:
      SSTRCPY(_szMsg,_nChars,"MSG_GETCLOSE");
      break;

    case MSG_GETFIRSTFILE:
      SSTRCPY(_szMsg,_nChars,"MSG_GETFIRSTFILE");
      break;

    case MSG_GETINFO:
      SSTRCPY(_szMsg,_nChars,"MSG_GETINFO");
      break;

    case MSG_GETNEXTFILE:
      SSTRCPY(_szMsg,_nChars,"MSG_GETNEXTFILE");
      break;

    case MSG_RENAME:
      SSTRCPY(_szMsg,_nChars,"MSG_RENAME");
      break;

    case MSG_PASSTHRU:
      SSTRCPY(_szMsg,_nChars,"MSG_PASSTHRU");
      break;

    case MSG_REGISTER_CALLBACK:
      SSTRCPY(_szMsg,_nChars,"MSG_REGISTER_CALLBACK");
      break;

    case MSG_RESETALL:
      SSTRCPY(_szMsg,_nChars,"MSG_RESETALL");
      break;
  }
}



/*
* Convert a CAP_ or ICAP_ capability numerical value to a string...
*/
void CTwnDsm::StringFromCap(char     *_szCap,
                      const int       _nChars,
                      const TW_UINT16 _Cap)
{
  switch (_Cap)
  {
    default:
      SSNPRINTF(_szCap,_nChars,_nChars,"CAP_0x%04x",_Cap);
      break;

    case CAP_CUSTOMBASE:
      SSTRCPY(_szCap,_nChars,"CAP_CUSTOMBASE");
      break;

    case CAP_XFERCOUNT:
      SSTRCPY(_szCap,_nChars,"CAP_XFERCOUNT");
      break;

    case ICAP_COMPRESSION:
      SSTRCPY(_szCap,_nChars,"ICAP_COMPRESSION");
      break;

    case ICAP_PIXELTYPE:
      SSTRCPY(_szCap,_nChars,"ICAP_PIXELTYPE");
      break;

    case ICAP_UNITS:
      SSTRCPY(_szCap,_nChars,"ICAP_UNITS");
      break;

    case ICAP_XFERMECH:
      SSTRCPY(_szCap,_nChars,"ICAP_XFERMECH");
      break;

    case CAP_AUTHOR:
      SSTRCPY(_szCap,_nChars,"CAP_AUTHOR");
      break;

    case CAP_CAPTION:
      SSTRCPY(_szCap,_nChars,"CAP_CAPTION");
      break;

    case CAP_FEEDERENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERENABLED");
      break;

    case CAP_FEEDERLOADED:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERLOADED");
      break;

    case CAP_TIMEDATE:
      SSTRCPY(_szCap,_nChars,"CAP_TIMEDATE");
      break;

    case CAP_SUPPORTEDCAPS:
      SSTRCPY(_szCap,_nChars,"CAP_SUPPORTEDCAPS");
      break;

    case CAP_EXTENDEDCAPS:
      SSTRCPY(_szCap,_nChars,"CAP_EXTENDEDCAPS");
      break;

    case CAP_AUTOFEED:
      SSTRCPY(_szCap,_nChars,"CAP_AUTOFEED");
      break;

    case CAP_CLEARPAGE:
      SSTRCPY(_szCap,_nChars,"CAP_CLEARPAGE");
      break;

    case CAP_FEEDPAGE:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDPAGE");
      break;

    case CAP_REWINDPAGE:
      SSTRCPY(_szCap,_nChars,"CAP_REWINDPAGE");
      break;

    case CAP_INDICATORS:
      SSTRCPY(_szCap,_nChars,"CAP_INDICATORS");
      break;

    case CAP_SUPPORTEDCAPSEXT:
      SSTRCPY(_szCap,_nChars,"CAP_SUPPORTEDCAPSEXT");
      break;

    case CAP_PAPERDETECTABLE:
      SSTRCPY(_szCap,_nChars,"CAP_PAPERDETECTABLE");
      break;

    case CAP_UICONTROLLABLE:
      SSTRCPY(_szCap,_nChars,"CAP_UICONTROLLABLE");
      break;

    case CAP_DEVICEONLINE:
      SSTRCPY(_szCap,_nChars,"CAP_DEVICEONLINE");
      break;

    case CAP_AUTOSCAN:
      SSTRCPY(_szCap,_nChars,"CAP_AUTOSCAN");
      break;

    case CAP_THUMBNAILSENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_THUMBNAILSENABLED");
      break;

    case CAP_DUPLEX:
      SSTRCPY(_szCap,_nChars,"CAP_DUPLEX");
      break;

    case CAP_DUPLEXENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_DUPLEXENABLED");
      break;

    case CAP_ENABLEDSUIONLY:
      SSTRCPY(_szCap,_nChars,"CAP_ENABLEDSUIONLY");
      break;

    case CAP_CUSTOMDSDATA:
      SSTRCPY(_szCap,_nChars,"CAP_CUSTOMDSDATA");
      break;

    case CAP_ENDORSER:
      SSTRCPY(_szCap,_nChars,"CAP_ENDORSER");
      break;

    case CAP_JOBCONTROL:
      SSTRCPY(_szCap,_nChars,"CAP_JOBCONTROL");
      break;

    case CAP_ALARMS:
      SSTRCPY(_szCap,_nChars,"CAP_ALARMS");
      break;

    case CAP_ALARMVOLUME:
      SSTRCPY(_szCap,_nChars,"CAP_ALARMVOLUME");
      break;

    case CAP_AUTOMATICCAPTURE:
      SSTRCPY(_szCap,_nChars,"CAP_AUTOMATICCAPTURE");
      break;

    case CAP_TIMEBEFOREFIRSTCAPTURE:
      SSTRCPY(_szCap,_nChars,"CAP_TIMEBEFOREFIRSTCAPTURE");
      break;

    case CAP_TIMEBETWEENCAPTURES:
      SSTRCPY(_szCap,_nChars,"CAP_TIMEBETWEENCAPTURES");
      break;

    case CAP_CLEARBUFFERS:
      SSTRCPY(_szCap,_nChars,"CAP_CLEARBUFFERS");
      break;

    case CAP_MAXBATCHBUFFERS:
      SSTRCPY(_szCap,_nChars,"CAP_MAXBATCHBUFFERS");
      break;

    case CAP_DEVICETIMEDATE:
      SSTRCPY(_szCap,_nChars,"CAP_DEVICETIMEDATE");
      break;

    case CAP_POWERSUPPLY:
      SSTRCPY(_szCap,_nChars,"CAP_POWERSUPPLY");
      break;

    case CAP_CAMERAPREVIEWUI:
      SSTRCPY(_szCap,_nChars,"CAP_CAMERAPREVIEWUI");
      break;

    case CAP_DEVICEEVENT:
      SSTRCPY(_szCap,_nChars,"CAP_DEVICEEVENT");
      break;

    case CAP_SERIALNUMBER:
      SSTRCPY(_szCap,_nChars,"CAP_SERIALNUMBER");
      break;

    case CAP_PRINTER:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTER");
      break;

    case CAP_PRINTERENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTERENABLED");
      break;

    case CAP_PRINTERINDEX:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTERINDEX");
      break;

    case CAP_PRINTERMODE:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTERMODE");
      break;

    case CAP_PRINTERSTRING:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTERSTRING");
      break;

    case CAP_PRINTERSUFFIX:
      SSTRCPY(_szCap,_nChars,"CAP_PRINTERSUFFIX");
      break;

    case CAP_LANGUAGE:
      SSTRCPY(_szCap,_nChars,"CAP_LANGUAGE");
      break;

    case CAP_FEEDERALIGNMENT:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERALIGNMENT");
      break;

    case CAP_FEEDERORDER:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERORDER");
      break;

    case CAP_REACQUIREALLOWED:
      SSTRCPY(_szCap,_nChars,"CAP_REACQUIREALLOWED");
      break;

    case CAP_BATTERYMINUTES:
      SSTRCPY(_szCap,_nChars,"CAP_BATTERYMINUTES");
      break;

    case CAP_BATTERYPERCENTAGE:
      SSTRCPY(_szCap,_nChars,"CAP_BATTERYPERCENTAGE");
      break;

    case CAP_CAMERASIDE:
      SSTRCPY(_szCap,_nChars,"CAP_CAMERASIDE");
      break;

    case CAP_SEGMENTED:
      SSTRCPY(_szCap,_nChars,"CAP_SEGMENTED");
      break;

    case CAP_CAMERAENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_CAMERAENABLED");
      break;

    case CAP_CAMERAORDER:
      SSTRCPY(_szCap,_nChars,"CAP_CAMERAORDER");
      break;

    case CAP_MICRENABLED:
      SSTRCPY(_szCap,_nChars,"CAP_MICRENABLED");
      break;

    case CAP_FEEDERPREP:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERPREP");
      break;

    case CAP_FEEDERPOCKET:
      SSTRCPY(_szCap,_nChars,"CAP_FEEDERPOCKET");
      break;

    case CAP_AUTOMATICSENSEMEDIUM:
      SSTRCPY(_szCap,_nChars,"CAP_AUTOMATICSENSEMEDIUM");
      break;

    case CAP_CUSTOMINTERFACEGUID:
      SSTRCPY(_szCap,_nChars,"CAP_CUSTOMINTERFACEGUID");
      break;

    case ICAP_AUTOBRIGHT:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOBRIGHT");
      break;

    case ICAP_BRIGHTNESS:
      SSTRCPY(_szCap,_nChars,"ICAP_BRIGHTNESS");
      break;

    case ICAP_CONTRAST:
      SSTRCPY(_szCap,_nChars,"ICAP_CONTRAST");
      break;

    case ICAP_CUSTHALFTONE:
      SSTRCPY(_szCap,_nChars,"ICAP_CUSTHALFTONE");
      break;

    case ICAP_EXPOSURETIME:
      SSTRCPY(_szCap,_nChars,"ICAP_EXPOSURETIME");
      break;

    case ICAP_FILTER:
      SSTRCPY(_szCap,_nChars,"ICAP_FILTER");
      break;

    case ICAP_FLASHUSED:
      SSTRCPY(_szCap,_nChars,"ICAP_FLASHUSED");
      break;

    case ICAP_GAMMA:
      SSTRCPY(_szCap,_nChars,"ICAP_GAMMA");
      break;

    case ICAP_HALFTONES:
      SSTRCPY(_szCap,_nChars,"ICAP_HALFTONES");
      break;

    case ICAP_HIGHLIGHT:
      SSTRCPY(_szCap,_nChars,"ICAP_HIGHLIGHT");
      break;

    case ICAP_IMAGEFILEFORMAT:
      SSTRCPY(_szCap,_nChars,"ICAP_IMAGEFILEFORMAT");
      break;

    case ICAP_LAMPSTATE:
      SSTRCPY(_szCap,_nChars,"ICAP_LAMPSTATE");
      break;

    case ICAP_LIGHTSOURCE:
      SSTRCPY(_szCap,_nChars,"ICAP_LIGHTSOURCE");
      break;

    case ICAP_ORIENTATION:
      SSTRCPY(_szCap,_nChars,"ICAP_ORIENTATION");
      break;

    case ICAP_PHYSICALWIDTH:
      SSTRCPY(_szCap,_nChars,"ICAP_PHYSICALWIDTH");
      break;

    case ICAP_PHYSICALHEIGHT:
      SSTRCPY(_szCap,_nChars,"ICAP_PHYSICALHEIGHT");
      break;

    case ICAP_SHADOW:
      SSTRCPY(_szCap,_nChars,"ICAP_SHADOW");
      break;

    case ICAP_FRAMES:
      SSTRCPY(_szCap,_nChars,"ICAP_FRAMES");
      break;

    case ICAP_XNATIVERESOLUTION:
      SSTRCPY(_szCap,_nChars,"ICAP_XNATIVERESOLUTION");
      break;

    case ICAP_YNATIVERESOLUTION:
      SSTRCPY(_szCap,_nChars,"ICAP_YNATIVERESOLUTION");
      break;

    case ICAP_XRESOLUTION:
      SSTRCPY(_szCap,_nChars,"ICAP_XRESOLUTION");
      break;

    case ICAP_YRESOLUTION:
      SSTRCPY(_szCap,_nChars,"ICAP_YRESOLUTION");
      break;

    case ICAP_MAXFRAMES:
      SSTRCPY(_szCap,_nChars,"ICAP_MAXFRAMES");
      break;

    case ICAP_TILES:
      SSTRCPY(_szCap,_nChars,"ICAP_TILES");
      break;

    case ICAP_BITORDER:
      SSTRCPY(_szCap,_nChars,"ICAP_BITORDER");
      break;

    case ICAP_CCITTKFACTOR:
      SSTRCPY(_szCap,_nChars,"ICAP_CCITTKFACTOR");
      break;

    case ICAP_LIGHTPATH:
      SSTRCPY(_szCap,_nChars,"ICAP_LIGHTPATH");
      break;

    case ICAP_PIXELFLAVOR:
      SSTRCPY(_szCap,_nChars,"ICAP_PIXELFLAVOR");
      break;

    case ICAP_PLANARCHUNKY:
      SSTRCPY(_szCap,_nChars,"ICAP_PLANARCHUNKY");
      break;

    case ICAP_ROTATION:
      SSTRCPY(_szCap,_nChars,"ICAP_ROTATION");
      break;

    case ICAP_SUPPORTEDSIZES:
      SSTRCPY(_szCap,_nChars,"ICAP_SUPPORTEDSIZES");
      break;

    case ICAP_THRESHOLD:
      SSTRCPY(_szCap,_nChars,"ICAP_THRESHOLD");
      break;

    case ICAP_XSCALING:
      SSTRCPY(_szCap,_nChars,"ICAP_XSCALING");
      break;

    case ICAP_YSCALING:
      SSTRCPY(_szCap,_nChars,"ICAP_YSCALING");
      break;

    case ICAP_BITORDERCODES:
      SSTRCPY(_szCap,_nChars,"ICAP_BITORDERCODES");
      break;

    case ICAP_PIXELFLAVORCODES:
      SSTRCPY(_szCap,_nChars,"ICAP_PIXELFLAVORCODES");
      break;

    case ICAP_JPEGPIXELTYPE:
      SSTRCPY(_szCap,_nChars,"ICAP_JPEGPIXELTYPE");
      break;

    case ICAP_TIMEFILL:
      SSTRCPY(_szCap,_nChars,"ICAP_TIMEFILL");
      break;

    case ICAP_BITDEPTH:
      SSTRCPY(_szCap,_nChars,"ICAP_BITDEPTH");
      break;

    case ICAP_BITDEPTHREDUCTION:
      SSTRCPY(_szCap,_nChars,"ICAP_BITDEPTHREDUCTION");
      break;

    case ICAP_UNDEFINEDIMAGESIZE:
      SSTRCPY(_szCap,_nChars,"ICAP_UNDEFINEDIMAGESIZE");
      break;

    case ICAP_IMAGEDATASET:
      SSTRCPY(_szCap,_nChars,"ICAP_IMAGEDATASET");
      break;

    case ICAP_EXTIMAGEINFO:
      SSTRCPY(_szCap,_nChars,"ICAP_EXTIMAGEINFO");
      break;

    case ICAP_MINIMUMHEIGHT:
      SSTRCPY(_szCap,_nChars,"ICAP_MINIMUMHEIGHT");
      break;

    case ICAP_MINIMUMWIDTH:
      SSTRCPY(_szCap,_nChars,"ICAP_MINIMUMWIDTH");
      break;

    case ICAP_AUTODISCARDBLANKPAGES:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTODISCARDBLANKPAGES");
      break;

    case ICAP_FLIPROTATION:
      SSTRCPY(_szCap,_nChars,"ICAP_FLIPROTATION");
      break;

    case ICAP_BARCODEDETECTIONENABLED:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODEDETECTIONENABLED");
      break;

    case ICAP_SUPPORTEDBARCODETYPES:
      SSTRCPY(_szCap,_nChars,"ICAP_SUPPORTEDBARCODETYPES");
      break;

    case ICAP_BARCODEMAXSEARCHPRIORITIES:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODEMAXSEARCHPRIORITIES");
      break;

    case ICAP_BARCODESEARCHPRIORITIES:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODESEARCHPRIORITIES");
      break;

    case ICAP_BARCODESEARCHMODE:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODESEARCHMODE");
      break;

    case ICAP_BARCODEMAXRETRIES:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODEMAXRETRIES");
      break;

    case ICAP_BARCODETIMEOUT:
      SSTRCPY(_szCap,_nChars,"ICAP_BARCODETIMEOUT");
      break;

    case ICAP_ZOOMFACTOR:
      SSTRCPY(_szCap,_nChars,"ICAP_ZOOMFACTOR");
      break;

    case ICAP_PATCHCODEDETECTIONENABLED:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODEDETECTIONENABLED");
      break;

    case ICAP_SUPPORTEDPATCHCODETYPES:
      SSTRCPY(_szCap,_nChars,"ICAP_SUPPORTEDPATCHCODETYPES");
      break;

    case ICAP_PATCHCODEMAXSEARCHPRIORITIES:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODEMAXSEARCHPRIORITIES");
      break;

    case ICAP_PATCHCODESEARCHPRIORITIES:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODESEARCHPRIORITIES");
      break;

    case ICAP_PATCHCODESEARCHMODE:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODESEARCHMODE");
      break;

    case ICAP_PATCHCODEMAXRETRIES:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODEMAXRETRIES");
      break;

    case ICAP_PATCHCODETIMEOUT:
      SSTRCPY(_szCap,_nChars,"ICAP_PATCHCODETIMEOUT");
      break;

    case ICAP_FLASHUSED2:
      SSTRCPY(_szCap,_nChars,"ICAP_FLASHUSED2");
      break;

    case ICAP_IMAGEFILTER:
      SSTRCPY(_szCap,_nChars,"ICAP_IMAGEFILTER");
      break;

    case ICAP_NOISEFILTER:
      SSTRCPY(_szCap,_nChars,"ICAP_NOISEFILTER");
      break;

    case ICAP_OVERSCAN:
      SSTRCPY(_szCap,_nChars,"ICAP_OVERSCAN");
      break;

    case ICAP_AUTOMATICBORDERDETECTION:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICBORDERDETECTION");
      break;

    case ICAP_AUTOMATICDESKEW:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICDESKEW");
      break;

    case ICAP_AUTOMATICROTATE:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICROTATE");
      break;

    case ICAP_JPEGQUALITY:
      SSTRCPY(_szCap,_nChars,"ICAP_JPEGQUALITY");
      break;

    case ICAP_FEEDERTYPE:
      SSTRCPY(_szCap,_nChars,"ICAP_FEEDERTYPE");
      break;

    case ICAP_ICCPROFILE:
      SSTRCPY(_szCap,_nChars,"ICAP_ICCPROFILE");
      break;

    case ICAP_AUTOSIZE:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOSIZE");
      break;

    case ICAP_AUTOMATICCROPUSESFRAME:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICCROPUSESFRAME");
      break;

    case ICAP_AUTOMATICLENGTHDETECTION:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICLENGTHDETECTION");
      break;

    case ICAP_AUTOMATICCOLORENABLED:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICCOLORENABLED");
      break;

    case ICAP_AUTOMATICCOLORNONCOLORPIXELTYPE:
      SSTRCPY(_szCap,_nChars,"ICAP_AUTOMATICCOLORNONCOLORPIXELTYPE");
      break;

    case ICAP_COLORMANAGEMENTENABLED:
      SSTRCPY(_szCap,_nChars,"ICAP_COLORMANAGEMENTENABLED");
      break;

    case ICAP_IMAGEMERGE:
      SSTRCPY(_szCap,_nChars,"ICAP_IMAGEMERGE");
      break;

    case ICAP_IMAGEMERGEHEIGHTTHRESHOLD:
      SSTRCPY(_szCap,_nChars,"ICAP_IMAGEMERGEHEIGHTTHRESHOLD");
      break;

    case ICAP_SUPPORTEDEXTIMAGEINFO:
      SSTRCPY(_szCap,_nChars,"ICAP_SUPPORTEDEXTIMAGEINFO");
      break;

    case ACAP_AUDIOFILEFORMAT:
      SSTRCPY(_szCap,_nChars,"ACAP_AUDIOFILEFORMAT");
      break;

    case ACAP_XFERMECH:
      SSTRCPY(_szCap,_nChars,"ACAP_XFERMECH");
      break;
  }
}



/*
* Convert a TWRC_ return code numerical value to a string...
*/
void CTwnDsm::StringFromRC(char     *_szRc,
                     const int       _nChars,
                     const TW_UINT16 _rc)
{
  switch (_rc)
  {
    default:    
      SSNPRINTF(_szRc,_nChars,_nChars,"TWRC_0x%04x",_rc);
      break;

    case TWRC_SUCCESS:
      SSTRCPY(_szRc,_nChars,"TWRC_SUCCESS");
      break;

    case TWRC_FAILURE:
      SSTRCPY(_szRc,_nChars,"TWRC_FAILURE");
      break;

    case TWRC_CHECKSTATUS:
      SSTRCPY(_szRc,_nChars,"TWRC_CHECKSTATUS");
      break;

    case TWRC_CANCEL:
      SSTRCPY(_szRc,_nChars,"TWRC_CANCEL");
      break;

    case TWRC_DSEVENT:
      SSTRCPY(_szRc,_nChars,"TWRC_DSEVENT");
      break;

    case TWRC_NOTDSEVENT:
      SSTRCPY(_szRc,_nChars,"TWRC_NOTDSEVENT");
      break;

    case TWRC_XFERDONE:
      SSTRCPY(_szRc,_nChars,"TWRC_XFERDONE");
      break;

    case TWRC_ENDOFLIST:
      SSTRCPY(_szRc,_nChars,"TWRC_ENDOFLIST");
      break;

    case TWRC_INFONOTSUPPORTED:
      SSTRCPY(_szRc,_nChars,"TWRC_INFONOTSUPPORTED");
      break;

    case TWRC_DATANOTAVAILABLE:
      SSTRCPY(_szRc,_nChars,"TWRC_DATANOTAVAILABLE");
      break;
  }
}

/*
* Convert a Container type to a string...
*/
void CTwnDsm::StringFromConType(char     *_szData,
                          const int       _nChars,
                          const TW_UINT16 _ConType)
{
  switch (_ConType)
  {
    default:    
      SSNPRINTF(_szData,_nChars,_nChars," TWON_0x%04x",_ConType);
      break;

    case TWON_DONTCARE16:
      SSTRCPY(_szData,_nChars," TWON_DONTCARE16");
      break;

    case TWON_ARRAY:
      SSTRCPY(_szData,_nChars," TWON_ARRAY");
      break;

    case TWON_ENUMERATION:
      SSTRCPY(_szData,_nChars," TWON_ENUMERATION");
      break;

    case TWON_ONEVALUE:
      SSTRCPY(_szData,_nChars," TWON_ONEVALUE ");
      break;

    case TWON_RANGE:
      SSTRCPY(_szData,_nChars," TWON_RANGE");
      break;
  }
}

/*
* Convert a Condition Code to a string...
*/
void CTwnDsm::StringFromConditionCode(char     *_szData,
                                const int       _nChars,
                                const TW_UINT16 _ConCode)
{
  switch (_ConCode)
  {
    default:    
      SSNPRINTF(_szData,_nChars,_nChars," TWCC_0x%04x",_ConCode);
      break;

    case TWCC_SUCCESS:
      SSTRCPY(_szData,_nChars," TWCC_SUCCESS");
      break;

    case TWCC_BUMMER:
      SSTRCPY(_szData,_nChars," TWCC_BUMMER");
      break;

    case TWCC_LOWMEMORY:
      SSTRCPY(_szData,_nChars," TWCC_LOWMEMORY");
      break;

    case TWCC_NODS:
      SSTRCPY(_szData,_nChars," TWCC_NODS");
      break;

    case TWCC_MAXCONNECTIONS:
      SSTRCPY(_szData,_nChars," TWCC_MAXCONNECTIONS");
      break;

    case TWCC_OPERATIONERROR:
      SSTRCPY(_szData,_nChars," TWCC_OPERATIONERROR");
      break;

    case TWCC_BADCAP:
      SSTRCPY(_szData,_nChars," TWCC_BADCAP");
      break;

    case TWCC_BADPROTOCOL:
      SSTRCPY(_szData,_nChars," TWCC_BADPROTOCOL");
      break;

    case TWCC_BADVALUE:
      SSTRCPY(_szData,_nChars," TWCC_BADVALUE");
      break;

    case TWCC_SEQERROR:
      SSTRCPY(_szData,_nChars," TWCC_SEQERROR");
      break;

    case TWCC_BADDEST:
      SSTRCPY(_szData,_nChars," TWCC_BADDEST");
      break;

    case TWCC_CAPUNSUPPORTED:
      SSTRCPY(_szData,_nChars," TWCC_CAPUNSUPPORTED");
      break;

    case TWCC_CAPBADOPERATION:
      SSTRCPY(_szData,_nChars," TWCC_CAPBADOPERATION");
      break;

    case TWCC_CAPSEQERROR:
      SSTRCPY(_szData,_nChars," TWCC_CAPSEQERROR");
      break;

    case TWCC_DENIED:
      SSTRCPY(_szData,_nChars," TWCC_DENIED");
      break;

    case TWCC_FILEEXISTS:
      SSTRCPY(_szData,_nChars," TWCC_FILEEXISTS");
      break;

    case TWCC_FILENOTFOUND:
      SSTRCPY(_szData,_nChars," TWCC_FILENOTFOUND");
      break;

    case TWCC_NOTEMPTY:
      SSTRCPY(_szData,_nChars," TWCC_NOTEMPTY");
      break;

    case TWCC_PAPERJAM:
      SSTRCPY(_szData,_nChars," TWCC_PAPERJAM");
      break;

    case TWCC_PAPERDOUBLEFEED:
      SSTRCPY(_szData,_nChars," TWCC_PAPERDOUBLEFEED");
      break;

    case TWCC_FILEWRITEERROR:
      SSTRCPY(_szData,_nChars," TWCC_FILEWRITEERROR");
      break;

    case TWCC_CHECKDEVICEONLINE:
      SSTRCPY(_szData,_nChars," TWCC_CHECKDEVICEONLINE");
      break;

    case TWCC_INTERLOCK:
      SSTRCPY(_szData,_nChars," TWCC_INTERLOCK");
      break;

    case TWCC_DAMAGEDCORNER:
      SSTRCPY(_szData,_nChars," TWCC_DAMAGEDCORNER");
      break;

    case TWCC_FOCUSERROR:
      SSTRCPY(_szData,_nChars," TWCC_FOCUSERROR");
      break;

    case TWCC_DOCTOOLIGHT:
      SSTRCPY(_szData,_nChars," TWCC_DOCTOOLIGHT");
      break;

    case TWCC_DOCTOODARK:
      SSTRCPY(_szData,_nChars," TWCC_DOCTOODARK");
      break;

    case TWCC_NOMEDIA:
      SSTRCPY(_szData,_nChars," TWCC_NOMEDIA");
      break;
    }
}


/*
* Allocate memory on behalf of the caller (could be the application
* or the driver)...
*/
TW_HANDLE PASCAL DSM_MemAllocate (TW_UINT32 _bytes)
{
  TW_HANDLE handle;

  // Validate...
  if (0 == _bytes)
  {
    kLOG((kLOGERR,"_bytes is zero..."));
    return (TW_HANDLE)NULL;
  }

  // Windows...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    handle = (TW_HANDLE)::GlobalAlloc(GPTR,_bytes);
  if (0 == handle)
  {
      kLOG((kLOGERR,"DSM_MemAllocate failed to allocate %ld bytes...",_bytes));
      return (TW_HANDLE)NULL;
  }
  return handle;
  
  // MacOS
  #elif (TWNDSM_OS == TWNDSM_OS_MACOSX)
    handle = (TW_HANDLE)NewHandleClear(_bytes);
  if (0 == handle)
  {
      kLOG((kLOGERR,"DSM_MemAllocate failed to allocate %ld bytes...",_bytes));
      return (TW_HANDLE)NULL;
  }
  return handle;

  // Linux
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    handle = (TW_HANDLE)calloc(_bytes,1);
  if (0 == handle)
  {
      kLOG((kLOGERR,"DSM_MemAllocate failed to allocate %ld bytes...",_bytes));
      return (TW_HANDLE)NULL;
  }
  return handle;

  // Oops...
  #else
    #error Sorry, we do not recognize this system...
  #endif
}



/*
* Free memory on behalf of the caller (could be the application
* or the driver)...
*/
void PASCAL DSM_MemFree (TW_HANDLE _handle)
{
  // Validate...
  if (0 == _handle)
  {
    kLOG((kLOGERR,"ignoring attempt to free null handle..."));
    return;
  }

  // Windows...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    ::GlobalFree(_handle);
    
  // MacOS
  #elif (TWNDSM_OS == TWNDSM_OS_MACOSX)
    DisposeHandle((Handle)_handle);

  // Linux...
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    free(_handle);

  // Oops...
  #else
    #error Sorry, we do not recognize this system...
  #endif
}



/*
* Lock memory on behalf of the caller...
*/
TW_MEMREF PASCAL DSM_MemLock (TW_HANDLE _handle)
{
  // Validate...
  if (0 == _handle)
  {
    kLOG((kLOGERR,"attempting to lock null handle..."));
    return (TW_MEMREF)NULL;
  }

  // Windows...technically we shouldn't have to do the
  // lock, since we allocated with GPTR, but I'm nervous
  // that we might get a GHND sent to us.  And since
  // this is a no-op for a GPTR, what they hey...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    return (TW_MEMREF)::GlobalLock(_handle);

  // MacOS
  #elif (TWNDSM_OS == TWNDSM_OS_MACOSX)
    return _handle ? *_handle : 0;

  // Linux...
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    return (TW_MEMREF)_handle;

  // Oops...
  #else
    #error Sorry, we do not recognize this system...
  #endif
}



/*
* Unlock memory on behalf of the caller, if needed.  This function
* is a placeholder at the moment.  It'll get more interesting if it
* has to do locking on Mac OS/X...
*/
void PASCAL DSM_MemUnlock (TW_HANDLE _handle)
{
  // Validate...
  if (0 == _handle)
  {
    kLOG((kLOGERR,"attempting to unlock null handle..."));
    return;
  }

  // Windows...
  #if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
    ::GlobalUnlock(_handle);

  // Linux...
  #elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)

  // Oops...
  #else
    #error Sorry, we do not recognize this system...
  #endif
}

/*
* This function wraps the function loading calls. Linux has a 
* special way to check dlsym failures.
*/
void* DSM_LoadFunction(void* _pHandle, const char* _pszSymbol)
{
  void* pRet = 0;
#if (TWNDSM_OS == TWNDSM_OS_MACOSX)
  pRet = CFBundleGetFunctionPointerForName((CFBundleRef)_pHandle,
					   CFStringCreateWithCStringNoCopy(0, _pszSymbol, kCFStringEncodingUTF8, 0));
#else

  #if (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
    dlerror();    /* Clear any existing error */
  #endif

  // Try to get the entry point...
  pRet = LOADFUNCTION(_pHandle, _pszSymbol);

#if (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
  char* psz_error = 0;

  if((psz_error = dlerror()) != NULL)
  {
    kLOG((kLOGERR,"dlsym error: %s",psz_error));
    pRet = 0;
  }
#endif
#endif
  return pRet;
}
