/******************************************************************************

  wsti.cpp
  WDM Still Imaging interface

  Copyright (C) Microsoft Corporation, 1997 - 1999
  All rights reserved

Notes:
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  PURPOSE.

******************************************************************************/

#include "stillvue.h"

//
// defined in Stivar.h
//
extern  int             nAuto, nError, nICanScan, nScanCount;
extern  PSTR            pszString,pszMsg,pszCommand,pszComment;
extern  HINSTANCE               hThisInstance;


//
// STI.H - STI_DEVICE_MJ_TYPE
//
STRINGTABLE StStiDeviceType[] =
{
    0, "StiDeviceTypeDefault",0,
    1, "StiDeviceTypeScanner",0,
    2, "StiDeviceTypeDigitalCamera",0,
    0, "Unknown device type",-1
};

//
// STIERR.H - errors
//
STRINGTABLE StStiError[] =
{
    STI_OK,                     "STI_OK",0,
    STI_NOTCONNECTED,           "STI_NOTCONNECTED",0,
    STI_CHANGENOEFFECT,         "STI_CHANGENOEFFECT",0,
    STIERR_OLD_VERSION,         "STIERR_OLD_VERSION",0,
    STIERR_BETA_VERSION,        "STIERR_BETA_VERSION",0,
    STIERR_BADDRIVER,           "STIERR_BADDRIVER",0,
    STIERR_DEVICENOTREG,        "STIERR_DEVICENOTREG",0,
    STIERR_OBJECTNOTFOUND,      "STIERR_OBJECTNOTFOUND",0,
    STIERR_INVALID_PARAM,       "STIERR_INVALID_PARAM",0,
    STIERR_NOINTERFACE,         "STIERR_NOINTERFACE",0,
    STIERR_GENERIC,             "STIERR_GENERIC", 0,
    STIERR_OUTOFMEMORY,         "STIERR_OUTOFMEMORY", 0,
    STIERR_UNSUPPORTED,         "STIERR_UNSUPPORTED", 0,
    STIERR_NOT_INITIALIZED,     "STIERR_NOT_INITIALIZED", 0,
    STIERR_ALREADY_INITIALIZED, "STIERR_ALREADY_INITIALIZED", 0,
    STIERR_DEVICE_LOCKED,       "STIERR_DEVICE_LOCKED", 0,
    STIERR_READONLY,            "STIERR_READONLY", 0,
    STIERR_NOTINITIALIZED,      "STIERR_NOTINITIALIZED", 0,
    STIERR_NEEDS_LOCK,          "STIERR_NEEDS_LOCK", 0,
    STIERR_SHARING_VIOLATION,   "STIERR_SHARING_VIOLATION", 0,
    STIERR_HANDLEEXISTS,        "STIERR_HANDLEEXISTS", 0,
    STIERR_INVALID_DEVICE_NAME, "STIERR_INVALID_DEVICE_NAME", 0,
    STIERR_INVALID_HW_TYPE,     "STIERR_INVALID_HW_TYPE", 0,
    STIERR_INVALID_HW_TYPE,     "STIERR_INVALID_HW_TYPE", 0,
    STIERR_NOEVENTS,            "STIERR_NOEVENTS", 0,
    0, "Unknown STI error",-1
};

//
// WINNT.H -  Predefined Value Types.
//
STRINGTABLE StRegValType[] =
{
    0, "REG_NONE",0,
    1, "REG_SZ",0,
    3, "REG_BINARY",0,
    4, "REG_DWORD",0,
    0, "Unknown reg type",-1
};

//
// global still image
//
PSTI                    pSti = NULL;        // handle to STI subsystem
PVOID                   pStiInfo = NULL;    // STI device info buffer
PSTI_DEVICE_INFORMATION pStiInfoPtr = NULL; // pointer to device in pStiBuffer
int                     nStiNumber = 0;     // 0 based index into pStiInfo
DWORD                   dwStiTotal = 0;     // total number of STI devices found
PSTIDEVICE              pStiDevice = NULL;  // STI device being used


/*****************************************************************************

        define ACQUIRE to load device specific command handler for stub
        functions defined in STIDDK.CPP

*****************************************************************************/
#ifdef ACQUIRE
//
// device specific image acquire code
//
    #include "acquire.cpp"

#else
//
// only exercise STI services
//
    #include "stisvc.cpp"

#endif


/*****************************************************************************
    HRESULT StiCreateInstance( )
        Opens STI subsystem

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiCreateInstance( )
{
    HRESULT hres = STI_OK;


    //
    // close any open devices before enumeration
    //
    StiClose();

    //
    // The StiCreateInstance interface locates the primary still image interface.
    // Use this call to optain the pointer to the IStillImage interface.
    //
    hres = StiCreateInstance(
                            GetModuleHandle(NULL),  // instance handle of this application
                            STI_VERSION,            // STI version
                            &pSti,                  // pointer to IStillImage interface
                            NULL                    // pointer to controlling unknown of OLE aggregation
                            );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"StiCreateInstance");
        nError++;
        return (STIERR_GENERIC);
    }
    DisplayOutput("The Sti subsystem is opened");

    //
    // Enable the menu items for IStillImage interface calls available
    //
    EnableMenuItem(hMenu, IDM_IMAGE_RELEASE,         MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_DEVLIST,           MF_ENABLED);
    EnableMenuItem(hMenu, IDM_CREATE_DEV,            MF_ENABLED);
    EnableMenuItem(hMenu, IDM_REGISTER_LAUNCH,       MF_ENABLED);
    EnableMenuItem(hMenu, IDM_UNREGISTER_LAUNCH,     MF_ENABLED);
    EnableMenuItem(hMenu, IDM_WRITE_ERRORLOG,        MF_ENABLED);

    EnableMenuItem(hMenu, IDM_CREATE_INSTANCE,       MF_GRAYED);

    return (hres);
}


/*****************************************************************************
    HRESULT StiClose( )
        Close any open devices and Sti subsystem

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiClose( )
{
    HRESULT hres = STI_OK;


    hres = StiDeviceRelease();
    hres = StiImageRelease();

    return (hres);
}


/*****************************************************************************
    HRESULT StiDeviceRelease( )
        Close the Sti subsystem

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiDeviceRelease( )
{
    HRESULT hres = STI_OK;


    //
    // The STI_DEVICE_INFORMATION array returned by GetDeviceList needs to
    // be freed with LocalFree(). Also, resetting internal Sti device counter.
    //
    if ( pStiInfo )
        LocalFree(pStiInfo);
    pStiInfo = pStiInfoPtr = NULL;
    dwStiTotal = 0;

    //
    // close device if any are open
    //
    if ( pStiDevice ) {
        //
        // Close an open device.
        //
        hres = pStiDevice->Release();

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"Release (Device)");
            nError++;
        } else
            DisplayOutput("Device Released");

        //
        // clear the Sti device pointer
        //
        pStiDevice = NULL;

        //
        // disable IStiDevice menu items
        //
        EnableMenuItem(hMenu, IDM_GET_DEVINFO,        MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_DEVVAL,         MF_GRAYED);
        EnableMenuItem(hMenu, IDM_SET_DEVVAL,         MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_LAUNCHINFO,     MF_GRAYED);
        EnableMenuItem(hMenu, IDM_ENABLE_HWNOTIF,     MF_GRAYED);
        CheckMenuItem(hMenu,  IDM_ENABLE_HWNOTIF,     MF_UNCHECKED);
        EnableMenuItem(hMenu, IDM_GET_HWNOTIF,        MF_GRAYED);
        EnableMenuItem(hMenu, IDM_REFRESH_DEVBUS,     MF_GRAYED);
        EnableMenuItem(hMenu, IDM_LAUNCH_APP_FOR_DEV, MF_GRAYED);
        EnableMenuItem(hMenu, IDM_SETUP_DEVPARAMS,    MF_GRAYED);

        EnableMenuItem(hMenu, IDM_GET_STATUS,         MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_CAPS,           MF_GRAYED);
        EnableMenuItem(hMenu, IDM_DEVICERESET,        MF_GRAYED);
        EnableMenuItem(hMenu, IDM_DIAGNOSTIC,         MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_LASTERRINFO,    MF_GRAYED);
        EnableMenuItem(hMenu, IDM_LOCKDEV,            MF_GRAYED);
        EnableMenuItem(hMenu, IDM_UNLOCKDEV,          MF_GRAYED);
        EnableMenuItem(hMenu, IDM_SUBSCRIBE,          MF_GRAYED);
        EnableMenuItem(hMenu, IDM_UNSUBSCRIBE,        MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_LASTNOTIFDATA,  MF_GRAYED);
        EnableMenuItem(hMenu, IDM_DEVICE_RELEASE,     MF_GRAYED);

        EnableMenuItem(hMenu, IDM_IMAGE_RELEASE,      MF_ENABLED);
    }

    return (hres);
}


/*****************************************************************************
    HRESULT StiImageRelease( )
        Close the Sti subsystem

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiImageRelease( )
{
    HRESULT hres = STI_OK;


    //
    // if Sti subsystem is open, close it
    //
    if ( pSti ) {
        //
        // Close the Still Imaging subsystem.
        //
        hres = pSti->Release();

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"Release (Image)");
            nError++;
        } else {
            DisplayOutput("Imaging subsystem Released");
            DisplayOutput("");
        }

        //
        // clear the Sti subsystem pointer
        //
        pSti = NULL;

        //
        // Disable the menu items for IStillImage interface calls
        //
        EnableMenuItem(hMenu, IDM_IMAGE_RELEASE,         MF_GRAYED);
        EnableMenuItem(hMenu, IDM_GET_DEVLIST,           MF_GRAYED);
        EnableMenuItem(hMenu, IDM_CREATE_DEV,            MF_GRAYED);
        EnableMenuItem(hMenu, IDM_REGISTER_LAUNCH,       MF_GRAYED);
        EnableMenuItem(hMenu, IDM_UNREGISTER_LAUNCH,     MF_GRAYED);
        EnableMenuItem(hMenu, IDM_WRITE_ERRORLOG,        MF_GRAYED);

        EnableMenuItem(hMenu, IDM_CREATE_INSTANCE,       MF_ENABLED);
    }

    return (hres);
}


/*****************************************************************************
    HRESULT StiEnum( )
        Opens STI subsystem and enumerates any still image devices found

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiEnum( )
{
    HRESULT hres = STI_OK;
    DWORD   dwCounter;

    PCSTR   pszStringTablePtr = NULL;

    //
    // check that STI subsystem is loaded
    //
    if ( ! pSti )
        return (STIERR_GENERIC);

    //
    // Enumerate devices
    //
    dwStiTotal = 0;
    pStiInfo = NULL;

    //
    // The GetDeviceList interface is used to get a list of the installed still
    // image devices. Use this call to obtain a STI_DEVICE_INFORMATION array
    // filled with info on all currently installed STI devices.
    // * NOTE: the STI subsystem allocates memory for the STI device information
    // buffer, but the caller needs to free this memory with LocalFree().
    //
    hres = pSti->GetDeviceList(
                              NULL,           // Type (reserved, use NULL)
                              NULL,           // Flags (reserved, use NULL)
                              &dwStiTotal,    // address of variable to return number of devices found
                              &pStiInfo       // STI device info buffer
                              );

    if ( ! SUCCEEDED(hres) || ! pStiInfo ) {
        StiDisplayError(hres,"GetDeviceList");
        nError++;
        StiClose();
        return (STIERR_GENERIC);
    }

    //
    // Display Sti info on each device found
    //
    for ( dwCounter = 0,pStiInfoPtr = (PSTI_DEVICE_INFORMATION) pStiInfo;
        dwCounter < dwStiTotal;
        dwCounter++, pStiInfoPtr++ ) {

        DisplayOutput("Device number %2d",dwCounter);
        pszStringTablePtr = StrFromTable(GET_STIDEVICE_TYPE(pStiInfoPtr->DeviceType),
                                         StStiDeviceType);
        DisplayOutput("  Device type %xh %s",
                      GET_STIDEVICE_TYPE(pStiInfoPtr->DeviceType),
                      pszStringTablePtr);
        DisplayOutput("  Device subtype %xh",
                      GET_STIDEVICE_SUBTYPE(pStiInfoPtr->DeviceType));
        DisplayOutput("  Internal name %S",
                      pStiInfoPtr->szDeviceInternalName);
        DisplayOutput("  Device capabilities %xh",
                      pStiInfoPtr->DeviceCapabilities);
        DisplayOutput("  Hardware configuration %xh",
                      pStiInfoPtr->dwHardwareConfiguration);
        DisplayOutput("  Vendor description %S",
                      pStiInfoPtr->pszVendorDescription);
        DisplayOutput("  Device description %S",
                      pStiInfoPtr->pszDeviceDescription);
        DisplayOutput("  Port Name %S",
                      pStiInfoPtr->pszPortName);
        DisplayOutput("  Prop provider %S",
                      pStiInfoPtr->pszPropProvider);
        DisplayOutput("  Local name %S",
                      pStiInfoPtr->pszLocalName);
        DisplayOutput("");
    }

    //
    // point to first device again
    //
    pStiInfoPtr = (PSTI_DEVICE_INFORMATION) pStiInfo + nStiNumber;

    DisplayOutput("->GetDeviceList found %d device%s",
                  dwStiTotal,
                  (dwStiTotal == 1) ? "" : "s" );

    if ( dwStiTotal != dwCounter ) {
        DisplayOutput("* ->Get DeviceList actually returned %d devices",dwCounter);
        dwStiTotal = dwCounter;
        nError++;
    }

    return (hres);
}


/*****************************************************************************
    INT StiSelect(HWND hWnd,int nContext)
        Select and open a specific Still Image device

    Parameters:
        handle to current window
                context we were called from

    Return:
        0 on success, -1 on error

*****************************************************************************/
INT StiSelect(HWND hWnd,int nContext)
{
    HRESULT hres = STI_OK;
    BOOL    bReturn;


    //
    // check that STI subsystem is loaded
    //
    if ( ! pSti )
        return (-1);

    if ( dwStiTotal == 0 ) {
        DisplayOutput("No devices to select!");
        return (-1);
    }

    switch ( nContext ) {
        case AUTO:
            //
            // automated device selection
            //

            // select next device (start at top of list if we reach the end)
            nStiNumber++;

            if ( nStiNumber >= (int) dwStiTotal )
                nStiNumber = 0;

            break;
        case EVENT:
            //
            // STI push event device selection
            //
            if ( nStiNumber == -1 ) {
                //
                // we could not select the correct device, just return
                //
                nStiNumber = 0;
                return (0);
            }
            break;
        case MANUAL:
            //
            // manual device selection
            //
            bReturn = fDialog(IDD_SELECT, hWnd, (FARPROC) SelectDevice);

            //
            // just return if user pressed CANCEL in dialog
            //
            if ( bReturn == FALSE )
                return (0);
            break;
    }

    //
    // close any currently active imaging device
    //
    if ( pStiDevice )
        StiDeviceRelease();

    //
    // get pointer to device selected in dialog
    //
    pStiInfoPtr = (PSTI_DEVICE_INFORMATION) pStiInfo + nStiNumber;

    if ( pStiInfoPtr->szDeviceInternalName == NULL ) {
        DisplayOutput("* invalid device name");
        nError++;
        return (-1);
    }

    if ( nAuto )
        DisplayOutput("Selecting device %d %S",
                      nStiNumber + 1,pStiInfoPtr->pszLocalName);

    //
    // The CreateDevice interface creates an IStiDevice object.
    // The IStiDevice object provides access to the IStiDevice interface
    // and device specific Imaging functionality.
    //
    hres = pSti->CreateDevice(
                             pStiInfoPtr->szDeviceInternalName,
                             // internal device name
                             STI_DEVICE_CREATE_BOTH, // device creation mode
                             &pStiDevice,            // pointer where IStiDevice object is to be stored
                             NULL );                 // pointer to controlling unknown of OLE aggregation

    if ( ! SUCCEEDED(hres) || ! pStiDevice ) {
        StiDisplayError(hres,"CreateDevice");
        nError++;
        return (-1);
    }


    //
    // Reset
    //
    hres = StiReset();

    //
    // GetStatus
    //
    hres = StiGetStatus(STI_DEVSTATUS_ONLINE_STATE);

    if ( ! SUCCEEDED(hres) ) {
        wsprintf(pszString,"%ls",pStiInfoPtr->pszLocalName);
        DisplayOutput("  \"%s\" is unavailable",pszString);
        return (-1);
    }

    //
    // Diagnostic
    //
    hres = StiDiagnostic();

    //
    // Enable Hw Event Notification
    //
    {
        int nState = TRUE;


        StiEnableHwNotification(pStiInfoPtr->szDeviceInternalName,&nState);
    }

    //
    // enable STI menu items
    //
    EnableMenuItem(hMenu, IDM_GET_DEVINFO,        MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_DEVVAL,         MF_ENABLED);
    EnableMenuItem(hMenu, IDM_SET_DEVVAL,         MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_LAUNCHINFO,     MF_ENABLED);
    EnableMenuItem(hMenu, IDM_ENABLE_HWNOTIF,     MF_ENABLED);
    CheckMenuItem(hMenu,  IDM_ENABLE_HWNOTIF,     MF_CHECKED);
    EnableMenuItem(hMenu, IDM_GET_HWNOTIF,        MF_ENABLED);
    EnableMenuItem(hMenu, IDM_REFRESH_DEVBUS,     MF_ENABLED);
    EnableMenuItem(hMenu, IDM_LAUNCH_APP_FOR_DEV, MF_ENABLED);
    EnableMenuItem(hMenu, IDM_SETUP_DEVPARAMS,    MF_ENABLED);

    EnableMenuItem(hMenu, IDM_GET_STATUS,         MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_CAPS,           MF_ENABLED);
    EnableMenuItem(hMenu, IDM_DEVICERESET,        MF_ENABLED);
    EnableMenuItem(hMenu, IDM_DIAGNOSTIC,         MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_LASTERRINFO,    MF_ENABLED);
    EnableMenuItem(hMenu, IDM_LOCKDEV,            MF_ENABLED);
    EnableMenuItem(hMenu, IDM_UNLOCKDEV,          MF_ENABLED);
    EnableMenuItem(hMenu, IDM_SUBSCRIBE,          MF_ENABLED);
    EnableMenuItem(hMenu, IDM_UNSUBSCRIBE,        MF_ENABLED);
    EnableMenuItem(hMenu, IDM_GET_LASTNOTIFDATA,  MF_ENABLED);
    EnableMenuItem(hMenu, IDM_DEVICE_RELEASE,     MF_ENABLED);

    EnableMenuItem(hMenu, IDM_IMAGE_RELEASE,      MF_GRAYED);

    //
    // check that this is a device we have command strings for
    //
    if ( nICanScan = IsScanDevice(pStiInfoPtr) ) {
        EnableMenuItem(hMenu, IDM_LAMPON,       MF_ENABLED);
        EnableMenuItem(hMenu, IDM_LAMPOFF,      MF_ENABLED);
        EnableMenuItem(hMenu, IDM_SCAN,         MF_ENABLED);
    } else {
        EnableMenuItem(hMenu, IDM_LAMPON,       MF_GRAYED);
        EnableMenuItem(hMenu, IDM_LAMPOFF,      MF_GRAYED);
        EnableMenuItem(hMenu, IDM_SCAN,         MF_GRAYED);

        LoadString(hThisInstance,IDS_APPNAME,pszString,MEDSTRING);
        DisplayOutput("Sorry, %s does not know how to Acquire with this device",
                      pszString);
    }
    wsprintf(pszString,"%ls",pStiInfoPtr->pszLocalName);
    DisplayOutput("  \"%s\" is ready for Action",pszString);

    return (0);
}


/******************************************************************************
    BOOL FAR PASCAL SelectDevice(HWND,UINT,WPARAM,LPARAM)
        Put up a dialog for user to select a Still Image device

    Parameters:
        The usual dialog box parameters.

    Return:
        Result of the call.

******************************************************************************/
BOOL FAR PASCAL SelectDevice(HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)
{
    PSTI_DEVICE_INFORMATION pTmpInfoPtr;
    DWORD                   dwCounter;
    INT_PTR                 iIndex;
    static int                  iLastPick = -1;


    switch ( msg ) {

        case WM_INITDIALOG:

            //
            // fill dialog with Sti Device Internal Names
            //
            for ( dwCounter = 0, pTmpInfoPtr = (PSTI_DEVICE_INFORMATION) pStiInfo;
                dwCounter < dwStiTotal;dwCounter++, pTmpInfoPtr++ ) {
                //
                // convert UNICODE string to ANSI
                //
                wsprintf(pszString,"%ls",pTmpInfoPtr->pszLocalName);

                iIndex = SendDlgItemMessage(hDlg,IDC_SELECT_DEVICE,
                                            CB_ADDSTRING,(WPARAM)0,(LPARAM) (LPCTSTR) pszString);
            }
            SendDlgItemMessage(hDlg,IDC_SELECT_DEVICE,CB_SETCURSEL,iLastPick,0);

            return (TRUE);

        case WM_COMMAND:
            switch ( wParam ) {
                case IDOK:
                    nStiNumber = (int)SendDlgItemMessage(hDlg,IDC_SELECT_DEVICE,
                                                         (UINT)CB_GETCURSEL,(WPARAM)0,(LPARAM)0);

                    //
                    // ensure device number not greater than total
                    // (NOTE: dwStiTotal is 1's base, while nStiNumber is 0 based)
                    //
                    if ( nStiNumber >= (int) dwStiTotal )
                        nStiNumber = (int) dwStiTotal - 1;
                    iLastPick = nStiNumber;

                    EndDialog(hDlg, TRUE);
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    return (TRUE);
            }

    }
    return (FALSE);
}


/*****************************************************************************
    void StiDisplayError(HRESULT,char *)
        Display verbose error information

    Parameters:
                HRESULT from failed call and call title string


    Return:
        none

*****************************************************************************/
void StiDisplayError(HRESULT hres,char *szCall)
{

    PCSTR   pszStringTablePtr = NULL;

    LastError(TRUE);
    DisplayOutput("* %s returned %xh (%d)",szCall,hres,hres);
    pszStringTablePtr = StrFromTable(hres,StStiError);
    DisplayOutput(" %s",pszStringTablePtr);

    return;
}


/*****************************************************************************
    HRESULT StiGetDeviceValue(LPWSTR,LPWSTR,DWORD *)


    Parameters:


    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiGetDeviceValue(LPWSTR szDevname,LPWSTR szKeyname,DWORD *dwType)
{
    HRESULT hres = STI_OK;
    DWORD   cbData = LONGSTRING;
    LPBYTE  pData;
    BOOL    bBadFlag = FALSE;

//
// WINNT.H - Predefined Value Types
//
    STRINGTABLE StRegType[] =
    {
        REG_NONE, "REG_NONE",0,
        REG_SZ, "REG_SZ",0,
        REG_BINARY, "REG_BINARY",0,
        REG_DWORD, "REG_DWORD",0,
        0, "Unknown Reg Type",-1
    };

    PCSTR    pszStringTablePtr = NULL;

    pData = (LPBYTE) malloc(LONGSTRING);
    if ( ! pData ) {
        DisplayOutput("* Error attempting to allocate %d bytes",LONGSTRING);
        return (STIERR_GENERIC);
    }
    memset(pData,0,sizeof(pData));

    //
    // The GetDeviceValue function is used to retrieve data associated with a
    // still image device. Essentially, data is associated with a device
    // through a key/data type/value triplet. The only reserved standard
    // ValueNames, as defined in STI.H, are the following:
    //
    // ICMProfiles - string containing a comma-sperated list of ICM profiles
    // TwainDS - TWAIN data source display name
    // ISISDriverName - ISIS driver name
    //
    hres = pSti->GetDeviceValue(
                               szDevname,              // internal device name
                               szKeyname,              // value tag string
                               dwType,                 // pointer where data type will be stored
                               pData,                  // pointer where value will be stored
                               &cbData                 // size of value pointer storage
                               );

    if ( !SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetDeviceValue");
        nError++;
    }

    DisplayOutput("GetDeviceValue for device %S",szDevname);
    DisplayOutput(" Key %S",szKeyname);
    pszStringTablePtr = StrFromTable(*dwType,StRegType);
    DisplayOutput(" Reg Type %d %s",* dwType,pszStringTablePtr);
    DisplayOutput(" %d bytes returned",cbData);
    DisplayOutput(" %s",pData);

    free(pData);

    return (hres);
}


/*****************************************************************************
    HRESULT StiRegister(HWND hWnd, int nOnOff)
                Register or Unregister the application to receive Sti Launch events.

    Parameters:
        Handle to the window to display image in.
                Instance for access to string table
                int nOnOff == ON to register, OFF to unregister

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiRegister(HWND hWnd,HINSTANCE hInstance,int nOnOff)
{
    HRESULT hres = STI_OK;
    CHAR    szModulePath[MAX_PATH+1];
    WCHAR   szModulePathW[MAX_PATH+1],
    szAppName[MEDSTRING];
    DWORD   cch;


    //
    // Retrieve name of this application from STRING table
    // and convert to UNICODE.
    //
    LoadString(hInstance,IDS_APPNAME,pszString,MEDSTRING);
    cch = MultiByteToWideChar(CP_ACP, 0,
                              pszString, -1,
                              szAppName,MEDSTRING);
    if ( ! cch ) {
        LastError(TRUE);
        return (STIERR_GENERIC);
    }

    //
    // Register/deregister app
    //
    if ( nOnOff == ON ) {
        //
        // Register our application.
        // Get full path to executable and convert to UNICODE.
        //
        cch = GetModuleFileName(NULL,szModulePath,sizeof(szModulePath));

        if ( ! cch ) {
            LastError(TRUE);
            return (STIERR_GENERIC);
        }
        cch = MultiByteToWideChar(CP_ACP, 0,
                                  szModulePath, -1,
                                  szModulePathW, sizeof(szModulePathW));

        //
        // The RegisterLaunchApplication function should be called by
        // applications that would like to be launched in response to an
        // STI push event. This function can be called more than once,
        // and should be called each time the application rus in case
        // the user relocates the application.
        //
        hres = pSti->RegisterLaunchApplication(
                                              szAppName,                      // short name of app
                                              szModulePathW           // full path to executable
                                              );

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"RegisterLaunchApplication");
            nError++;
        } else {
            DisplayOutput("%s registered for STI Launch Application",pszString);
        }
    } else {
        //
        // Unregister our application
        //
        hres = pSti->UnregisterLaunchApplication(
                                                szAppName                       // short name of app
                                                );
        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"UnregisterLaunchApplication");
            nError++;
        } else {
            DisplayOutput("%s Unregistered from STI Launch",pszString);
        }
    }

    return (hres);
}


/*****************************************************************************
    HRESULT StiEvent(HWND hWnd)
                Handle a push model event.
                This function is called when the test app has been
                        a) registered as a push event handler
                        b) launched by a push event

    Parameters:
        Handle to the window to display image in.

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiEvent(HWND hWnd)
{
    HRESULT hres = STI_OK;
    WCHAR   szDeviceName[STI_MAX_INTERNAL_NAME_LENGTH + 1],
    szEventName[LONGSTRING];
    DWORD   cch,
    dwEventCode = 0,
    cbData = LONGSTRING;
    int             nCounter;
    BOOL    bBadFlag = FALSE;


    memset(szDeviceName,0,STI_MAX_INTERNAL_NAME_LENGTH + 1);
    memset(szEventName,0,LONGSTRING);

    //
    // For an application started through push model launch,
    // GetSTILaunchInformation returns the associated information. This
    // information is used to determine which device to use and what
    // event caused the application to be launched.
    //
    hres = pSti->GetSTILaunchInformation(
                                        szDeviceName,           // pointer to where device name will be stored
                                        &dwEventCode,           // reserved
                                        szEventName                     // pointer to where GUID will be stored
                                        );

    if ( !SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetSTILaunchInformation");
        nError++;
        return (STIERR_GENERIC);
    }

    memset(pszMsg,0,LONGSTRING);
    memset(pszComment,0,LONGSTRING);
    cch = WideCharToMultiByte(CP_ACP,0,
                              szDeviceName,-1,
                              pszString,STI_MAX_INTERNAL_NAME_LENGTH + 1,
                              pszMsg,&bBadFlag);
    if ( ! cch )
        LastError(TRUE);
    if ( bBadFlag ) {
        DisplayOutput("* UNICODE translation error");
        bBadFlag = FALSE;
    }
    DisplayOutput("%s launched via STI push",pszString);
    DisplayOutput("Event code %d (%xh)",dwEventCode,dwEventCode);

    cch = WideCharToMultiByte(CP_ACP,0,
                              szEventName,-1,
                              pszString,STI_MAX_INTERNAL_NAME_LENGTH + 1,
                              pszMsg,&bBadFlag);
    if ( ! cch )
        LastError(TRUE);
    if ( bBadFlag ) {
        DisplayOutput("* UNICODE translation error");
        bBadFlag = FALSE;
    }
    DisplayOutput("Event name %s",pszString);

    //
    // find the STI device that sent the event
    // set nStiNumber to -1 (no device), then set to event device when found
    //
    for ( nStiNumber = -1,nCounter = 0,
          pStiInfoPtr = (PSTI_DEVICE_INFORMATION) pStiInfo;
        nCounter < (int) dwStiTotal;pStiInfoPtr++,nCounter++ ) {
        if ( ! wcscmp(szDeviceName,pStiInfoPtr->szDeviceInternalName) )
            nStiNumber = nCounter;
    }
    DisplayOutput("");

    return (hres);
}


/*****************************************************************************
    HRESULT StiGetDeviceInfo(LPWSTR szDevName)
                Display information about the selected device

    Parameters:
                WCHAR string of the selected device

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiGetDeviceInfo(LPWSTR szDevname)
{
    HRESULT     hres = STI_OK;
    PVOID       pInfo = NULL;

    PSTI_DEVICE_INFORMATION pInfoPtr = NULL;

    PCSTR    pszStringTablePtr = NULL;

    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // Given a device name, the GetDeviceInfo interface makes available a
    // structure that describes the various attributes of the device.
    // * NOTE: the STI subsystem allocates memory for the STI device information
    // buffer, but the caller needs to free this memory with LocalFree().
    //
    hres = pSti->GetDeviceInfo(
                              szDevname,              // pointer to the internal device name
                              &pInfo);                // STI device info buffer

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetDeviceInfo");
        nError++;
    }

    pInfoPtr = (PSTI_DEVICE_INFORMATION) pInfo;

    DisplayOutput("GetDeviceInfo for %S",szDevname);
    pszStringTablePtr = StrFromTable(GET_STIDEVICE_TYPE(pInfoPtr->DeviceType),
                                     StStiDeviceType);
    DisplayOutput("  Device type %xh %s",
                  GET_STIDEVICE_TYPE(pInfoPtr->DeviceType),
                  pszStringTablePtr);
    DisplayOutput("  Device subtype %xh",
                  GET_STIDEVICE_SUBTYPE(pInfoPtr->DeviceType));
    DisplayOutput("  Internal name %S",
                  pInfoPtr->szDeviceInternalName);
    DisplayOutput("  Device capabilities %xh",
                  pInfoPtr->DeviceCapabilities);
    DisplayOutput("  Hardware configuration %xh",
                  pInfoPtr->dwHardwareConfiguration);
    DisplayOutput("  Vendor description %S",
                  pInfoPtr->pszVendorDescription);
    DisplayOutput("  Device description %S",
                  pInfoPtr->pszDeviceDescription);
    DisplayOutput("  Port Name %S",
                  pInfoPtr->pszPortName);
    DisplayOutput("  Prop provider %S",
                  pInfoPtr->pszPropProvider);
    DisplayOutput("  Local name %S",
                  pInfoPtr->pszLocalName);
    DisplayOutput("");

    // free the STI_DEVICE_INFORMATION buffer
    if ( pInfo )
        LocalFree(pInfo);

    return (hres);
}


/*****************************************************************************
    HRESULT StiEnableHwNotification(LPWSTR szDevnameW,int *nState)
                Determine the current notification handling state and if requested,
                change it.

    Parameters:
                internal device name
        pointer to state request (current state returned in pointer)
                        ON = turn on polling
                        OFF = turn off polling
                        PEEK = return current polling state

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiEnableHwNotification(LPWSTR szDevnameW,int *nState)
{
    HRESULT hres = STI_OK;
    BOOL    bState = OFF;


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // GetHwNotificationState gets the current state of notification handling.
    // The state parameter returns TRUE if the notification is enabled.
    //
    hres = pSti->GetHwNotificationState(
                                       szDevnameW,             // internal device name
                                       &bState                 // pointer where state will be stored
                                       );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetHwNotificationState");
        nError++;
    } else
        DisplayOutput("Hardware Notification state is %s",
                      bState ? "TRUE (ON)" : "FALSE (OFF)");

    if ( *nState != PEEK ) {
        //
        // EnableHwNotifications is used to turn event notification on and off.
        // For polled devices, this function will turn polling on and off.
        //
        hres = pSti->EnableHwNotifications(
                                          szDevnameW,             // internal device name
                                          *nState                 // new state to be set
                                          );
        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"GetHwNotificationState");
            nError++;
            return (hres);
        }

        //
        // Ensure the state was changed
        //
        hres = pSti->GetHwNotificationState(
                                           szDevnameW,             // internal device name
                                           &bState                 // pointer where state will be stored
                                           );

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"GetHwNotificationState");
            nError++;
        } else
            DisplayOutput("Hw state has been set to %s",
                          bState ? "TRUE (ON)" : "FALSE (OFF)");

        if ( bState ) {
            if ( GetMenuState(hMenu, IDM_ENABLE_HWNOTIF, NULL) == MF_UNCHECKED )
                CheckMenuItem(hMenu, IDM_ENABLE_HWNOTIF, MF_CHECKED);
        } else {
            if ( GetMenuState(hMenu, IDM_ENABLE_HWNOTIF, NULL) == MF_CHECKED )
                CheckMenuItem(hMenu, IDM_ENABLE_HWNOTIF, MF_UNCHECKED);
        }
    }

    return (hres);
}


/*****************************************************************************
    HRESULT StiWriteErrLog(DWORD dwSeverity,LPCWSTR pszMessage)
                Write a string to the error log

    Parameters:
                DWORD severity, which can be
                        STI_TRACE_INFORMATION
                        STI_TRACE_WARNING
                        STI_TRACE_ERROR
                Wide character message to write to log.

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiWriteErrLog(DWORD dwSeverity,LPCWSTR pszMessage)
{
    HRESULT hres = STI_OK;


    //
    // check that STI subsystem is loaded
    //
    if ( ! pSti )
        return (-1);

    //
    // WriteToErrorLog can be used to write debugging and diagnostic
    // information into the STI log file, located in the Windows directory
    // STI_TRACE.LOG. The user can control whether informational, warning or
    // error messages, or any combination of these three are put in the log
    // file through the Scanners & Cameras control panel.
    //
    hres = pSti->WriteToErrorLog(
                                dwSeverity,                     // severity of error
                                pszMessage                      // string to write to log
                                );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"WriteToErrorLog");
        nError++;
    } else
        DisplayOutput("WriteToErrorLog wrote %S",pszMessage);

    return (hres);
}


/*****************************************************************************
    HRESULT StiGetStatus(int nMask)
                Retrieve the user mode status of the driver.

    Parameters:
                StatusMask to retrieve status for. Can be a combination of:
                        STI_DEV_ONLINE_STATE
                        STI_DEV_EVENTS_STATE

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiGetStatus(int nMask)
{
    HRESULT                             hres = STI_OK;
    DWORD                           dwTimeout = 2000;
    STI_DEVICE_STATUS       StiStatus;


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // Must lock device before GetStatus
    //
    // The LockDevice locks the apparatus for a single application to access.
    // Each LockDevice should be paired with a matching UnLockDevice call.
    //
    hres = pStiDevice->LockDevice(
                                 dwTimeout               // timeout in milliseconds
                                 );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"LockDevice");
        nError++;
    } else {
        DisplayOutput("Device is locked for GetStatus");

        //
        // Get and display status
        //
        ZeroMemory(&StiStatus,sizeof(StiStatus));
        //
        // The STI_DEVICE_STATUS dwSize field MUST be set by the caller.
        //
        StiStatus.dwSize = sizeof(STI_DEVICE_STATUS);
        //
        // The STI_DEVICE_STATUS StatusMask field MUST be set to the desired
        // status to retrieve.
        //
        StiStatus.StatusMask = nMask;

        //
        // The GetStatus interface gets the status from the user-mode
        // minidriver. Status returned can indicate online status and/or
        // device event activity.
        //
        hres = pStiDevice->GetStatus(
                                    &StiStatus                              // pointer to a STI_DEVICE_STATUS struct
                                    );

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"GetStatus");
        } else {
            DisplayOutput("->GetStatus on %S",pStiInfoPtr->pszLocalName);

            DisplayOutput(" %xh (%d) StatusMask",
                          StiStatus.StatusMask,StiStatus.StatusMask);
            DisplayOutput(" %xh (%d) dwOnlineState",
                          StiStatus.dwOnlineState,StiStatus.dwOnlineState);
            DisplayOutput(" %xh (%d) dwHardwareStatusCode",
                          StiStatus.dwHardwareStatusCode,StiStatus.dwHardwareStatusCode);
            DisplayOutput(" %xh (%d) dwEventHandlingState",
                          StiStatus.dwEventHandlingState,StiStatus.dwEventHandlingState);
            DisplayOutput(" %xh (%d) dwPollingInterval",
                          StiStatus.dwPollingInterval,StiStatus.dwPollingInterval);
        }

        if ( StiStatus.dwSize != sizeof(STI_DEVICE_STATUS) ) {
            DisplayOutput("* Expected STI_DEVICE_STATUS dwSize %d, got %d",
                          sizeof(STI_DEVICE_STATUS),StiStatus.dwSize);
        }
    }

    //
    // The UnLockDevice interface unlocks a device that was previously locked.
    //
    hres = pStiDevice->UnLockDevice();

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"UnLockDevice");
        nError++;
    } else
        DisplayOutput("Device is unlocked");

    return (hres);
}


/*****************************************************************************
    HRESULT StiGetCaps()
                Return the device capabilities

    Parameters:

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiGetCaps()
{
    HRESULT                             hres = STI_OK;
    STI_DEV_CAPS            StiDevCaps = { 0};


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // The GetCapabilities function returns the capabilities of the device.
    //
    hres = pStiDevice->GetCapabilities(
                                      &StiDevCaps                             // pointer to a STI_DEV_CAPS struct
                                      );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetCapabilities");
    } else {
        DisplayOutput("->GetCapabilities on %S",pStiInfoPtr->pszLocalName);

        DisplayOutput(" %xh (%d) dwGeneric",
                      StiDevCaps.dwGeneric,StiDevCaps.dwGeneric);
    }

    return (hres);
}


/*****************************************************************************
    HRESULT StiReset()
                Puts the device into a known state.

    Parameters:
                none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiReset()
{
    HRESULT hres = STI_OK;
    DWORD   dwTimeout = 2000;


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // Must lock device before DeviceReset
    //
    // The LockDevice locks the apparatus for a single application to access.
    // Each LockDevice should be paired with a matching UnLockDevice call.
    //
    hres = pStiDevice->LockDevice(
                                 dwTimeout               // timeout in milliseconds
                                 );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"LockDevice");
        nError++;
    } else {
        DisplayOutput("Device is locked for DeviceReset");

        //
        // The DeviceReset interface requests that a device be returned to a
        // known state.
        //
        hres = pStiDevice->DeviceReset();

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"DeviceReset");
            nError++;
        } else
            DisplayOutput("DeviceReset on %S",pStiInfoPtr->pszLocalName);
    }

    //
    // The UnLockDevice interface unlocks a device that was previously locked.
    //
    hres = pStiDevice->UnLockDevice();

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"UnLockDevice");
        nError++;
    } else
        DisplayOutput("Device is unlocked");

    return (hres);
}


/*****************************************************************************
    HRESULT StiDiagnostic()
                Return user mode driver diagnostic info

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiDiagnostic()
{
    HRESULT hres = STI_OK;
    DIAG    diag;
    DWORD   dwTimeout = 2000;


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // Must lock device before Diagnostic
    //
    // The LockDevice locks the apparatus for a single application to access.
    // Each LockDevice should be paired with a matching UnLockDevice call.
    //
    hres = pStiDevice->LockDevice(
                                 dwTimeout               // timeout in milliseconds
                                 );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"LockDevice");
        nError++;
    } else {
        DisplayOutput("Device is locked for Diagnostic");

        //
        // get diagnostic info
        //
        ZeroMemory(&diag,sizeof(diag));
        //
        // The DIAG dwSize field MUST be set by the caller.
        //
        diag.dwSize = sizeof(DIAG);
        //
        // The dwBasicDiagCode of this structure should be initialized with
        // the desired request code. Currently, only one request code is
        // defined, STI_DIAGCODE_HWPRESENCE.
        diag.dwBasicDiagCode = STI_DIAGCODE_HWPRESENCE;
        //
        // There is also a vendor defined field called dwVendorDiagCode that
        // can optionally be filled in.
        //
        diag.dwVendorDiagCode = 0;

        //
        // The Diagnostic interface executes the diagnostic method of the user
        // mode minidriver.
        //
        hres = pStiDevice->Diagnostic(
                                     &diag                           // pointer to STI_DIAG structure
                                     );

        if ( ! SUCCEEDED(hres) ) {
            StiDisplayError(hres,"Diagnostic");
            nError++;
        } else {
            DisplayOutput("Diagnostic on %S",pStiInfoPtr->pszLocalName);

            DisplayOutput(" %xh (%d) dwBasicDiagCode",
                          diag.dwBasicDiagCode,diag.dwBasicDiagCode);
            DisplayOutput(" %xh (%d) dwVendorDiagCode",
                          diag.dwVendorDiagCode,diag.dwVendorDiagCode);
            DisplayOutput(" %xh (%d) dwStatusMask",
                          diag.dwStatusMask,diag.dwStatusMask);

            if ( diag.dwSize != sizeof(DIAG) )
                DisplayOutput("* Expected DIAG dwSize %d, got %d",
                              sizeof(DIAG),diag.dwSize);

            //
            // any extended error info?
            //
            if ( diag.sErrorInfo.dwSize == 0 ) {
                DisplayOutput("No Extended Errors");
            } else {
                if ( diag.sErrorInfo.dwSize != sizeof(STI_ERROR_INFO) )
                    DisplayOutput("* Expected STI_ERROR_INFO dwSize %d, got %d",
                                  sizeof(STI_ERROR_INFO),diag.sErrorInfo.dwSize);
//                              DisplayOutput(" %xh (%d) sErrorInfo.dwSize",
//                                      diag.sErrorInfo.dwSize,diag.sErrorInfo.dwSize);
                DisplayOutput(" %xh (%d) sErrorInfo.dwGenericError",
                              diag.sErrorInfo.dwGenericError,diag.sErrorInfo.dwGenericError);
                DisplayOutput(" %xh (%d) sErrorInfo.dwVendorError",
                              diag.sErrorInfo.dwVendorError,diag.sErrorInfo.dwVendorError);
                if ( * diag.sErrorInfo.szExtendedErrorText )
                    DisplayOutput(" sErrorInfo.szExtendedErrorText %s",
                                  diag.sErrorInfo.szExtendedErrorText);
            }
        }
    }

    //
    // The UnLockDevice interface unlocks a device that was previously locked.
    //
    hres = pStiDevice->UnLockDevice();

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"UnLockDevice");
        nError++;
    } else
        DisplayOutput("Device is unlocked");

    return (hres);
}


/*****************************************************************************
    HRESULT StiGetLastErrorInfo()
                Get and display last error from Sti device.

    Parameters:
        none

    Return:
        HRESULT of last Sti call

*****************************************************************************/
HRESULT StiGetLastErrorInfo()
{
    HRESULT                     hres = STI_OK;
    STI_ERROR_INFO  StiError;
    DWORD                   dwTimeout = 2000;


    //
    // check that an Sti device is selected
    //
    if ( pStiDevice == NULL )
        return (STIERR_GENERIC);

    //
    // get last error info
    //
    ZeroMemory(&StiError,sizeof(StiError));
    //
    // The STI_ERROR_INFO dwSize field MUST be set by the caller.
    //
    StiError.dwSize = sizeof(STI_ERROR_INFO);

    //
    // The GetLastErrorInfo interface returns the last known error from
    // the user-mode minidriver.
    //
    hres = pStiDevice->GetLastErrorInfo(
                                       &StiError                               // pointer to STI_ERROR_INFO structure
                                       );

    if ( ! SUCCEEDED(hres) ) {
        StiDisplayError(hres,"GetLastErrorInfo");
        nError++;
    } else
        DisplayOutput("GetLastErrorInfo on %S",pStiInfoPtr->pszLocalName);

    //
    // any extended error info?
    //
    if ( StiError.dwSize == 0 ) {
        DisplayOutput("No Extended Errors");
    } else {
        if ( StiError.dwSize != sizeof(STI_ERROR_INFO) )
            DisplayOutput("* Expected STI_ERROR_INFO dwSize %d, got %d",
                          sizeof(STI_ERROR_INFO),StiError.dwSize);
        DisplayOutput(" %xh (%d) sErrorInfo.dwGenericError",
                      StiError.dwGenericError,StiError.dwGenericError);
        DisplayOutput(" %xh (%d) sErrorInfo.dwVendorError",
                      StiError.dwVendorError,StiError.dwVendorError);
        if ( * StiError.szExtendedErrorText )
            DisplayOutput(" sErrorInfo.szExtendedErrorText %s",
                          StiError.szExtendedErrorText);
    }

    return (hres);
}



